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;
23 import org.eclipse.core.runtime.IStatus;
27 * This class is used to perform operations on multiple <code>IJavaElement</code>.
28 * It is responible for running each operation in turn, collecting
29 * the errors and merging the corresponding <code>JavaElementDelta</code>s.
31 * If several errors occured, they are collected in a multi-status
32 * <code>JavaModelStatus</code>. Otherwise, a simple <code>JavaModelStatus</code>
35 public abstract class MultiOperation extends JavaModelOperation {
37 * The list of renamings supplied to the operation
39 protected String[] fRenamingsList= null;
41 * Table specifying the new parent for elements being
42 * copied/moved/renamed.
43 * Keyed by elements being processed, and
44 * values are the corresponding destination parent.
46 protected Map fParentElements;
48 * Table specifying insertion positions for elements being
49 * copied/moved/renamed. Keyed by elements being processed, and
50 * values are the corresponding insertion point.
51 * @see processElements(IProgressMonitor)
53 protected Map fInsertBeforeElements= new HashMap(1);
55 * This table presents the data in <code>fRenamingList</code> in a more
58 protected Map fRenamings;
60 * Creates a new <code>MultiOperation</code>.
62 protected MultiOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements, boolean force) {
63 super(elementsToProcess, parentElements, force);
64 fParentElements = new HashMap(elementsToProcess.length);
65 if (elementsToProcess.length == parentElements.length) {
66 for (int i = 0; i < elementsToProcess.length; i++) {
67 fParentElements.put(elementsToProcess[i], parentElements[i]);
69 } else { //same destination for all elements to be moved/copied/renamed
70 for (int i = 0; i < elementsToProcess.length; i++) {
71 fParentElements.put(elementsToProcess[i], parentElements[0]);
77 * Creates a new <code>MultiOperation</code> on <code>elementsToProcess</code>.
79 protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
80 super(elementsToProcess, force);
83 * Convenience method to create a <code>JavaModelException</code>
84 * embending a <code>JavaModelStatus</code>.
86 protected void error(int code, IJavaElement element) throws JavaModelException {
87 throw new JavaModelException(new JavaModelStatus(code, element));
90 * Executes the operation.
92 * @exception JavaModelException if one or several errors occured during the operation.
93 * If multiple errors occured, the corresponding <code>JavaModelStatus</code> is a
94 * multi-status. Otherwise, it is a simple one.
96 protected void executeOperation() throws JavaModelException {
100 * Returns the parent of the element being copied/moved/renamed.
102 protected IJavaElement getDestinationParent(IJavaElement child) {
103 return (IJavaElement)fParentElements.get(child);
106 * Returns the name to be used by the progress monitor.
108 protected abstract String getMainTaskName();
110 * Returns the new name for <code>element</code>, or <code>null</code>
111 * if there are no renamings specified.
113 protected String getNewNameFor(IJavaElement element) {
114 if (fRenamings != null)
115 return (String) fRenamings.get(element);
120 * Sets up the renamings hashtable - keys are the elements and
121 * values are the new name.
123 private void initializeRenamings() {
124 if (fRenamingsList != null && fRenamingsList.length == fElementsToProcess.length) {
125 fRenamings = new HashMap(fRenamingsList.length);
126 for (int i = 0; i < fRenamingsList.length; i++) {
127 if (fRenamingsList[i] != null) {
128 fRenamings.put(fElementsToProcess[i], fRenamingsList[i]);
134 * Returns <code>true</code> if this operation represents a move or rename, <code>false</code>
135 * if this operation represents a copy.<br>
136 * Note: a rename is just a move within the same parent with a name change.
138 protected boolean isMove() {
142 * Returns <code>true</code> if this operation represents a rename, <code>false</code>
143 * if this operation represents a copy or move.
145 protected boolean isRename() {
150 * Subclasses must implement this method to process a given <code>IJavaElement</code>.
152 protected abstract void processElement(IJavaElement element) throws JavaModelException;
154 * Processes all the <code>IJavaElement</code>s in turn, collecting errors
155 * and updating the progress monitor.
157 * @exception JavaModelException if one or several operation(s) was unable to
160 protected void processElements() throws JavaModelException {
161 beginTask(getMainTaskName(), fElementsToProcess.length);
162 IJavaModelStatus[] errors = new IJavaModelStatus[3];
163 int errorsCounter = 0;
164 for (int i = 0; i < fElementsToProcess.length; i++) {
166 verify(fElementsToProcess[i]);
167 processElement(fElementsToProcess[i]);
168 } catch (JavaModelException jme) {
169 if (errorsCounter == errors.length) {
171 System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter*2]), 0, errorsCounter);
173 errors[errorsCounter++] = jme.getJavaModelStatus();
179 if (errorsCounter == 1) {
180 throw new JavaModelException(errors[0]);
181 } else if (errorsCounter > 1) {
182 if (errorsCounter != errors.length) {
184 System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter]), 0, errorsCounter);
186 throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
190 * Sets the insertion position in the new container for the modified element. The element
191 * being modified will be inserted before the specified new sibling. The given sibling
192 * must be a child of the destination container specified for the modified element.
193 * The default is <code>null</code>, which indicates that the element is to be
194 * inserted at the end of the container.
196 public void setInsertBefore(IJavaElement modifiedElement, IJavaElement newSibling) {
197 fInsertBeforeElements.put(modifiedElement, newSibling);
200 * Sets the new names to use for each element being copied. The renamings
201 * correspond to the elements being processed, and the number of
202 * renamings must match the number of elements being processed.
203 * A <code>null</code> entry in the list indicates that an element
204 * is not to be renamed.
206 * <p>Note that some renamings may not be used. If both a parent
207 * and a child have been selected for copy/move, only the parent
208 * is changed. Therefore, if a new name is specified for the child,
209 * the child's name will not be changed.
211 public void setRenamings(String[] renamings) {
212 fRenamingsList = renamings;
213 initializeRenamings();
216 * This method is called for each <code>IJavaElement</code> before
217 * <code>processElement</code>. It should check that this <code>element</code>
220 protected abstract void verify(IJavaElement element) throws JavaModelException;
222 * Verifies that the <code>destination</code> specified for the <code>element</code> is valid for the types of the
223 * <code>element</code> and <code>destination</code>.
225 protected void verifyDestination(IJavaElement element, IJavaElement destination) throws JavaModelException {
226 if (destination == null || !destination.exists())
227 error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
229 int destType = destination.getElementType();
230 switch (element.getElementType()) {
231 case IJavaElement.PACKAGE_DECLARATION :
232 case IJavaElement.IMPORT_DECLARATION :
233 if (destType != IJavaElement.COMPILATION_UNIT)
234 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
236 case IJavaElement.TYPE :
237 if (destType != IJavaElement.COMPILATION_UNIT && destType != IJavaElement.TYPE)
238 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
240 case IJavaElement.METHOD :
241 case IJavaElement.FIELD :
242 // case IJavaElement.INITIALIZER :
243 // if (destType != IJavaElement.TYPE || destination instanceof BinaryType)
244 // error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
246 case IJavaElement.COMPILATION_UNIT :
247 if (destType != IJavaElement.PACKAGE_FRAGMENT)
248 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
249 else if (isMove() && ((ICompilationUnit) element).isWorkingCopy())
250 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
252 case IJavaElement.PACKAGE_FRAGMENT :
253 IPackageFragment fragment = (IPackageFragment) element;
254 IJavaElement parent = fragment.getParent();
255 if (parent.isReadOnly())
256 error(IJavaModelStatusConstants.READ_ONLY, element);
257 else if (destType != IJavaElement.PACKAGE_FRAGMENT_ROOT)
258 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
261 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
265 * Verify that the new name specified for <code>element</code> is
266 * valid for that type of Java element.
268 protected void verifyRenaming(IJavaElement element) throws JavaModelException {
269 String newName = getNewNameFor(element);
270 boolean isValid = true;
272 switch (element.getElementType()) {
273 case IJavaElement.PACKAGE_FRAGMENT :
274 if (element.getElementName().equals(IPackageFragment.DEFAULT_PACKAGE_NAME)) {
275 // don't allow renaming of default package (see PR #1G47GUM)
276 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, element));
278 // isValid = JavaConventions.validatePackageName(newName).getSeverity() != IStatus.ERROR;
281 case IJavaElement.COMPILATION_UNIT :
282 // isValid = JavaConventions.validateCompilationUnitName(newName).getSeverity() != IStatus.ERROR;
285 case IJavaElement.INITIALIZER :
286 isValid = false; //cannot rename initializers
289 // isValid = JavaConventions.validateIdentifier(newName).getSeverity() != IStatus.ERROR;
295 throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_NAME, element, newName));
299 * Verifies that the positioning sibling specified for the <code>element</code> is exists and
300 * its parent is the destination container of this <code>element</code>.
302 protected void verifySibling(IJavaElement element, IJavaElement destination) throws JavaModelException {
303 IJavaElement insertBeforeElement = (IJavaElement) fInsertBeforeElements.get(element);
304 if (insertBeforeElement != null) {
305 if (!insertBeforeElement.exists() || !insertBeforeElement.getParent().equals(destination)) {
306 error(IJavaModelStatusConstants.INVALID_SIBLING, insertBeforeElement);