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;
25 * This class is used to perform operations on multiple <code>IJavaElement</code>.
26 * It is responible for running each operation in turn, collecting
27 * the errors and merging the corresponding <code>JavaElementDelta</code>s.
29 * If several errors occured, they are collected in a multi-status
30 * <code>JavaModelStatus</code>. Otherwise, a simple <code>JavaModelStatus</code>
33 public abstract class MultiOperation extends JavaModelOperation {
35 * The list of renamings supplied to the operation
37 protected String[] fRenamingsList= null;
39 * Table specifying the new parent for elements being
40 * copied/moved/renamed.
41 * Keyed by elements being processed, and
42 * values are the corresponding destination parent.
44 protected Map fParentElements;
46 * Table specifying insertion positions for elements being
47 * copied/moved/renamed. Keyed by elements being processed, and
48 * values are the corresponding insertion point.
49 * @see processElements(IProgressMonitor)
51 protected Map fInsertBeforeElements= new HashMap(1);
53 * This table presents the data in <code>fRenamingList</code> in a more
56 protected Map fRenamings;
58 * Creates a new <code>MultiOperation</code>.
60 protected MultiOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements, boolean force) {
61 super(elementsToProcess, parentElements, force);
62 fParentElements = new HashMap(elementsToProcess.length);
63 if (elementsToProcess.length == parentElements.length) {
64 for (int i = 0; i < elementsToProcess.length; i++) {
65 fParentElements.put(elementsToProcess[i], parentElements[i]);
67 } else { //same destination for all elements to be moved/copied/renamed
68 for (int i = 0; i < elementsToProcess.length; i++) {
69 fParentElements.put(elementsToProcess[i], parentElements[0]);
75 * Creates a new <code>MultiOperation</code> on <code>elementsToProcess</code>.
77 protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
78 super(elementsToProcess, force);
81 * Convenience method to create a <code>JavaModelException</code>
82 * embending a <code>JavaModelStatus</code>.
84 protected void error(int code, IJavaElement element) throws JavaModelException {
85 throw new JavaModelException(new JavaModelStatus(code, element));
88 * Executes the operation.
90 * @exception JavaModelException if one or several errors occured during the operation.
91 * If multiple errors occured, the corresponding <code>JavaModelStatus</code> is a
92 * multi-status. Otherwise, it is a simple one.
94 protected void executeOperation() throws JavaModelException {
98 * Returns the parent of the element being copied/moved/renamed.
100 protected IJavaElement getDestinationParent(IJavaElement child) {
101 return (IJavaElement)fParentElements.get(child);
104 * Returns the name to be used by the progress monitor.
106 protected abstract String getMainTaskName();
108 * Returns the new name for <code>element</code>, or <code>null</code>
109 * if there are no renamings specified.
111 protected String getNewNameFor(IJavaElement element) {
112 if (fRenamings != null)
113 return (String) fRenamings.get(element);
118 * Sets up the renamings hashtable - keys are the elements and
119 * values are the new name.
121 private void initializeRenamings() {
122 if (fRenamingsList != null && fRenamingsList.length == fElementsToProcess.length) {
123 fRenamings = new HashMap(fRenamingsList.length);
124 for (int i = 0; i < fRenamingsList.length; i++) {
125 if (fRenamingsList[i] != null) {
126 fRenamings.put(fElementsToProcess[i], fRenamingsList[i]);
132 * Returns <code>true</code> if this operation represents a move or rename, <code>false</code>
133 * if this operation represents a copy.<br>
134 * Note: a rename is just a move within the same parent with a name change.
136 protected boolean isMove() {
140 * Returns <code>true</code> if this operation represents a rename, <code>false</code>
141 * if this operation represents a copy or move.
143 protected boolean isRename() {
148 * Subclasses must implement this method to process a given <code>IJavaElement</code>.
150 protected abstract void processElement(IJavaElement element) throws JavaModelException;
152 * Processes all the <code>IJavaElement</code>s in turn, collecting errors
153 * and updating the progress monitor.
155 * @exception JavaModelException if one or several operation(s) was unable to
158 protected void processElements() throws JavaModelException {
159 beginTask(getMainTaskName(), fElementsToProcess.length);
160 IJavaModelStatus[] errors = new IJavaModelStatus[3];
161 int errorsCounter = 0;
162 for (int i = 0; i < fElementsToProcess.length; i++) {
164 verify(fElementsToProcess[i]);
165 processElement(fElementsToProcess[i]);
166 } catch (JavaModelException jme) {
167 if (errorsCounter == errors.length) {
169 System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter*2]), 0, errorsCounter);
171 errors[errorsCounter++] = jme.getJavaModelStatus();
177 if (errorsCounter == 1) {
178 throw new JavaModelException(errors[0]);
179 } else if (errorsCounter > 1) {
180 if (errorsCounter != errors.length) {
182 System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter]), 0, errorsCounter);
184 throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
188 * Sets the insertion position in the new container for the modified element. The element
189 * being modified will be inserted before the specified new sibling. The given sibling
190 * must be a child of the destination container specified for the modified element.
191 * The default is <code>null</code>, which indicates that the element is to be
192 * inserted at the end of the container.
194 public void setInsertBefore(IJavaElement modifiedElement, IJavaElement newSibling) {
195 fInsertBeforeElements.put(modifiedElement, newSibling);
198 * Sets the new names to use for each element being copied. The renamings
199 * correspond to the elements being processed, and the number of
200 * renamings must match the number of elements being processed.
201 * A <code>null</code> entry in the list indicates that an element
202 * is not to be renamed.
204 * <p>Note that some renamings may not be used. If both a parent
205 * and a child have been selected for copy/move, only the parent
206 * is changed. Therefore, if a new name is specified for the child,
207 * the child's name will not be changed.
209 public void setRenamings(String[] renamings) {
210 fRenamingsList = renamings;
211 initializeRenamings();
214 * This method is called for each <code>IJavaElement</code> before
215 * <code>processElement</code>. It should check that this <code>element</code>
218 protected abstract void verify(IJavaElement element) throws JavaModelException;
220 * Verifies that the <code>destination</code> specified for the <code>element</code> is valid for the types of the
221 * <code>element</code> and <code>destination</code>.
223 protected void verifyDestination(IJavaElement element, IJavaElement destination) throws JavaModelException {
224 if (destination == null || !destination.exists())
225 error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
227 int destType = destination.getElementType();
228 switch (element.getElementType()) {
229 case IJavaElement.PACKAGE_DECLARATION :
230 case IJavaElement.IMPORT_DECLARATION :
231 if (destType != IJavaElement.COMPILATION_UNIT)
232 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
234 case IJavaElement.TYPE :
235 if (destType != IJavaElement.COMPILATION_UNIT && destType != IJavaElement.TYPE)
236 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
238 case IJavaElement.METHOD :
239 case IJavaElement.FIELD :
240 // case IJavaElement.INITIALIZER :
241 // if (destType != IJavaElement.TYPE || destination instanceof BinaryType)
242 // error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
244 case IJavaElement.COMPILATION_UNIT :
245 if (destType != IJavaElement.PACKAGE_FRAGMENT)
246 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
247 else if (isMove() && ((ICompilationUnit) element).isWorkingCopy())
248 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
250 case IJavaElement.PACKAGE_FRAGMENT :
251 IPackageFragment fragment = (IPackageFragment) element;
252 IJavaElement parent = fragment.getParent();
253 if (parent.isReadOnly())
254 error(IJavaModelStatusConstants.READ_ONLY, element);
255 else if (destType != IJavaElement.PACKAGE_FRAGMENT_ROOT)
256 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
259 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
263 * Verify that the new name specified for <code>element</code> is
264 * valid for that type of Java element.
266 protected void verifyRenaming(IJavaElement element) throws JavaModelException {
267 String newName = getNewNameFor(element);
268 boolean isValid = true;
270 switch (element.getElementType()) {
271 case IJavaElement.PACKAGE_FRAGMENT :
272 if (element.getElementName().equals(IPackageFragment.DEFAULT_PACKAGE_NAME)) {
273 // don't allow renaming of default package (see PR #1G47GUM)
274 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, element));
276 // isValid = JavaConventions.validatePackageName(newName).getSeverity() != IStatus.ERROR;
279 case IJavaElement.COMPILATION_UNIT :
280 // isValid = JavaConventions.validateCompilationUnitName(newName).getSeverity() != IStatus.ERROR;
283 case IJavaElement.INITIALIZER :
284 isValid = false; //cannot rename initializers
287 // isValid = JavaConventions.validateIdentifier(newName).getSeverity() != IStatus.ERROR;
293 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_NAME, element, newName));
297 * Verifies that the positioning sibling specified for the <code>element</code> is exists and
298 * its parent is the destination container of this <code>element</code>.
300 protected void verifySibling(IJavaElement element, IJavaElement destination) throws JavaModelException {
301 IJavaElement insertBeforeElement = (IJavaElement) fInsertBeforeElements.get(element);
302 if (insertBeforeElement != null) {
303 if (!insertBeforeElement.exists() || !insertBeforeElement.getParent().equals(destination)) {
304 error(IJavaModelStatusConstants.INVALID_SIBLING, insertBeforeElement);