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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
13 import java.util.HashMap;
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.IPackageFragment;
21 import net.sourceforge.phpdt.core.JavaModelException;
24 * This class is used to perform operations on multiple
25 * <code>IJavaElement</code>. It is responible for running each operation in
26 * turn, collecting the errors and merging the corresponding
27 * <code>JavaElementDelta</code>s.
29 * If several errors occured, they are collected in a multi-status
30 * <code>JavaModelStatus</code>. Otherwise, a simple
31 * <code>JavaModelStatus</code> is thrown.
33 public abstract class MultiOperation extends JavaModelOperation {
35 * The list of renamings supplied to the operation
37 protected String[] fRenamingsList = null;
40 * Table specifying the new parent for elements being copied/moved/renamed.
41 * Keyed by elements being processed, and values are the corresponding
44 protected Map fParentElements;
47 * Table specifying insertion positions for elements being
48 * copied/moved/renamed. Keyed by elements being processed, and values are
49 * the corresponding insertion point.
51 * @see processElements(IProgressMonitor)
53 protected Map fInsertBeforeElements = new HashMap(1);
56 * This table presents the data in <code>fRenamingList</code> in a more
59 protected Map fRenamings;
62 * Creates a new <code>MultiOperation</code>.
64 protected MultiOperation(IJavaElement[] elementsToProcess,
65 IJavaElement[] parentElements, boolean force) {
66 super(elementsToProcess, parentElements, force);
67 fParentElements = new HashMap(elementsToProcess.length);
68 if (elementsToProcess.length == parentElements.length) {
69 for (int i = 0; i < elementsToProcess.length; i++) {
70 fParentElements.put(elementsToProcess[i], parentElements[i]);
72 } else { // same destination for all elements to be
73 // moved/copied/renamed
74 for (int i = 0; i < elementsToProcess.length; i++) {
75 fParentElements.put(elementsToProcess[i], parentElements[0]);
82 * Creates a new <code>MultiOperation</code> on
83 * <code>elementsToProcess</code>.
85 protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
86 super(elementsToProcess, force);
90 * Convenience method to create a <code>JavaModelException</code>
91 * embending a <code>JavaModelStatus</code>.
93 protected void error(int code, IJavaElement element)
94 throws JavaModelException {
95 throw new JavaModelException(new JavaModelStatus(code, element));
99 * Executes the operation.
101 * @exception JavaModelException
102 * if one or several errors occured during the operation. If
103 * multiple errors occured, the corresponding
104 * <code>JavaModelStatus</code> is a multi-status.
105 * Otherwise, it is a simple one.
107 protected void executeOperation() throws JavaModelException {
112 * Returns the parent of the element being copied/moved/renamed.
114 protected IJavaElement getDestinationParent(IJavaElement child) {
115 return (IJavaElement) fParentElements.get(child);
119 * Returns the name to be used by the progress monitor.
121 protected abstract String getMainTaskName();
124 * Returns the new name for <code>element</code>, or <code>null</code>
125 * if there are no renamings specified.
127 protected String getNewNameFor(IJavaElement element) {
128 if (fRenamings != null)
129 return (String) fRenamings.get(element);
135 * Sets up the renamings hashtable - keys are the elements and values are
138 private void initializeRenamings() {
139 if (fRenamingsList != null
140 && fRenamingsList.length == fElementsToProcess.length) {
141 fRenamings = new HashMap(fRenamingsList.length);
142 for (int i = 0; i < fRenamingsList.length; i++) {
143 if (fRenamingsList[i] != null) {
144 fRenamings.put(fElementsToProcess[i], fRenamingsList[i]);
151 * Returns <code>true</code> if this operation represents a move or
152 * rename, <code>false</code> if this operation represents a copy.<br>
153 * Note: a rename is just a move within the same parent with a name change.
155 protected boolean isMove() {
160 * Returns <code>true</code> if this operation represents a rename,
161 * <code>false</code> if this operation represents a copy or move.
163 protected boolean isRename() {
168 * Subclasses must implement this method to process a given
169 * <code>IJavaElement</code>.
171 protected abstract void processElement(IJavaElement element)
172 throws JavaModelException;
175 * Processes all the <code>IJavaElement</code>s in turn, collecting
176 * errors and updating the progress monitor.
178 * @exception JavaModelException
179 * if one or several operation(s) was unable to be completed.
181 protected void processElements() throws JavaModelException {
182 beginTask(getMainTaskName(), fElementsToProcess.length);
183 IJavaModelStatus[] errors = new IJavaModelStatus[3];
184 int errorsCounter = 0;
185 for (int i = 0; i < fElementsToProcess.length; i++) {
187 verify(fElementsToProcess[i]);
188 processElement(fElementsToProcess[i]);
189 } catch (JavaModelException jme) {
190 if (errorsCounter == errors.length) {
192 System.arraycopy(errors, 0,
193 (errors = new IJavaModelStatus[errorsCounter * 2]),
196 errors[errorsCounter++] = jme.getJavaModelStatus();
202 if (errorsCounter == 1) {
203 throw new JavaModelException(errors[0]);
204 } else if (errorsCounter > 1) {
205 if (errorsCounter != errors.length) {
207 System.arraycopy(errors, 0,
208 (errors = new IJavaModelStatus[errorsCounter]), 0,
211 throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
216 * Sets the insertion position in the new container for the modified
217 * element. The element being modified will be inserted before the specified
218 * new sibling. The given sibling must be a child of the destination
219 * container specified for the modified element. The default is
220 * <code>null</code>, which indicates that the element is to be inserted
221 * at the end of the container.
223 public void setInsertBefore(IJavaElement modifiedElement,
224 IJavaElement newSibling) {
225 fInsertBeforeElements.put(modifiedElement, newSibling);
229 * Sets the new names to use for each element being copied. The renamings
230 * correspond to the elements being processed, and the number of renamings
231 * must match the number of elements being processed. A <code>null</code>
232 * entry in the list indicates that an element is not to be renamed.
235 * Note that some renamings may not be used. If both a parent and a child
236 * have been selected for copy/move, only the parent is changed. Therefore,
237 * if a new name is specified for the child, the child's name will not be
240 public void setRenamings(String[] renamings) {
241 fRenamingsList = renamings;
242 initializeRenamings();
246 * This method is called for each <code>IJavaElement</code> before
247 * <code>processElement</code>. It should check that this
248 * <code>element</code> can be processed.
250 protected abstract void verify(IJavaElement element)
251 throws JavaModelException;
254 * Verifies that the <code>destination</code> specified for the
255 * <code>element</code> is valid for the types of the <code>element</code>
256 * and <code>destination</code>.
258 protected void verifyDestination(IJavaElement element,
259 IJavaElement destination) throws JavaModelException {
260 if (destination == null || !destination.exists())
261 error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
263 int destType = destination.getElementType();
264 switch (element.getElementType()) {
265 case IJavaElement.PACKAGE_DECLARATION:
266 case IJavaElement.IMPORT_DECLARATION:
267 if (destType != IJavaElement.COMPILATION_UNIT)
268 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
270 case IJavaElement.TYPE:
271 if (destType != IJavaElement.COMPILATION_UNIT
272 && destType != IJavaElement.TYPE)
273 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
275 case IJavaElement.METHOD:
276 case IJavaElement.FIELD:
277 // case IJavaElement.INITIALIZER :
278 // if (destType != IJavaElement.TYPE || destination instanceof
280 // error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
282 case IJavaElement.COMPILATION_UNIT:
283 if (destType != IJavaElement.PACKAGE_FRAGMENT)
284 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
285 else if (isMove() && ((ICompilationUnit) element).isWorkingCopy())
286 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
288 case IJavaElement.PACKAGE_FRAGMENT:
289 IPackageFragment fragment = (IPackageFragment) element;
290 IJavaElement parent = fragment.getParent();
291 if (parent.isReadOnly())
292 error(IJavaModelStatusConstants.READ_ONLY, element);
293 else if (destType != IJavaElement.PACKAGE_FRAGMENT_ROOT)
294 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
297 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
302 * Verify that the new name specified for <code>element</code> is valid
303 * for that type of Java element.
305 protected void verifyRenaming(IJavaElement element)
306 throws JavaModelException {
307 String newName = getNewNameFor(element);
308 boolean isValid = true;
310 switch (element.getElementType()) {
311 case IJavaElement.PACKAGE_FRAGMENT:
312 if (element.getElementName().equals(
313 IPackageFragment.DEFAULT_PACKAGE_NAME)) {
314 // don't allow renaming of default package (see PR #1G47GUM)
315 throw new JavaModelException(new JavaModelStatus(
316 IJavaModelStatusConstants.NAME_COLLISION, element));
319 // JavaConventions.validatePackageName(newName).getSeverity() !=
323 case IJavaElement.COMPILATION_UNIT:
325 // JavaConventions.validateCompilationUnitName(newName).getSeverity()
329 case IJavaElement.INITIALIZER:
330 isValid = false; // cannot rename initializers
334 // JavaConventions.validateIdentifier(newName).getSeverity() !=
341 throw new JavaModelException(new JavaModelStatus(
342 IJavaModelStatusConstants.INVALID_NAME, element, newName));
347 * Verifies that the positioning sibling specified for the
348 * <code>element</code> is exists and its parent is the destination
349 * container of this <code>element</code>.
351 protected void verifySibling(IJavaElement element, IJavaElement destination)
352 throws JavaModelException {
353 IJavaElement insertBeforeElement = (IJavaElement) fInsertBeforeElements
355 if (insertBeforeElement != null) {
356 if (!insertBeforeElement.exists()
357 || !insertBeforeElement.getParent().equals(destination)) {
358 error(IJavaModelStatusConstants.INVALID_SIBLING,
359 insertBeforeElement);