1) Added missing strings for italic, underline and strike through.
[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  * 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.
28  * <p>
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.
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         /**
40          * Table specifying the new parent for elements being copied/moved/renamed.
41          * Keyed by elements being processed, and values are the corresponding
42          * destination parent.
43          */
44         protected Map fParentElements;
45
46         /**
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.
50          * 
51          * @see processElements(IProgressMonitor)
52          */
53         protected Map fInsertBeforeElements = new HashMap(1);
54
55         /**
56          * This table presents the data in <code>fRenamingList</code> in a more
57          * convenient way.
58          */
59         protected Map fRenamings;
60
61         /**
62          * Creates a new <code>MultiOperation</code>.
63          */
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]);
71                         }
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]);
76                         }
77                 }
78
79         }
80
81         /**
82          * Creates a new <code>MultiOperation</code> on
83          * <code>elementsToProcess</code>.
84          */
85         protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
86                 super(elementsToProcess, force);
87         }
88
89         /**
90          * Convenience method to create a <code>JavaModelException</code>
91          * embending a <code>JavaModelStatus</code>.
92          */
93         protected void error(int code, IJavaElement element)
94                         throws JavaModelException {
95                 throw new JavaModelException(new JavaModelStatus(code, element));
96         }
97
98         /**
99          * Executes the operation.
100          * 
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.
106          */
107         protected void executeOperation() throws JavaModelException {
108                 processElements();
109         }
110
111         /**
112          * Returns the parent of the element being copied/moved/renamed.
113          */
114         protected IJavaElement getDestinationParent(IJavaElement child) {
115                 return (IJavaElement) fParentElements.get(child);
116         }
117
118         /**
119          * Returns the name to be used by the progress monitor.
120          */
121         protected abstract String getMainTaskName();
122
123         /**
124          * Returns the new name for <code>element</code>, or <code>null</code>
125          * if there are no renamings specified.
126          */
127         protected String getNewNameFor(IJavaElement element) {
128                 if (fRenamings != null)
129                         return (String) fRenamings.get(element);
130                 else
131                         return null;
132         }
133
134         /**
135          * Sets up the renamings hashtable - keys are the elements and values are
136          * the new name.
137          */
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]);
145                                 }
146                         }
147                 }
148         }
149
150         /**
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.
154          */
155         protected boolean isMove() {
156                 return false;
157         }
158
159         /**
160          * Returns <code>true</code> if this operation represents a rename,
161          * <code>false</code> if this operation represents a copy or move.
162          */
163         protected boolean isRename() {
164                 return false;
165         }
166
167         /**
168          * Subclasses must implement this method to process a given
169          * <code>IJavaElement</code>.
170          */
171         protected abstract void processElement(IJavaElement element)
172                         throws JavaModelException;
173
174         /**
175          * Processes all the <code>IJavaElement</code>s in turn, collecting
176          * errors and updating the progress monitor.
177          * 
178          * @exception JavaModelException
179          *                if one or several operation(s) was unable to be completed.
180          */
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++) {
186                         try {
187                                 verify(fElementsToProcess[i]);
188                                 processElement(fElementsToProcess[i]);
189                         } catch (JavaModelException jme) {
190                                 if (errorsCounter == errors.length) {
191                                         // resize
192                                         System.arraycopy(errors, 0,
193                                                         (errors = new IJavaModelStatus[errorsCounter * 2]),
194                                                         0, errorsCounter);
195                                 }
196                                 errors[errorsCounter++] = jme.getJavaModelStatus();
197                         } finally {
198                                 worked(1);
199                         }
200                 }
201                 done();
202                 if (errorsCounter == 1) {
203                         throw new JavaModelException(errors[0]);
204                 } else if (errorsCounter > 1) {
205                         if (errorsCounter != errors.length) {
206                                 // resize
207                                 System.arraycopy(errors, 0,
208                                                 (errors = new IJavaModelStatus[errorsCounter]), 0,
209                                                 errorsCounter);
210                         }
211                         throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
212                 }
213         }
214
215         /**
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.
222          */
223         public void setInsertBefore(IJavaElement modifiedElement,
224                         IJavaElement newSibling) {
225                 fInsertBeforeElements.put(modifiedElement, newSibling);
226         }
227
228         /**
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.
233          * 
234          * <p>
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
238          * changed.
239          */
240         public void setRenamings(String[] renamings) {
241                 fRenamingsList = renamings;
242                 initializeRenamings();
243         }
244
245         /**
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.
249          */
250         protected abstract void verify(IJavaElement element)
251                         throws JavaModelException;
252
253         /**
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>.
257          */
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);
262
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);
269                         break;
270                 case IJavaElement.TYPE:
271                         if (destType != IJavaElement.COMPILATION_UNIT
272                                         && destType != IJavaElement.TYPE)
273                                 error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
274                         break;
275                 case IJavaElement.METHOD:
276                 case IJavaElement.FIELD:
277                         // case IJavaElement.INITIALIZER :
278                         // if (destType != IJavaElement.TYPE || destination instanceof
279                         // BinaryType)
280                         // error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
281                         // break;
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);
287                         break;
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);
295                         break;
296                 default:
297                         error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
298                 }
299         }
300
301         /**
302          * Verify that the new name specified for <code>element</code> is valid
303          * for that type of Java element.
304          */
305         protected void verifyRenaming(IJavaElement element)
306                         throws JavaModelException {
307                 String newName = getNewNameFor(element);
308                 boolean isValid = true;
309
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));
317                         }
318                         // isValid =
319                         // JavaConventions.validatePackageName(newName).getSeverity() !=
320                         // IStatus.ERROR;
321                         isValid = true;
322                         break;
323                 case IJavaElement.COMPILATION_UNIT:
324                         // isValid =
325                         // JavaConventions.validateCompilationUnitName(newName).getSeverity()
326                         // != IStatus.ERROR;
327                         isValid = true;
328                         break;
329                 case IJavaElement.INITIALIZER:
330                         isValid = false; // cannot rename initializers
331                         break;
332                 default:
333                         // isValid =
334                         // JavaConventions.validateIdentifier(newName).getSeverity() !=
335                         // IStatus.ERROR;
336                         isValid = true;
337                         break;
338                 }
339
340                 if (!isValid) {
341                         throw new JavaModelException(new JavaModelStatus(
342                                         IJavaModelStatusConstants.INVALID_NAME, element, newName));
343                 }
344         }
345
346         /**
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>.
350          */
351         protected void verifySibling(IJavaElement element, IJavaElement destination)
352                         throws JavaModelException {
353                 IJavaElement insertBeforeElement = (IJavaElement) fInsertBeforeElements
354                                 .get(element);
355                 if (insertBeforeElement != null) {
356                         if (!insertBeforeElement.exists()
357                                         || !insertBeforeElement.getParent().equals(destination)) {
358                                 error(IJavaModelStatusConstants.INVALID_SIBLING,
359                                                 insertBeforeElement);
360                         }
361                 }
362         }
363 }