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