1) Added missing strings for italic, underline and strike through.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaModelOperation.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.io.InputStream;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16
17 import net.sourceforge.phpdt.core.ICompilationUnit;
18 import net.sourceforge.phpdt.core.IJavaElement;
19 import net.sourceforge.phpdt.core.IJavaElementDelta;
20 import net.sourceforge.phpdt.core.IJavaModel;
21 import net.sourceforge.phpdt.core.IJavaModelStatus;
22 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
23 import net.sourceforge.phpdt.core.IPackageFragment;
24 import net.sourceforge.phpdt.core.IWorkingCopy;
25 import net.sourceforge.phpdt.core.JavaModelException;
26 import net.sourceforge.phpdt.internal.core.util.PerThreadObject;
27 import net.sourceforge.phpdt.internal.core.util.Util;
28
29 import org.eclipse.core.resources.IContainer;
30 //import org.eclipse.core.resources.IFile;
31 import org.eclipse.core.resources.IFolder;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IResourceStatus;
34 import org.eclipse.core.resources.IWorkspace;
35 import org.eclipse.core.resources.IWorkspaceRunnable;
36 import org.eclipse.core.resources.ResourcesPlugin;
37 import org.eclipse.core.runtime.CoreException;
38 import org.eclipse.core.runtime.IPath;
39 import org.eclipse.core.runtime.IProgressMonitor;
40 import org.eclipse.core.runtime.OperationCanceledException;
41 import org.eclipse.core.runtime.Path;
42 import org.eclipse.core.runtime.SubProgressMonitor;
43 import org.eclipse.core.runtime.jobs.ISchedulingRule;
44
45 /**
46  * Defines behavior common to all Java Model operations
47  */
48 public abstract class JavaModelOperation implements IWorkspaceRunnable,
49                 IProgressMonitor {
50         protected interface IPostAction {
51                 /*
52                  * Returns the id of this action.
53                  * 
54                  * @see JavaModelOperation#postAction
55                  */
56                 String getID();
57
58                 /*
59                  * Run this action.
60                  */
61                 void run() throws JavaModelException;
62         }
63
64         /*
65          * Constants controlling the insertion mode of an action.
66          * 
67          * @see JavaModelOperation#postAction
68          */
69         protected static final int APPEND = 1; // insert at the end
70
71         protected static final int REMOVEALL_APPEND = 2; // remove all existing
72                                                                                                                 // ones with same ID,
73                                                                                                                 // and add new one at
74                                                                                                                 // the end
75
76         protected static final int KEEP_EXISTING = 3; // do not insert if already
77                                                                                                         // existing with same ID
78
79         /*
80          * Whether tracing post actions is enabled.
81          */
82         protected static boolean POST_ACTION_VERBOSE;
83
84         /*
85          * A list of IPostActions.
86          */
87         protected IPostAction[] actions;
88
89         protected int actionsStart = 0;
90
91         protected int actionsEnd = -1;
92
93         /*
94          * A HashMap of attributes that can be used by operations
95          */
96         protected HashMap attributes;
97
98         public static final String HAS_MODIFIED_RESOURCE_ATTR = "hasModifiedResource"; //$NON-NLS-1$
99
100         public static final String TRUE = "true"; //$NON-NLS-1$
101
102         // public static final String FALSE = "false"; //$NON-NLS-1$
103
104         /**
105          * The elements this operation operates on, or <code>null</code> if this
106          * operation does not operate on specific elements.
107          */
108         protected IJavaElement[] fElementsToProcess;
109
110         /**
111          * The parent elements this operation operates with or <code>null</code>
112          * if this operation does not operate with specific parent elements.
113          */
114         protected IJavaElement[] fParentElements;
115
116         /**
117          * An empty collection of <code>IJavaElement</code> s - the common empty
118          * result if no elements are created, or if this operation is not actually
119          * executed.
120          */
121         protected static IJavaElement[] NO_ELEMENTS = new IJavaElement[] {};
122
123         /**
124          * The elements created by this operation - empty until the operation
125          * actually creates elements.
126          */
127         protected IJavaElement[] resultElements = NO_ELEMENTS;
128
129         /**
130          * The progress monitor passed into this operation
131          */
132         protected IProgressMonitor progressMonitor = null;
133
134         /**
135          * A flag indicating whether this operation is nested.
136          */
137         protected boolean isNested = false;
138
139         /**
140          * Conflict resolution policy - by default do not force (fail on a
141          * conflict).
142          */
143         protected boolean force = false;
144
145         /*
146          * A per thread stack of java model operations (PerThreadObject of
147          * ArrayList).
148          */
149         protected static PerThreadObject operationStacks = new PerThreadObject();
150
151         protected JavaModelOperation() {
152         }
153
154         /**
155          * A common constructor for all Java Model operations.
156          */
157         protected JavaModelOperation(IJavaElement[] elements) {
158                 fElementsToProcess = elements;
159         }
160
161         /**
162          * Common constructor for all Java Model operations.
163          */
164         protected JavaModelOperation(IJavaElement[] elementsToProcess,
165                         IJavaElement[] parentElements) {
166                 fElementsToProcess = elementsToProcess;
167                 fParentElements = parentElements;
168         }
169
170         /**
171          * A common constructor for all Java Model operations.
172          */
173         protected JavaModelOperation(IJavaElement[] elementsToProcess,
174                         IJavaElement[] parentElements, boolean force) {
175                 fElementsToProcess = elementsToProcess;
176                 fParentElements = parentElements;
177                 this.force = force;
178         }
179
180         /**
181          * A common constructor for all Java Model operations.
182          */
183         protected JavaModelOperation(IJavaElement[] elements, boolean force) {
184                 fElementsToProcess = elements;
185                 this.force = force;
186         }
187
188         /**
189          * Common constructor for all Java Model operations.
190          */
191 //      protected JavaModelOperation(IJavaElement element) {
192 //              fElementsToProcess = new IJavaElement[] { element };
193 //      }
194
195         /**
196          * A common constructor for all Java Model operations.
197          */
198 //      protected JavaModelOperation(IJavaElement element, boolean force) {
199 //              fElementsToProcess = new IJavaElement[] { element };
200 //              this.force = force;
201 //      }
202
203         /*
204          * Registers the given action at the end of the list of actions to run.
205          */
206         protected void addAction(IPostAction action) {
207                 int length = this.actions.length;
208                 if (length == ++this.actionsEnd) {
209                         System.arraycopy(this.actions, 0,
210                                         this.actions = new IPostAction[length * 2], 0, length);
211                 }
212                 this.actions[this.actionsEnd] = action;
213         }
214
215         /*
216          * Registers the given delta with the Java Model Manager.
217          */
218         protected void addDelta(IJavaElementDelta delta) {
219                 JavaModelManager.getJavaModelManager().registerJavaModelDelta(delta);
220         }
221
222         /*
223          * Registers the given reconcile delta with the Java Model Manager.
224          */
225         protected void addReconcileDelta(IWorkingCopy workingCopy,
226                         IJavaElementDelta delta) {
227                 HashMap reconcileDeltas = JavaModelManager.getJavaModelManager().reconcileDeltas;
228                 JavaElementDelta previousDelta = (JavaElementDelta) reconcileDeltas
229                                 .get(workingCopy);
230                 if (previousDelta != null) {
231                         IJavaElementDelta[] children = delta.getAffectedChildren();
232                         for (int i = 0, length = children.length; i < length; i++) {
233                                 JavaElementDelta child = (JavaElementDelta) children[i];
234                                 previousDelta.insertDeltaTree(child.getElement(), child);
235                         }
236                 } else {
237                         reconcileDeltas.put(workingCopy, delta);
238                 }
239         }
240
241         /*
242          * Deregister the reconcile delta for the given working copy
243          */
244         protected void removeReconcileDelta(IWorkingCopy workingCopy) {
245                 JavaModelManager.getJavaModelManager().reconcileDeltas
246                                 .remove(workingCopy);
247         }
248
249         /**
250          * @see IProgressMonitor
251          */
252         public void beginTask(String name, int totalWork) {
253                 if (progressMonitor != null) {
254                         progressMonitor.beginTask(name, totalWork);
255                 }
256         }
257
258         /**
259          * Checks with the progress monitor to see whether this operation should be
260          * canceled. An operation should regularly call this method during its
261          * operation so that the user can cancel it.
262          * 
263          * @exception OperationCanceledException
264          *                if cancelling the operation has been requested
265          * @see IProgressMonitor#isCanceled
266          */
267         protected void checkCanceled() {
268                 if (isCanceled()) {
269                         throw new OperationCanceledException(Util
270                                         .bind("operation.cancelled")); //$NON-NLS-1$
271                 }
272         }
273
274         /**
275          * Common code used to verify the elements this operation is processing.
276          * 
277          * @see JavaModelOperation#verify()
278          */
279         protected IJavaModelStatus commonVerify() {
280                 if (fElementsToProcess == null || fElementsToProcess.length == 0) {
281                         return new JavaModelStatus(
282                                         IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
283                 }
284                 for (int i = 0; i < fElementsToProcess.length; i++) {
285                         if (fElementsToProcess[i] == null) {
286                                 return new JavaModelStatus(
287                                                 IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
288                         }
289                 }
290                 return JavaModelStatus.VERIFIED_OK;
291         }
292
293         /**
294          * Convenience method to copy resources
295          */
296         protected void copyResources(IResource[] resources, IPath destinationPath)
297                         throws JavaModelException {
298                 IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
299                 IWorkspace workspace = resources[0].getWorkspace();
300                 try {
301                         workspace.copy(resources, destinationPath, false,
302                                         subProgressMonitor);
303                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
304                 } catch (CoreException e) {
305                         throw new JavaModelException(e);
306                 }
307         }
308
309         /**
310          * Convenience method to create a file
311          */
312 //      protected void createFile(IContainer folder, String name,
313 //                      InputStream contents, boolean force) throws JavaModelException {
314 //              IFile file = folder.getFile(new Path(name));
315 //              try {
316 //                      file.create(contents, force ? IResource.FORCE
317 //                                      | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
318 //                                      getSubProgressMonitor(1));
319 //                      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
320 //              } catch (CoreException e) {
321 //                      throw new JavaModelException(e);
322 //              }
323 //      }
324
325         /**
326          * Convenience method to create a folder
327          */
328         protected void createFolder(IContainer parentFolder, String name,
329                         boolean force) throws JavaModelException {
330                 IFolder folder = parentFolder.getFolder(new Path(name));
331                 try {
332                         // we should use true to create the file locally. Only VCM should
333                         // use tru/false
334                         folder.create(force ? IResource.FORCE | IResource.KEEP_HISTORY
335                                         : IResource.KEEP_HISTORY, true, // local
336                                         getSubProgressMonitor(1));
337                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
338                 } catch (CoreException e) {
339                         throw new JavaModelException(e);
340                 }
341         }
342
343         /**
344          * Convenience method to delete an empty package fragment
345          */
346         protected void deleteEmptyPackageFragment(IPackageFragment fragment,
347                         boolean force, IResource rootResource) throws JavaModelException {
348
349                 IContainer resource = (IContainer) fragment.getResource();
350
351                 try {
352                         resource.delete(force ? IResource.FORCE | IResource.KEEP_HISTORY
353                                         : IResource.KEEP_HISTORY, getSubProgressMonitor(1));
354                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
355                         while (resource instanceof IFolder) {
356                                 // deleting a package: delete the parent if it is empty (eg.
357                                 // deleting x.y where folder x doesn't have resources but y)
358                                 // without deleting the package fragment root
359                                 resource = resource.getParent();
360                                 if (!resource.equals(rootResource)
361                                                 && resource.members().length == 0) {
362                                         resource.delete(force ? IResource.FORCE
363                                                         | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
364                                                         getSubProgressMonitor(1));
365                                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
366                                 }
367                         }
368                 } catch (CoreException e) {
369                         throw new JavaModelException(e);
370                 }
371         }
372
373         /**
374          * Convenience method to delete a resource
375          */
376         protected void deleteResource(IResource resource, int flags)
377                         throws JavaModelException {
378                 try {
379                         resource.delete(flags, getSubProgressMonitor(1));
380                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
381                 } catch (CoreException e) {
382                         throw new JavaModelException(e);
383                 }
384         }
385
386         /**
387          * Convenience method to delete resources
388          */
389         protected void deleteResources(IResource[] resources, boolean force)
390                         throws JavaModelException {
391                 if (resources == null || resources.length == 0)
392                         return;
393                 IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
394                 IWorkspace workspace = resources[0].getWorkspace();
395                 try {
396                         workspace.delete(resources, force ? IResource.FORCE
397                                         | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
398                                         subProgressMonitor);
399                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
400                 } catch (CoreException e) {
401                         throw new JavaModelException(e);
402                 }
403         }
404
405         /**
406          * @see IProgressMonitor
407          */
408         public void done() {
409                 if (progressMonitor != null) {
410                         progressMonitor.done();
411                 }
412         }
413
414         /*
415          * Returns whether the given path is equals to one of the given other paths.
416          */
417 //      protected boolean equalsOneOf(IPath path, IPath[] otherPaths) {
418 //              for (int i = 0, length = otherPaths.length; i < length; i++) {
419 //                      if (path.equals(otherPaths[i])) {
420 //                              return true;
421 //                      }
422 //              }
423 //              return false;
424 //      }
425
426         /**
427          * Verifies the operation can proceed and executes the operation. Subclasses
428          * should override <code>#verify</code> and <code>executeOperation</code>
429          * to implement the specific operation behavior.
430          * 
431          * @exception JavaModelException
432          *                The operation has failed.
433          */
434         // protected void execute() throws JavaModelException {
435         // IJavaModelStatus status= verify();
436         // if (status.isOK()) {
437         // // if first time here, computes the root infos before executing the
438         // operation
439         // DeltaProcessor deltaProcessor =
440         // JavaModelManager.getJavaModelManager().deltaProcessor;
441         // if (deltaProcessor.roots == null) {
442         // deltaProcessor.initializeRoots();
443         // }
444         // executeOperation();
445         // } else {
446         // throw new JavaModelException(status);
447         // }
448         // }
449         /**
450          * Convenience method to run an operation within this operation
451          */
452         public void executeNestedOperation(JavaModelOperation operation,
453                         int subWorkAmount) throws JavaModelException {
454                 IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
455                 // fix for 1FW7IKC, part (1)
456                 try {
457                         operation.setNested(true);
458                         operation.run(subProgressMonitor);
459                 } catch (CoreException ce) {
460                         if (ce instanceof JavaModelException) {
461                                 throw (JavaModelException) ce;
462                         } else {
463                                 // translate the core exception to a java model exception
464                                 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
465                                         Throwable e = ce.getStatus().getException();
466                                         if (e instanceof JavaModelException) {
467                                                 throw (JavaModelException) e;
468                                         }
469                                 }
470                                 throw new JavaModelException(ce);
471                         }
472                 }
473         }
474
475         /**
476          * Performs the operation specific behavior. Subclasses must override.
477          */
478         protected abstract void executeOperation() throws JavaModelException;
479
480         /*
481          * Returns the attribute registered at the given key with the top level
482          * operation. Returns null if no such attribute is found.
483          */
484         protected Object getAttribute(Object key) {
485                 ArrayList stack = this.getCurrentOperationStack();
486                 if (stack.size() == 0)
487                         return null;
488                 JavaModelOperation topLevelOp = (JavaModelOperation) stack.get(0);
489                 if (topLevelOp.attributes == null) {
490                         return null;
491                 } else {
492                         return topLevelOp.attributes.get(key);
493                 }
494         }
495
496         /**
497          * Returns the compilation unit the given element is contained in, or the
498          * element itself (if it is a compilation unit), otherwise <code>null</code>.
499          */
500         protected ICompilationUnit getCompilationUnitFor(IJavaElement element) {
501
502                 return ((JavaElement) element).getCompilationUnit();
503         }
504
505         /*
506          * Returns the stack of operations running in the current thread. Returns an
507          * empty stack if no operations are currently running in this thread.
508          */
509         protected ArrayList getCurrentOperationStack() {
510                 ArrayList stack = (ArrayList) operationStacks.getCurrent();
511                 if (stack == null) {
512                         stack = new ArrayList();
513                         operationStacks.setCurrent(stack);
514                 }
515                 return stack;
516         }
517
518         /**
519          * Returns the elements to which this operation applies, or
520          * <code>null</code> if not applicable.
521          */
522         protected IJavaElement[] getElementsToProcess() {
523                 return fElementsToProcess;
524         }
525
526         /**
527          * Returns the element to which this operation applies, or <code>null</code>
528          * if not applicable.
529          */
530         protected IJavaElement getElementToProcess() {
531                 if (fElementsToProcess == null || fElementsToProcess.length == 0) {
532                         return null;
533                 }
534                 return fElementsToProcess[0];
535         }
536
537         /**
538          * Returns the Java Model this operation is operating in.
539          */
540         public IJavaModel getJavaModel() {
541                 if (fElementsToProcess == null || fElementsToProcess.length == 0) {
542                         return getParentElement().getJavaModel();
543                 } else {
544                         return fElementsToProcess[0].getJavaModel();
545                 }
546         }
547
548         // protected IPath[] getNestedFolders(IPackageFragmentRoot root) throws
549         // JavaModelException {
550         // IPath rootPath = root.getPath();
551         // IClasspathEntry[] classpath = root.getJavaProject().getRawClasspath();
552         // int length = classpath.length;
553         // IPath[] result = new IPath[length];
554         // int index = 0;
555         // for (int i = 0; i < length; i++) {
556         // IPath path = classpath[i].getPath();
557         // if (rootPath.isPrefixOf(path) && !rootPath.equals(path)) {
558         // result[index++] = path;
559         // }
560         // }
561         // if (index < length) {
562         // System.arraycopy(result, 0, result = new IPath[index], 0, index);
563         // }
564         // return result;
565         // }
566         /**
567          * Returns the parent element to which this operation applies, or
568          * <code>null</code> if not applicable.
569          */
570         protected IJavaElement getParentElement() {
571                 if (fParentElements == null || fParentElements.length == 0) {
572                         return null;
573                 }
574                 return fParentElements[0];
575         }
576
577         /**
578          * Returns the parent elements to which this operation applies, or
579          * <code>null</code> if not applicable.
580          */
581 //      protected IJavaElement[] getParentElements() {
582 //              return fParentElements;
583 //      }
584
585         /**
586          * Returns the elements created by this operation.
587          */
588 //      public IJavaElement[] getResultElements() {
589 //              return resultElements;
590 //      }
591
592         /*
593          * Returns the scheduling rule for this operation (i.e. the resource that
594          * needs to be locked while this operation is running. Subclasses can
595          * override.
596          */
597         protected ISchedulingRule getSchedulingRule() {
598                 return ResourcesPlugin.getWorkspace().getRoot();
599         }
600
601         /**
602          * Creates and returns a subprogress monitor if appropriate.
603          */
604         protected IProgressMonitor getSubProgressMonitor(int workAmount) {
605                 IProgressMonitor sub = null;
606                 if (progressMonitor != null) {
607                         sub = new SubProgressMonitor(progressMonitor, workAmount,
608                                         SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
609                 }
610                 return sub;
611         }
612
613         /**
614          * Returns whether this operation has performed any resource modifications.
615          * Returns false if this operation has not been executed yet.
616          */
617         public boolean hasModifiedResource() {
618                 return !this.isReadOnly()
619                                 && this.getAttribute(HAS_MODIFIED_RESOURCE_ATTR) == TRUE;
620         }
621
622         public void internalWorked(double work) {
623                 if (progressMonitor != null) {
624                         progressMonitor.internalWorked(work);
625                 }
626         }
627
628         /**
629          * @see IProgressMonitor
630          */
631         public boolean isCanceled() {
632                 if (progressMonitor != null) {
633                         return progressMonitor.isCanceled();
634                 }
635                 return false;
636         }
637
638         /**
639          * Returns <code>true</code> if this operation performs no resource
640          * modifications, otherwise <code>false</code>. Subclasses must override.
641          */
642         public boolean isReadOnly() {
643                 return false;
644         }
645
646         /*
647          * Returns whether this operation is the first operation to run in the
648          * current thread.
649          */
650         protected boolean isTopLevelOperation() {
651                 ArrayList stack;
652                 return (stack = this.getCurrentOperationStack()).size() > 0
653                                 && stack.get(0) == this;
654         }
655
656         /*
657          * Returns the index of the first registered action with the given id,
658          * starting from a given position. Returns -1 if not found.
659          */
660         protected int firstActionWithID(String id, int start) {
661                 for (int i = start; i <= this.actionsEnd; i++) {
662                         if (this.actions[i].getID().equals(id)) {
663                                 return i;
664                         }
665                 }
666                 return -1;
667         }
668
669         /**
670          * Convenience method to move resources
671          */
672         protected void moveResources(IResource[] resources, IPath destinationPath)
673                         throws JavaModelException {
674                 IProgressMonitor subProgressMonitor = null;
675                 if (progressMonitor != null) {
676                         subProgressMonitor = new SubProgressMonitor(progressMonitor,
677                                         resources.length,
678                                         SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
679                 }
680                 IWorkspace workspace = resources[0].getWorkspace();
681                 try {
682                         workspace.move(resources, destinationPath, false,
683                                         subProgressMonitor);
684                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
685                 } catch (CoreException e) {
686                         throw new JavaModelException(e);
687                 }
688         }
689
690         /**
691          * Creates and returns a new <code>IJavaElementDelta</code> on the Java
692          * Model.
693          */
694         public JavaElementDelta newJavaElementDelta() {
695                 return new JavaElementDelta(getJavaModel());
696         }
697
698         /*
699          * Removes the last pushed operation from the stack of running operations.
700          * Returns the poped operation or null if the stack was empty.
701          */
702         protected JavaModelOperation popOperation() {
703                 ArrayList stack = getCurrentOperationStack();
704                 int size = stack.size();
705                 if (size > 0) {
706                         if (size == 1) { // top level operation
707                                 operationStacks.setCurrent(null); // release reference (see
708                                                                                                         // http://bugs.eclipse.org/bugs/show_bug.cgi?id=33927)
709                         }
710                         return (JavaModelOperation) stack.remove(size - 1);
711                 } else {
712                         return null;
713                 }
714         }
715
716         /*
717          * Registers the given action to be run when the outer most java model
718          * operation has finished. The insertion mode controls whether: - the action
719          * should discard all existing actions with the same id, and be queued at
720          * the end (REMOVEALL_APPEND), - the action should be ignored if there is
721          * already an action with the same id (KEEP_EXISTING), - the action should
722          * be queued at the end without looking at existing actions (APPEND)
723          */
724         protected void postAction(IPostAction action, int insertionMode) {
725                 if (POST_ACTION_VERBOSE) {
726                         System.out
727                                         .print("(" + Thread.currentThread() + ") [JavaModelOperation.postAction(IPostAction, int)] Posting action " + action.getID()); //$NON-NLS-1$ //$NON-NLS-2$
728                         switch (insertionMode) {
729                         case REMOVEALL_APPEND:
730                                 System.out.println(" (REMOVEALL_APPEND)"); //$NON-NLS-1$
731                                 break;
732                         case KEEP_EXISTING:
733                                 System.out.println(" (KEEP_EXISTING)"); //$NON-NLS-1$
734                                 break;
735                         case APPEND:
736                                 System.out.println(" (APPEND)"); //$NON-NLS-1$
737                                 break;
738                         }
739                 }
740
741                 JavaModelOperation topLevelOp = (JavaModelOperation) getCurrentOperationStack()
742                                 .get(0);
743                 IPostAction[] postActions = topLevelOp.actions;
744                 if (postActions == null) {
745                         topLevelOp.actions = postActions = new IPostAction[1];
746                         postActions[0] = action;
747                         topLevelOp.actionsEnd = 0;
748                 } else {
749                         String id = action.getID();
750                         switch (insertionMode) {
751                         case REMOVEALL_APPEND:
752                                 int index = this.actionsStart - 1;
753                                 while ((index = topLevelOp.firstActionWithID(id, index + 1)) >= 0) {
754                                         // remove action[index]
755                                         System.arraycopy(postActions, index + 1, postActions,
756                                                         index, topLevelOp.actionsEnd - index);
757                                         postActions[topLevelOp.actionsEnd--] = null;
758                                 }
759                                 topLevelOp.addAction(action);
760                                 break;
761                         case KEEP_EXISTING:
762                                 if (topLevelOp.firstActionWithID(id, 0) < 0) {
763                                         topLevelOp.addAction(action);
764                                 }
765                                 break;
766                         case APPEND:
767                                 topLevelOp.addAction(action);
768                                 break;
769                         }
770                 }
771         }
772
773         /*
774          * Returns whether the given path is the prefix of one of the given other
775          * paths.
776          */
777 //      protected boolean prefixesOneOf(IPath path, IPath[] otherPaths) {
778 //              for (int i = 0, length = otherPaths.length; i < length; i++) {
779 //                      if (path.isPrefixOf(otherPaths[i])) {
780 //                              return true;
781 //                      }
782 //              }
783 //              return false;
784 //      }
785
786         /*
787          * Pushes the given operation on the stack of operations currently running
788          * in this thread.
789          */
790         protected void pushOperation(JavaModelOperation operation) {
791                 getCurrentOperationStack().add(operation);
792         }
793
794         /**
795          * Main entry point for Java Model operations. Executes this operation and
796          * registers any deltas created.
797          * 
798          * @see IWorkspaceRunnable
799          * @exception CoreException
800          *                if the operation fails
801          */
802         public void run(IProgressMonitor monitor) throws CoreException {
803                 JavaModelManager manager = JavaModelManager.getJavaModelManager();
804                 DeltaProcessor deltaProcessor = manager.getDeltaProcessor();
805                 int previousDeltaCount = deltaProcessor.javaModelDeltas.size();
806                 try {
807                         progressMonitor = monitor;
808                         pushOperation(this);
809                         try {
810                                 // computes the root infos before executing the operation
811                                 // noop if aready initialized
812                                 JavaModelManager.getJavaModelManager().deltaState
813                                                 .initializeRoots();
814
815                                 executeOperation();
816                         } finally {
817                                 if (this.isTopLevelOperation()) {
818                                         this.runPostActions();
819                                 }
820                         }
821                 } finally {
822
823                         try {
824                                 // update JavaModel using deltas that were recorded during this
825                                 // operation
826                                 for (int i = previousDeltaCount, size = manager.javaModelDeltas
827                                                 .size(); i < size; i++) {
828                                         manager
829                                                         .updateJavaModel((IJavaElementDelta) manager.javaModelDeltas
830                                                                         .get(i));
831                                 }
832
833                                 // fire only iff:
834                                 // - the operation is a top level operation
835                                 // - the operation did produce some delta(s)
836                                 // - but the operation has not modified any resource
837                                 if (this.isTopLevelOperation()) {
838                                         if ((manager.javaModelDeltas.size() > previousDeltaCount || !manager.reconcileDeltas
839                                                         .isEmpty())
840                                                         && !this.hasModifiedResource()) {
841                                                 manager.fire(null,
842                                                                 JavaModelManager.DEFAULT_CHANGE_EVENT);
843                                         } // else deltas are fired while processing the resource
844                                                 // delta
845                                 }
846                         } finally {
847                                 popOperation();
848                         }
849                 }
850         }
851
852         /**
853          * Main entry point for Java Model operations. Runs a Java Model Operation
854          * as an IWorkspaceRunnable if not read-only.
855          */
856         public void runOperation(IProgressMonitor monitor)
857                         throws JavaModelException {
858                 IJavaModelStatus status = verify();
859                 if (!status.isOK()) {
860                         throw new JavaModelException(status);
861                 }
862                 try {
863                         if (isReadOnly()) {
864                                 run(monitor);
865                         } else {
866                                 // Use IWorkspace.run(...) to ensure that a build will be done
867                                 // in autobuild mode.
868                                 // Note that if the tree is locked, this will throw a
869                                 // CoreException, but this is ok
870                                 // as this operation is modifying the tree (not read-only) and a
871                                 // CoreException will be thrown anyway.
872                                 ResourcesPlugin.getWorkspace().run(this, getSchedulingRule(),
873                                                 IWorkspace.AVOID_UPDATE, monitor);
874                         }
875                 } catch (CoreException ce) {
876                         if (ce instanceof JavaModelException) {
877                                 throw (JavaModelException) ce;
878                         } else {
879                                 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
880                                         Throwable e = ce.getStatus().getException();
881                                         if (e instanceof JavaModelException) {
882                                                 throw (JavaModelException) e;
883                                         }
884                                 }
885                                 throw new JavaModelException(ce);
886                         }
887                 }
888         }
889
890         protected void runPostActions() throws JavaModelException {
891                 while (this.actionsStart <= this.actionsEnd) {
892                         IPostAction postAction = this.actions[this.actionsStart++];
893                         if (POST_ACTION_VERBOSE) {
894                                 System.out
895                                                 .println("(" + Thread.currentThread() + ") [JavaModelOperation.runPostActions()] Running action " + postAction.getID()); //$NON-NLS-1$ //$NON-NLS-2$
896                         }
897                         postAction.run();
898                 }
899         }
900
901         /*
902          * Registers the given attribute at the given key with the top level
903          * operation.
904          */
905         protected void setAttribute(Object key, Object attribute) {
906                 JavaModelOperation topLevelOp = (JavaModelOperation) this
907                                 .getCurrentOperationStack().get(0);
908                 if (topLevelOp.attributes == null) {
909                         topLevelOp.attributes = new HashMap();
910                 }
911                 topLevelOp.attributes.put(key, attribute);
912         }
913
914         /**
915          * @see IProgressMonitor
916          */
917         public void setCanceled(boolean b) {
918                 if (progressMonitor != null) {
919                         progressMonitor.setCanceled(b);
920                 }
921         }
922
923         /**
924          * Sets whether this operation is nested or not.
925          * 
926          * @see CreateElementInCUOperation#checkCanceled
927          */
928         protected void setNested(boolean nested) {
929                 isNested = nested;
930         }
931
932         /**
933          * @see IProgressMonitor
934          */
935         public void setTaskName(String name) {
936                 if (progressMonitor != null) {
937                         progressMonitor.setTaskName(name);
938                 }
939         }
940
941         /**
942          * @see IProgressMonitor
943          */
944         public void subTask(String name) {
945                 if (progressMonitor != null) {
946                         progressMonitor.subTask(name);
947                 }
948         }
949
950         /**
951          * Returns a status indicating if there is any known reason this operation
952          * will fail. Operations are verified before they are run.
953          * 
954          * Subclasses must override if they have any conditions to verify before
955          * this operation executes.
956          * 
957          * @see IJavaModelStatus
958          */
959         protected IJavaModelStatus verify() {
960                 return commonVerify();
961         }
962
963         /**
964          * @see IProgressMonitor
965          */
966         public void worked(int work) {
967                 if (progressMonitor != null) {
968                         progressMonitor.worked(work);
969                         checkCanceled();
970                 }
971         }
972 }