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