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