Skip to main content
summaryrefslogtreecommitdiffstats
blob: ffd41512cfeb8cfe6c03304b284f8fb0cb44324f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
package org.eclipse.jdt.internal.core;

/*
 * (c) Copyright IBM Corp. 2000, 2001.
 * All Rights Reserved.
 */
import org.eclipse.core.resources.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.jdom.*;
import org.eclipse.jdt.internal.core.jdom.*;
import java.util.Hashtable;

/**
 * This operation copies/moves a collection of elements from their current
 * container to a new container, optionally renaming the
 * elements.
 * <p>Notes:<ul>
 *    <li>If there is already an element with the same name in
 *    the new container, the operation either overwrites or aborts,
 *    depending on the collision policy setting. The default setting is
 *	  abort.
 *
 *    <li>When constructors are copied to a type, the constructors
 *    are automatically renamed to the name of the destination
 *    type.
 *
 *	  <li>When main types are renamed (move within the same parent),
 *		the compilation unit and constructors are automatically renamed
 *
 *    <li>The collection of elements being copied must all share the
 *    same type of container (for example, must all be type members).
 *
 *    <li>The elements are inserted in the new container in the order given.
 *
 *    <li>The elements can be positioned in the new container - see #setInsertBefore.
 *    By default, the elements are inserted based on the default positions as specified in
 * 	the creation operation for that element type.
 *
 *    <li>This operation can be used to copy and rename elements within
 *    the same container. 
 *
 *    <li>This operation only copies elements contained within compilation units. 
 * </ul>
 *
 */
public class CopyElementsOperation extends MultiOperation {

	private Hashtable fSources = new Hashtable();
	/**
	 * When executed, this operation will copy the given elements to the
	 * given containers.  The elements and destination containers must be in
	 * the correct order. If there is > 1 destination, the number of destinations
	 * must be the same as the number of elements being copied/moved/renamed.
	 */
	public CopyElementsOperation(
		IJavaElement[] elementsToCopy,
		IJavaElement[] destContainers,
		boolean force) {
		super(elementsToCopy, destContainers, force);
	}

	/**
	 * When executed, this operation will copy the given elements to the
	 * given container.
	 */
	public CopyElementsOperation(
		IJavaElement[] elementsToCopy,
		IJavaElement destContainer,
		boolean force) {
		this(elementsToCopy, new IJavaElement[] { destContainer }, force);
	}

	/**
	 * Returns the <code>String</code> to use as the main task name
	 * for progress monitoring.
	 */
	protected String getMainTaskName() {
		return "Copying elements...";
	}

	/**
	 * Returns the nested operation to use for processing this element
	 */
	protected JavaModelOperation getNestedOperation(IJavaElement element) {
		try {
			IJavaElement dest = getDestinationParent(element);
			switch (element.getElementType()) {
				case IJavaElement.PACKAGE_DECLARATION :
					return new CreatePackageDeclarationOperation(
						element.getElementName(),
						(ICompilationUnit) dest);
				case IJavaElement.IMPORT_DECLARATION :
					return new CreateImportOperation(
						element.getElementName(),
						(ICompilationUnit) dest);
				case IJavaElement.TYPE :
					if (isRenamingMainType(element, dest)) {
						return new RenameResourceElementsOperation(
							new IJavaElement[] { dest },
							new IJavaElement[] { dest.getParent()},
							new String[] { getNewNameFor(element) + ".java" },
							fForce);
					} else {
						return new CreateTypeOperation(
							dest,
							getSourceFor(element) + JavaModelManager.LINE_SEPARATOR,
							fForce);
					}
				case IJavaElement.METHOD :
					return new CreateMethodOperation(
						(IType) dest,
						getSourceFor(element) + JavaModelManager.LINE_SEPARATOR,
						fForce);
				case IJavaElement.FIELD :
					return new CreateFieldOperation(
						(IType) dest,
						getSourceFor(element) + JavaModelManager.LINE_SEPARATOR,
						fForce);
				case IJavaElement.INITIALIZER :
					return new CreateInitializerOperation(
						(IType) dest,
						getSourceFor(element) + JavaModelManager.LINE_SEPARATOR);
				default :
					return null;
			}
		} catch (JavaModelException npe) {
			return null;
		}
	}

	/**
	 * Returns the cached source for this element or compute it if not already cached.
	 */
	private String getSourceFor(IJavaElement element) throws JavaModelException {
		String source = (String) fSources.get(element);
		if (source == null && element instanceof IMember) {
			IMember member = (IMember) element;
			ICompilationUnit cu = member.getCompilationUnit();
			String cuSource = cu.getSource();
			IDOMCompilationUnit domCU =
				new DOMFactory().createCompilationUnit(cuSource, cu.getElementName());
			IDOMNode node = ((JavaElement) element).findNode(domCU);
			source = new String(node.getCharacters());
			fSources.put(element, source);
		}
		return source;
	}

	/**
	 * Returns <code>true</code> if this element is the main type of its compilation unit.
	 */
	protected boolean isRenamingMainType(IJavaElement element, IJavaElement dest) {
		if ((isRename() || getNewNameFor(element) != null)
			&& dest.getElementType() == IJavaElement.COMPILATION_UNIT) {
			String typeName = dest.getElementName();
			typeName = typeName.substring(0, typeName.length() - 5);
			return element.getElementName().equals(typeName)
				&& element.getParent().equals(dest);
		}
		return false;
	}

	/**
	 * Copy/move the element from the source to destination, renaming
	 * the elements as specified, honoring the collision policy.
	 *
	 * @exception JavaModelException if the operation is unable to
	 * be completed
	 */
	protected void processElement(IJavaElement element) throws JavaModelException {
		JavaModelOperation op = getNestedOperation(element);
		boolean createElementInCUOperation = op instanceof CreateElementInCUOperation;
		if (op == null) {
			return;
		}
		if (createElementInCUOperation) {
			IJavaElement sibling = (IJavaElement) fInsertBeforeElements.get(element);
			if (sibling != null) {
				((CreateElementInCUOperation) op).setRelativePosition(
					sibling,
					CreateElementInCUOperation.INSERT_BEFORE);
			} else
				if (isRename()) {
					IJavaElement anchor = resolveRenameAnchor(element);
					if (anchor != null) {
						((CreateElementInCUOperation) op).setRelativePosition(
							anchor,
							CreateElementInCUOperation.INSERT_AFTER);
						// insert after so that the anchor is found before when deleted below
					}
				}
			String newName = getNewNameFor(element);
			if (newName != null) {
				((CreateElementInCUOperation) op).setAlteredName(newName);
			}
		}
		executeNestedOperation(op, 1);

		JavaElement destination = (JavaElement) getDestinationParent(element);
		destination.getCompilationUnit().close();

		if (createElementInCUOperation
			&& isMove()
			&& !isRenamingMainType(element, destination)) {
			DeleteElementsOperation deleteOp =
				new DeleteElementsOperation(new IJavaElement[] { element }, fForce);
			executeNestedOperation(deleteOp, 1);
		}
	}

	/**
	 * Returns the anchor used for positioning in the destination for 
	 * the element being renamed. For renaming, if no anchor has
	 * explicitly been provided, the element is anchored in the same position.
	 */
	private IJavaElement resolveRenameAnchor(IJavaElement element)
		throws JavaModelException {
		IParent parent = (IParent) element.getParent();
		IJavaElement[] children = parent.getChildren();
		for (int i = 0; i < children.length; i++) {
			IJavaElement child = children[i];
			if (child.equals(element)) {
				return child;
			}
		}
		return null;
	}

	/**
	 * Possible failures:
	 * <ul>
	 *  <li>NO_ELEMENTS_TO_PROCESS - no elements supplied to the operation
	 *	<li>INDEX_OUT_OF_BOUNDS - the number of renamings supplied to the operation
	 *		does not match the number of elements that were supplied.
	 * </ul>
	 */
	protected IJavaModelStatus verify() {
		IJavaModelStatus status = super.verify();
		if (!status.isOK()) {
			return status;
		}
		if (fRenamingsList != null
			&& fRenamingsList.length != fElementsToProcess.length) {
			return new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS);
		}
		return JavaModelStatus.VERIFIED_OK;
	}

	/**
	 * @see MultiOperation
	 *
	 * Possible failure codes:
	 * <ul>
	 *
	 *	<li>ELEMENT_DOES_NOT_EXIST - <code>element</code> or its specified destination is
	 *		is <code>null</code> or does not exist. If a <code>null</code> element is
	 *		supplied, no element is provided in the status, otherwise, the non-existant element
	 *		is supplied in the status.
	 *	<li>INVALID_ELEMENT_TYPES - <code>element</code> is not contained within a compilation unit.
	 *		This operation only operates on elements contained within compilation units.
	 *  <li>READ_ONLY - <code>element</code> is read only.
	 *	<li>INVALID_DESTINATION - The destination parent specified for <code>element</code>
	 *		is of an incompatible type. The destination for a package declaration or import declaration must
	 *		be a compilation unit; the destination for a type must be a type or compilation
	 *		unit; the destinaion for any type member (other than a type) must be a type. When
	 *		this error occurs, the element provided in the operation status is the <code>element</code>.
	 *	<li>INVALID_NAME - the new name for <code>element</code> does not have valid syntax.
	 *      In this case the element and name are provided in the status.
	
	 * </ul>
	 */
	protected void verify(IJavaElement element) throws JavaModelException {
		if (element == null || !element.exists())
			error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, element);

		if (element.getElementType() < IJavaElement.TYPE)
			error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);

		if (element.isReadOnly())
			error(IJavaModelStatusConstants.READ_ONLY, element);

		IJavaElement dest = getDestinationParent(element);
		verifyDestination(element, dest);
		verifySibling(element, dest);
		if (fRenamingsList != null) {
			verifyRenaming(element);
		}
	}

}

Back to the top