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