Parser detects wrong include files now
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / MultiOperation.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import java.util.HashMap;
14 import java.util.Map;
15
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;
22
23
24 /**
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.
28  * <p>
29  * If several errors occured, they are collected in a multi-status
30  * <code>JavaModelStatus</code>. Otherwise, a simple <code>JavaModelStatus</code>
31  * is thrown.
32  */
33 public abstract class MultiOperation extends JavaModelOperation {
34         /**
35          * The list of renamings supplied to the operation
36          */
37         protected String[] fRenamingsList= null;
38         /**
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.
43          */
44         protected Map fParentElements;
45         /**
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)
50          */
51         protected Map fInsertBeforeElements= new HashMap(1);
52         /**
53          * This table presents the data in <code>fRenamingList</code> in a more
54          * convenient way.
55          */
56         protected Map fRenamings;
57         /**
58          * Creates a new <code>MultiOperation</code>.
59          */
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]);
66                         }
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]);
70                         }
71                 }
72         
73         }
74         /**
75          * Creates a new <code>MultiOperation</code> on <code>elementsToProcess</code>.
76          */
77         protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
78                 super(elementsToProcess, force);
79         }
80         /**
81          * Convenience method to create a <code>JavaModelException</code>
82          * embending a <code>JavaModelStatus</code>.
83          */
84         protected void error(int code, IJavaElement element) throws JavaModelException {
85                 throw new JavaModelException(new JavaModelStatus(code, element));
86         }
87         /**
88          * Executes the operation.
89          *
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.
93          */
94         protected void executeOperation() throws JavaModelException {
95                 processElements();
96         }
97         /**
98          * Returns the parent of the element being copied/moved/renamed.
99          */
100         protected IJavaElement getDestinationParent(IJavaElement child) {
101                 return (IJavaElement)fParentElements.get(child);
102         }
103         /**
104          * Returns the name to be used by the progress monitor.
105          */
106         protected abstract String getMainTaskName();
107         /**
108          * Returns the new name for <code>element</code>, or <code>null</code>
109          * if there are no renamings specified.
110          */
111         protected String getNewNameFor(IJavaElement element) {
112                 if (fRenamings != null)
113                         return (String) fRenamings.get(element);
114                 else
115                         return null;
116         }
117         /**
118          * Sets up the renamings hashtable - keys are the elements and
119          * values are the new name.
120          */
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]);
127                                 }
128                         }
129                 }
130         }
131         /**
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.
135          */
136         protected boolean isMove() {
137                 return false;
138         }
139         /**
140          * Returns <code>true</code> if this operation represents a rename, <code>false</code>
141          * if this operation represents a copy or move.
142          */
143         protected boolean isRename() {
144                 return false;
145         }
146         
147         /**
148          * Subclasses must implement this method to process a given <code>IJavaElement</code>.
149          */
150         protected abstract void processElement(IJavaElement element) throws JavaModelException;
151         /**
152          * Processes all the <code>IJavaElement</code>s in turn, collecting errors
153          * and updating the progress monitor.
154          *
155          * @exception JavaModelException if one or several operation(s) was unable to
156          * be completed.
157          */
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++) {
163                         try {
164                                 verify(fElementsToProcess[i]);
165                                 processElement(fElementsToProcess[i]);
166                         } catch (JavaModelException jme) {
167                                 if (errorsCounter == errors.length) {
168                                         // resize
169                                         System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter*2]), 0, errorsCounter);
170                                 }
171                                 errors[errorsCounter++] = jme.getJavaModelStatus();
172                         } finally {
173                                 worked(1);
174                         }
175                 }
176                 done();
177                 if (errorsCounter == 1) {
178                         throw new JavaModelException(errors[0]);
179                 } else if (errorsCounter > 1) {
180                         if (errorsCounter != errors.length) {
181                                 // resize
182                                 System.arraycopy(errors, 0, (errors = new IJavaModelStatus[errorsCounter]), 0, errorsCounter);
183                         }
184                         throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
185                 }
186         }
187         /**
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.
193          */
194         public void setInsertBefore(IJavaElement modifiedElement, IJavaElement newSibling) {
195                 fInsertBeforeElements.put(modifiedElement, newSibling);
196         }
197         /**
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.
203          *
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.
208          */
209         public void setRenamings(String[] renamings) {
210                 fRenamingsList = renamings;
211                 initializeRenamings();
212         }
213         /**
214          * This method is called for each <code>IJavaElement</code> before
215          * <code>processElement</code>. It should check that this <code>element</code>
216          * can be processed.
217          */
218         protected abstract void verify(IJavaElement element) throws JavaModelException;
219         /**
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>.
222          */
223         protected void verifyDestination(IJavaElement element, IJavaElement destination) throws JavaModelException {
224                 if (destination == null || !destination.exists())
225                         error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
226                 
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);
233                                 break;
234                         case IJavaElement.TYPE :
235                                 if (destType != IJavaElement.COMPILATION_UNIT && destType != IJavaElement.TYPE)
236                                         error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
237                                 break;
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);
243 //                              break;
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);
249                                 break;
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);
257                                 break;
258                         default :
259                                 error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
260                 }
261         }
262         /**
263          * Verify that the new name specified for <code>element</code> is
264          * valid for that type of Java element.
265          */
266         protected void verifyRenaming(IJavaElement element) throws JavaModelException {
267                 String newName = getNewNameFor(element);
268                 boolean isValid = true;
269         
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));
275                                 }
276 //                              isValid = JavaConventions.validatePackageName(newName).getSeverity() != IStatus.ERROR;
277                                 isValid = true;
278                                 break;
279                         case IJavaElement.COMPILATION_UNIT :
280 //                              isValid = JavaConventions.validateCompilationUnitName(newName).getSeverity() != IStatus.ERROR;
281                         isValid = true;
282                                 break;
283                         case IJavaElement.INITIALIZER :
284                                 isValid = false; //cannot rename initializers
285                                 break;
286                         default :
287 //                              isValid = JavaConventions.validateIdentifier(newName).getSeverity() != IStatus.ERROR;
288                         isValid = true;
289                                 break;
290                 }
291         
292                 if (!isValid) {
293                         throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_NAME, element, newName));
294                 }
295         }
296         /**
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>.
299          */
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);
305                         }
306                 }
307         }
308 }