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