001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core;
011:
012: import java.util.HashMap;
013: import java.util.Map;
014:
015: import org.eclipse.core.runtime.IPath;
016: import org.eclipse.jdt.core.ICompilationUnit;
017: import org.eclipse.jdt.core.IImportDeclaration;
018: import org.eclipse.jdt.core.IJavaElement;
019: import org.eclipse.jdt.core.IJavaModelStatus;
020: import org.eclipse.jdt.core.IJavaModelStatusConstants;
021: import org.eclipse.jdt.core.IMember;
022: import org.eclipse.jdt.core.IParent;
023: import org.eclipse.jdt.core.IType;
024: import org.eclipse.jdt.core.JavaModelException;
025: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
026: import org.eclipse.jdt.internal.core.util.Messages;
027:
028: /**
029: * This operation copies/moves a collection of elements from their current
030: * container to a new container, optionally renaming the
031: * elements.
032: * <p>Notes:<ul>
033: * <li>If there is already an element with the same name in
034: * the new container, the operation either overwrites or aborts,
035: * depending on the collision policy setting. The default setting is
036: * abort.
037: *
038: * <li>When constructors are copied to a type, the constructors
039: * are automatically renamed to the name of the destination
040: * type.
041: *
042: * <li>When main types are renamed (move within the same parent),
043: * the compilation unit and constructors are automatically renamed
044: *
045: * <li>The collection of elements being copied must all share the
046: * same type of container (for example, must all be type members).
047: *
048: * <li>The elements are inserted in the new container in the order given.
049: *
050: * <li>The elements can be positioned in the new container - see #setInsertBefore.
051: * By default, the elements are inserted based on the default positions as specified in
052: * the creation operation for that element type.
053: *
054: * <li>This operation can be used to copy and rename elements within
055: * the same container.
056: *
057: * <li>This operation only copies elements contained within compilation units.
058: * </ul>
059: *
060: */
061: public class CopyElementsOperation extends MultiOperation implements
062: SuffixConstants {
063:
064: private Map sources = new HashMap();
065:
066: /**
067: * When executed, this operation will copy the given elements to the
068: * given containers. The elements and destination containers must be in
069: * the correct order. If there is > 1 destination, the number of destinations
070: * must be the same as the number of elements being copied/moved/renamed.
071: */
072: public CopyElementsOperation(IJavaElement[] elementsToCopy,
073: IJavaElement[] destContainers, boolean force) {
074: super (elementsToCopy, destContainers, force);
075: }
076:
077: /**
078: * When executed, this operation will copy the given elements to the
079: * given container.
080: */
081: public CopyElementsOperation(IJavaElement[] elementsToCopy,
082: IJavaElement destContainer, boolean force) {
083: this (elementsToCopy, new IJavaElement[] { destContainer },
084: force);
085: }
086:
087: /**
088: * Returns the <code>String</code> to use as the main task name
089: * for progress monitoring.
090: */
091: protected String getMainTaskName() {
092: return Messages.operation_copyElementProgress;
093: }
094:
095: /**
096: * Returns the nested operation to use for processing this element
097: */
098: protected JavaModelOperation getNestedOperation(IJavaElement element) {
099: try {
100: IJavaElement dest = getDestinationParent(element);
101: switch (element.getElementType()) {
102: case IJavaElement.PACKAGE_DECLARATION:
103: return new CreatePackageDeclarationOperation(element
104: .getElementName(), (ICompilationUnit) dest);
105: case IJavaElement.IMPORT_DECLARATION:
106: IImportDeclaration importDeclaration = (IImportDeclaration) element;
107: return new CreateImportOperation(element
108: .getElementName(), (ICompilationUnit) dest,
109: importDeclaration.getFlags());
110: case IJavaElement.TYPE:
111: if (isRenamingMainType(element, dest)) {
112: IPath path = element.getPath();
113: String extension = path.getFileExtension();
114: return new RenameResourceElementsOperation(
115: new IJavaElement[] { dest },
116: new IJavaElement[] { dest.getParent() },
117: new String[] { getNewNameFor(element) + '.'
118: + extension }, this .force);
119: } else {
120: String source = getSourceFor(element);
121: String lineSeparator = org.eclipse.jdt.internal.core.util.Util
122: .getLineSeparator(source, element
123: .getJavaProject());
124: return new CreateTypeOperation(dest, source
125: + lineSeparator, this .force);
126: }
127: case IJavaElement.METHOD:
128: String source = getSourceFor(element);
129: String lineSeparator = org.eclipse.jdt.internal.core.util.Util
130: .getLineSeparator(source, element
131: .getJavaProject());
132: return new CreateMethodOperation((IType) dest, source
133: + lineSeparator, this .force);
134: case IJavaElement.FIELD:
135: source = getSourceFor(element);
136: lineSeparator = org.eclipse.jdt.internal.core.util.Util
137: .getLineSeparator(source, element
138: .getJavaProject());
139: return new CreateFieldOperation((IType) dest, source
140: + lineSeparator, this .force);
141: case IJavaElement.INITIALIZER:
142: source = getSourceFor(element);
143: lineSeparator = org.eclipse.jdt.internal.core.util.Util
144: .getLineSeparator(source, element
145: .getJavaProject());
146: return new CreateInitializerOperation((IType) dest,
147: source + lineSeparator);
148: default:
149: return null;
150: }
151: } catch (JavaModelException npe) {
152: return null;
153: }
154: }
155:
156: /**
157: * Returns the cached source for this element or compute it if not already cached.
158: */
159: private String getSourceFor(IJavaElement element)
160: throws JavaModelException {
161: String source = (String) this .sources.get(element);
162: if (source == null && element instanceof IMember) {
163: source = ((IMember) element).getSource();
164: this .sources.put(element, source);
165: }
166: return source;
167: }
168:
169: /**
170: * Returns <code>true</code> if this element is the main type of its compilation unit.
171: */
172: protected boolean isRenamingMainType(IJavaElement element,
173: IJavaElement dest) throws JavaModelException {
174: if ((isRename() || getNewNameFor(element) != null)
175: && dest.getElementType() == IJavaElement.COMPILATION_UNIT) {
176: String typeName = dest.getElementName();
177: typeName = org.eclipse.jdt.internal.core.util.Util
178: .getNameWithoutJavaLikeExtension(typeName);
179: return element.getElementName().equals(typeName)
180: && element.getParent().equals(dest);
181: }
182: return false;
183: }
184:
185: /**
186: * Copy/move the element from the source to destination, renaming
187: * the elements as specified, honoring the collision policy.
188: *
189: * @exception JavaModelException if the operation is unable to
190: * be completed
191: */
192: protected void processElement(IJavaElement element)
193: throws JavaModelException {
194: JavaModelOperation op = getNestedOperation(element);
195: boolean createElementInCUOperation = op instanceof CreateElementInCUOperation;
196: if (op == null) {
197: return;
198: }
199: if (createElementInCUOperation) {
200: IJavaElement sibling = (IJavaElement) this .insertBeforeElements
201: .get(element);
202: if (sibling != null) {
203: ((CreateElementInCUOperation) op).setRelativePosition(
204: sibling,
205: CreateElementInCUOperation.INSERT_BEFORE);
206: } else if (isRename()) {
207: IJavaElement anchor = resolveRenameAnchor(element);
208: if (anchor != null) {
209: ((CreateElementInCUOperation) op)
210: .setRelativePosition(
211: anchor,
212: CreateElementInCUOperation.INSERT_AFTER); // insert after so that the anchor is found before when deleted below
213: }
214: }
215: String newName = getNewNameFor(element);
216: if (newName != null) {
217: ((CreateElementInCUOperation) op)
218: .setAlteredName(newName);
219: }
220: }
221: executeNestedOperation(op, 1);
222:
223: JavaElement destination = (JavaElement) getDestinationParent(element);
224: ICompilationUnit unit = destination.getCompilationUnit();
225: if (!unit.isWorkingCopy()) {
226: unit.close();
227: }
228:
229: if (createElementInCUOperation && isMove()
230: && !isRenamingMainType(element, destination)) {
231: DeleteElementsOperation deleteOp = new DeleteElementsOperation(
232: new IJavaElement[] { element }, this .force);
233: executeNestedOperation(deleteOp, 1);
234: }
235: }
236:
237: /**
238: * Returns the anchor used for positioning in the destination for
239: * the element being renamed. For renaming, if no anchor has
240: * explicitly been provided, the element is anchored in the same position.
241: */
242: private IJavaElement resolveRenameAnchor(IJavaElement element)
243: throws JavaModelException {
244: IParent parent = (IParent) element.getParent();
245: IJavaElement[] children = parent.getChildren();
246: for (int i = 0; i < children.length; i++) {
247: IJavaElement child = children[i];
248: if (child.equals(element)) {
249: return child;
250: }
251: }
252: return null;
253: }
254:
255: /**
256: * Possible failures:
257: * <ul>
258: * <li>NO_ELEMENTS_TO_PROCESS - no elements supplied to the operation
259: * <li>INDEX_OUT_OF_BOUNDS - the number of renamings supplied to the operation
260: * does not match the number of elements that were supplied.
261: * </ul>
262: */
263: protected IJavaModelStatus verify() {
264: IJavaModelStatus status = super .verify();
265: if (!status.isOK()) {
266: return status;
267: }
268: if (this .renamingsList != null
269: && this .renamingsList.length != this .elementsToProcess.length) {
270: return new JavaModelStatus(
271: IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS);
272: }
273: return JavaModelStatus.VERIFIED_OK;
274: }
275:
276: /**
277: * @see MultiOperation
278: *
279: * Possible failure codes:
280: * <ul>
281: *
282: * <li>ELEMENT_DOES_NOT_EXIST - <code>element</code> or its specified destination is
283: * is <code>null</code> or does not exist. If a <code>null</code> element is
284: * supplied, no element is provided in the status, otherwise, the non-existant element
285: * is supplied in the status.
286: * <li>INVALID_ELEMENT_TYPES - <code>element</code> is not contained within a compilation unit.
287: * This operation only operates on elements contained within compilation units.
288: * <li>READ_ONLY - <code>element</code> is read only.
289: * <li>INVALID_DESTINATION - The destination parent specified for <code>element</code>
290: * is of an incompatible type. The destination for a package declaration or import declaration must
291: * be a compilation unit; the destination for a type must be a type or compilation
292: * unit; the destinaion for any type member (other than a type) must be a type. When
293: * this error occurs, the element provided in the operation status is the <code>element</code>.
294: * <li>INVALID_NAME - the new name for <code>element</code> does not have valid syntax.
295: * In this case the element and name are provided in the status.
296:
297: * </ul>
298: */
299: protected void verify(IJavaElement element)
300: throws JavaModelException {
301: if (element == null || !element.exists())
302: error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST,
303: element);
304:
305: if (element.getElementType() < IJavaElement.TYPE)
306: error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES,
307: element);
308:
309: if (element.isReadOnly())
310: error(IJavaModelStatusConstants.READ_ONLY, element);
311:
312: IJavaElement dest = getDestinationParent(element);
313: verifyDestination(element, dest);
314: verifySibling(element, dest);
315: if (this.renamingsList != null) {
316: verifyRenaming(element);
317: }
318: }
319: }
|