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