03578f88c0a146d097d43a5ca55d0d9882d033cb
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / CopyElementsOperation.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import java.util.HashMap;
14 import java.util.Map;
15
16 import net.sourceforge.phpdt.core.ICompilationUnit;
17 import net.sourceforge.phpdt.core.IJavaElement;
18 import net.sourceforge.phpdt.core.IJavaModelStatus;
19 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
20 import net.sourceforge.phpdt.core.IMember;
21 import net.sourceforge.phpdt.core.IParent;
22 import net.sourceforge.phpdt.core.JavaModelException;
23 import net.sourceforge.phpdt.core.jdom.DOMFactory;
24 import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit;
25 import net.sourceforge.phpdt.core.jdom.IDOMNode;
26 import net.sourceforge.phpdt.internal.core.util.Util;
27
28 /**
29  * This operation copies/moves a collection of elements from their current
30  * container to a new container, optionally renaming the elements.
31  * <p>
32  * Notes:
33  * <ul>
34  * <li>If there is already an element with the same name in the new container,
35  * the operation either overwrites or aborts, depending on the collision policy
36  * setting. The default setting is abort.
37  * 
38  * <li>When constructors are copied to a type, the constructors are
39  * automatically renamed to the name of the destination type.
40  * 
41  * <li>When main types are renamed (move within the same parent), the
42  * compilation unit and constructors are automatically renamed
43  * 
44  * <li>The collection of elements being copied must all share the same type of
45  * container (for example, must all be type members).
46  * 
47  * <li>The elements are inserted in the new container in the order given.
48  * 
49  * <li>The elements can be positioned in the new container - see
50  * #setInsertBefore. By default, the elements are inserted based on the default
51  * positions as specified in the creation operation for that element type.
52  * 
53  * <li>This operation can be used to copy and rename elements within the same
54  * container.
55  * 
56  * <li>This operation only copies elements contained within compilation units.
57  * </ul>
58  * 
59  */
60 public class CopyElementsOperation extends MultiOperation {
61
62         private Map fSources = new HashMap();
63
64         /**
65          * When executed, this operation will copy the given elements to the given
66          * containers. The elements and destination containers must be in the
67          * correct order. If there is > 1 destination, the number of destinations
68          * must be the same as the number of elements being copied/moved/renamed.
69          */
70         public CopyElementsOperation(IJavaElement[] elementsToCopy,
71                         IJavaElement[] destContainers, boolean force) {
72                 super(elementsToCopy, destContainers, force);
73         }
74
75         /**
76          * When executed, this operation will copy the given elements to the given
77          * container.
78          */
79         public CopyElementsOperation(IJavaElement[] elementsToCopy,
80                         IJavaElement destContainer, boolean force) {
81                 this(elementsToCopy, new IJavaElement[] { destContainer }, force);
82         }
83
84         /**
85          * Returns the <code>String</code> to use as the main task name for
86          * progress monitoring.
87          */
88         protected String getMainTaskName() {
89                 return Util.bind("operation.copyElementProgress"); //$NON-NLS-1$
90         }
91
92         /**
93          * Returns the nested operation to use for processing this element
94          */
95         protected JavaModelOperation getNestedOperation(IJavaElement element) {
96                 return null;
97                 // try {
98                 // IJavaElement dest = getDestinationParent(element);
99                 // switch (element.getElementType()) {
100                 // case IJavaElement.PACKAGE_DECLARATION :
101                 // return new
102                 // CreatePackageDeclarationOperation(element.getElementName(),
103                 // (ICompilationUnit) dest);
104                 // case IJavaElement.IMPORT_DECLARATION :
105                 // return new CreateImportOperation(element.getElementName(),
106                 // (ICompilationUnit) dest);
107                 // case IJavaElement.TYPE :
108                 // if (isRenamingMainType(element, dest)) {
109                 // return new RenameResourceElementsOperation(new IJavaElement[] {dest},
110                 // new IJavaElement[] {dest.getParent()}, new
111                 // String[]{getNewNameFor(element) + ".php"}, fForce); //$NON-NLS-1$
112                 // } else {
113                 // return new CreateTypeOperation(dest, getSourceFor(element) +
114                 // ProjectPrefUtil.LINE_SEPARATOR, fForce);
115                 // }
116                 // case IJavaElement.METHOD :
117                 // return new CreateMethodOperation((IType) dest, getSourceFor(element)
118                 // + ProjectPrefUtil.LINE_SEPARATOR, fForce);
119                 // case IJavaElement.FIELD :
120                 // return new CreateFieldOperation((IType) dest, getSourceFor(element) +
121                 // ProjectPrefUtil.LINE_SEPARATOR, fForce);
122                 // case IJavaElement.INITIALIZER :
123                 // return new CreateInitializerOperation((IType) dest,
124                 // getSourceFor(element) + ProjectPrefUtil.LINE_SEPARATOR);
125                 // default :
126                 // return null;
127                 // }
128                 // } catch (JavaModelException npe) {
129                 // return null;
130                 // }
131         }
132
133         /**
134          * Returns the cached source for this element or compute it if not already
135          * cached.
136          */
137         private String getSourceFor(IJavaElement element) throws JavaModelException {
138                 String source = (String) fSources.get(element);
139                 if (source == null && element instanceof IMember) {
140                         IMember member = (IMember) element;
141                         ICompilationUnit cu = member.getCompilationUnit();
142                         String cuSource = cu.getSource();
143                         IDOMCompilationUnit domCU = new DOMFactory().createCompilationUnit(
144                                         cuSource, cu.getElementName());
145                         IDOMNode node = ((JavaElement) element).findNode(domCU);
146                         source = new String(node.getCharacters());
147                         fSources.put(element, source);
148                 }
149                 return source;
150         }
151
152         /**
153          * Returns <code>true</code> if this element is the main type of its
154          * compilation unit.
155          */
156         protected boolean isRenamingMainType(IJavaElement element, IJavaElement dest) {
157                 if ((isRename() || getNewNameFor(element) != null)
158                                 && dest.getElementType() == IJavaElement.COMPILATION_UNIT) {
159                         String typeName = dest.getElementName();
160                         typeName = typeName.substring(0, typeName.length() - 5);
161                         return element.getElementName().equals(typeName)
162                                         && element.getParent().equals(dest);
163                 }
164                 return false;
165         }
166
167         /**
168          * Copy/move the element from the source to destination, renaming the
169          * elements as specified, honoring the collision policy.
170          * 
171          * @exception JavaModelException
172          *                if the operation is unable to be completed
173          */
174         protected void processElement(IJavaElement element)
175                         throws JavaModelException {
176                 JavaModelOperation op = getNestedOperation(element);
177                 boolean createElementInCUOperation = op instanceof CreateElementInCUOperation;
178                 if (op == null) {
179                         return;
180                 }
181                 if (createElementInCUOperation) {
182                         IJavaElement sibling = (IJavaElement) fInsertBeforeElements
183                                         .get(element);
184                         if (sibling != null) {
185                                 ((CreateElementInCUOperation) op).setRelativePosition(sibling,
186                                                 CreateElementInCUOperation.INSERT_BEFORE);
187                         } else if (isRename()) {
188                                 IJavaElement anchor = resolveRenameAnchor(element);
189                                 if (anchor != null) {
190                                         ((CreateElementInCUOperation) op).setRelativePosition(
191                                                         anchor, CreateElementInCUOperation.INSERT_AFTER); // insert
192                                                                                                                                                                 // after
193                                                                                                                                                                 // so
194                                                                                                                                                                 // that
195                                                                                                                                                                 // the
196                                                                                                                                                                 // anchor
197                                                                                                                                                                 // is
198                                                                                                                                                                 // found
199                                                                                                                                                                 // before
200                                                                                                                                                                 // when
201                                                                                                                                                                 // deleted
202                                                                                                                                                                 // below
203                                 }
204                         }
205                         String newName = getNewNameFor(element);
206                         if (newName != null) {
207                                 ((CreateElementInCUOperation) op).setAlteredName(newName);
208                         }
209                 }
210                 executeNestedOperation(op, 1);
211
212                 JavaElement destination = (JavaElement) getDestinationParent(element);
213                 ICompilationUnit unit = destination.getCompilationUnit();
214                 if (!unit.isWorkingCopy()) {
215                         unit.close();
216                 }
217
218                 if (createElementInCUOperation && isMove()
219                                 && !isRenamingMainType(element, destination)) {
220                         DeleteElementsOperation deleteOp = new DeleteElementsOperation(
221                                         new IJavaElement[] { element }, force);
222                         executeNestedOperation(deleteOp, 1);
223                 }
224         }
225
226         /**
227          * Returns the anchor used for positioning in the destination for the
228          * element being renamed. For renaming, if no anchor has explicitly been
229          * provided, the element is anchored in the same position.
230          */
231         private IJavaElement resolveRenameAnchor(IJavaElement element)
232                         throws JavaModelException {
233                 IParent parent = (IParent) element.getParent();
234                 IJavaElement[] children = parent.getChildren();
235                 for (int i = 0; i < children.length; i++) {
236                         IJavaElement child = children[i];
237                         if (child.equals(element)) {
238                                 return child;
239                         }
240                 }
241                 return null;
242         }
243
244         /**
245          * Possible failures:
246          * <ul>
247          * <li>NO_ELEMENTS_TO_PROCESS - no elements supplied to the operation
248          * <li>INDEX_OUT_OF_BOUNDS - the number of renamings supplied to the
249          * operation does not match the number of elements that were supplied.
250          * </ul>
251          */
252         protected IJavaModelStatus verify() {
253                 IJavaModelStatus status = super.verify();
254                 if (!status.isOK()) {
255                         return status;
256                 }
257                 if (fRenamingsList != null
258                                 && fRenamingsList.length != fElementsToProcess.length) {
259                         return new JavaModelStatus(
260                                         IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS);
261                 }
262                 return JavaModelStatus.VERIFIED_OK;
263         }
264
265         /**
266          * @see MultiOperation
267          * 
268          * Possible failure codes:
269          * <ul>
270          * 
271          * <li>ELEMENT_DOES_NOT_EXIST - <code>element</code> or its specified
272          * destination is is <code>null</code> or does not exist. If a
273          * <code>null</code> element is supplied, no element is provided in the
274          * status, otherwise, the non-existant element is supplied in the status.
275          * <li>INVALID_ELEMENT_TYPES - <code>element</code> is not contained
276          * within a compilation unit. This operation only operates on elements
277          * contained within compilation units.
278          * <li>READ_ONLY - <code>element</code> is read only.
279          * <li>INVALID_DESTINATION - The destination parent specified for
280          * <code>element</code> is of an incompatible type. The destination for a
281          * package declaration or import declaration must be a compilation unit; the
282          * destination for a type must be a type or compilation unit; the destinaion
283          * for any type member (other than a type) must be a type. When this error
284          * occurs, the element provided in the operation status is the
285          * <code>element</code>.
286          * <li>INVALID_NAME - the new name for <code>element</code> does not have
287          * valid syntax. In this case the element and name are provided in the
288          * status.
289          * 
290          * </ul>
291          */
292         protected void verify(IJavaElement element) throws JavaModelException {
293                 if (element == null || !element.exists())
294                         error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, element);
295
296                 if (element.getElementType() < IJavaElement.TYPE)
297                         error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
298
299                 if (element.isReadOnly())
300                         error(IJavaModelStatusConstants.READ_ONLY, element);
301
302                 IJavaElement dest = getDestinationParent(element);
303                 verifyDestination(element, dest);
304                 verifySibling(element, dest);
305                 if (fRenamingsList != null) {
306                         verifyRenaming(element);
307                 }
308         }
309 }