2efde2af60a4a455131eafb0d8b71864e9516860
[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 //                        deltaProcessor.initializeRoots();
387 //                      }
388 //                      executeOperation();
389 //              } else {
390 //                      throw new JavaModelException(status);
391 //              }
392 //      }
393         
394         /**
395          * Convenience method to run an operation within this operation
396          */
397         public void executeNestedOperation(JavaModelOperation operation, int subWorkAmount) throws JavaModelException {
398                 IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
399                 // fix for 1FW7IKC, part (1)
400                 try {
401                         operation.setNested(true);
402                         operation.run(subProgressMonitor);
403                 } catch (CoreException ce) {
404                         if (ce instanceof JavaModelException) {
405                                 throw (JavaModelException)ce;
406                         } else {
407                                 // translate the core exception to a java model exception
408                                 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
409                                         Throwable e = ce.getStatus().getException();
410                                         if (e instanceof JavaModelException) {
411                                                 throw (JavaModelException) e;
412                                         }
413                                 }
414                                 throw new JavaModelException(ce);
415                         }
416                 }
417         }
418         /**
419          * Performs the operation specific behavior. Subclasses must override.
420          */
421         protected abstract void executeOperation() throws JavaModelException;
422         /*
423          * Returns the attribute registered at the given key with the top level operation.
424          * Returns null if no such attribute is found.
425          */
426         protected Object getAttribute(Object key) {
427                 ArrayList stack = this.getCurrentOperationStack();
428                 if (stack.size() == 0) return null;
429                 JavaModelOperation topLevelOp = (JavaModelOperation)stack.get(0);
430                 if (topLevelOp.attributes == null) {
431                         return null;
432                 } else {
433                         return topLevelOp.attributes.get(key);
434                 }
435         }
436         /**
437          * Returns the compilation unit the given element is contained in,
438          * or the element itself (if it is a compilation unit),
439          * otherwise <code>null</code>.
440          */
441         protected ICompilationUnit getCompilationUnitFor(IJavaElement element) {
442         
443                 return ((JavaElement)element).getCompilationUnit();
444         }
445         /*
446          * Returns the stack of operations running in the current thread.
447          * Returns an empty stack if no operations are currently running in this thread. 
448          */
449         protected ArrayList getCurrentOperationStack() {
450                 ArrayList stack = (ArrayList)operationStacks.getCurrent();
451                 if (stack == null) {
452                         stack = new ArrayList();
453                         operationStacks.setCurrent(stack);
454                 }
455                 return stack;
456         }
457         /**
458          * Returns the elements to which this operation applies,
459          * or <code>null</code> if not applicable.
460          */
461         protected IJavaElement[] getElementsToProcess() {
462                 return fElementsToProcess;
463         }
464         /**
465          * Returns the element to which this operation applies,
466          * or <code>null</code> if not applicable.
467          */
468         protected IJavaElement getElementToProcess() {
469                 if (fElementsToProcess == null || fElementsToProcess.length == 0) {
470                         return null;
471                 }
472                 return fElementsToProcess[0];
473         }
474         /**
475          * Returns the Java Model this operation is operating in.
476          */
477         public IJavaModel getJavaModel() {
478                 if (fElementsToProcess == null || fElementsToProcess.length == 0) {
479                         return getParentElement().getJavaModel();
480                 } else {
481                         return fElementsToProcess[0].getJavaModel();
482                 }
483         }
484 //      protected IPath[] getNestedFolders(IPackageFragmentRoot root) throws JavaModelException {
485 //              IPath rootPath = root.getPath();
486 //              IClasspathEntry[] classpath = root.getJavaProject().getRawClasspath();
487 //              int length = classpath.length;
488 //              IPath[] result = new IPath[length];
489 //              int index = 0;
490 //              for (int i = 0; i < length; i++) {
491 //                      IPath path = classpath[i].getPath();
492 //                      if (rootPath.isPrefixOf(path) && !rootPath.equals(path)) {
493 //                              result[index++] = path;
494 //                      }
495 //              }
496 //              if (index < length) {
497 //                      System.arraycopy(result, 0, result = new IPath[index], 0, index);
498 //              }
499 //              return result;
500 //      }
501         /**
502          * Returns the parent element to which this operation applies,
503          * or <code>null</code> if not applicable.
504          */
505         protected IJavaElement getParentElement() {
506                 if (fParentElements == null || fParentElements.length == 0) {
507                         return null;
508                 }
509                 return fParentElements[0];
510         }
511         /**
512          * Returns the parent elements to which this operation applies,
513          * or <code>null</code> if not applicable.
514          */
515         protected IJavaElement[] getParentElements() {
516                 return fParentElements;
517         }
518         /**
519          * Returns the elements created by this operation.
520          */
521         public IJavaElement[] getResultElements() {
522                 return resultElements;
523         }
524         /*
525          * Returns the scheduling rule for this operation (i.e. the resource that needs to be locked 
526          * while this operation is running.
527          * Subclasses can override.
528          */
529         protected ISchedulingRule getSchedulingRule() {
530                 return ResourcesPlugin.getWorkspace().getRoot();
531         }
532         /**
533          * Creates and returns a subprogress monitor if appropriate.
534          */
535         protected IProgressMonitor getSubProgressMonitor(int workAmount) {
536                 IProgressMonitor sub = null;
537                 if (progressMonitor != null) {
538                         sub = new SubProgressMonitor(progressMonitor, workAmount, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
539                 }
540                 return sub;
541         }
542
543         /**
544          * Returns whether this operation has performed any resource modifications.
545          * Returns false if this operation has not been executed yet.
546          */
547         public boolean hasModifiedResource() {
548                 return !this.isReadOnly() && this.getAttribute(HAS_MODIFIED_RESOURCE_ATTR) == TRUE; 
549         }
550         public void internalWorked(double work) {
551                 if (progressMonitor != null) {
552                         progressMonitor.internalWorked(work);
553                 }
554         }
555         /**
556          * @see IProgressMonitor
557          */
558         public boolean isCanceled() {
559                 if (progressMonitor != null) {
560                         return progressMonitor.isCanceled();
561                 }
562                 return false;
563         }
564         /**
565          * Returns <code>true</code> if this operation performs no resource modifications,
566          * otherwise <code>false</code>. Subclasses must override.
567          */
568         public boolean isReadOnly() {
569                 return false;
570         }
571         /*
572          * Returns whether this operation is the first operation to run in the current thread.
573          */
574         protected boolean isTopLevelOperation() {
575                 ArrayList stack;
576                 return 
577                         (stack = this.getCurrentOperationStack()).size() > 0
578                         && stack.get(0) == this;
579         }
580         /*
581          * Returns the index of the first registered action with the given id, starting from a given position.
582          * Returns -1 if not found.
583          */
584         protected int firstActionWithID(String id, int start) {
585                 for (int i = start; i <= this.actionsEnd; i++) {
586                         if (this.actions[i].getID().equals(id)) {
587                                 return i;
588                         }
589                 }
590                 return -1;
591         }
592         
593         /**
594          * Convenience method to move resources
595          */
596         protected void moveResources(IResource[] resources, IPath destinationPath) throws JavaModelException {
597                 IProgressMonitor subProgressMonitor = null;
598                 if (progressMonitor != null) {
599                         subProgressMonitor = new SubProgressMonitor(progressMonitor, resources.length, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
600                 }
601                 IWorkspace workspace = resources[0].getWorkspace();
602                 try {
603                         workspace.move(resources, destinationPath, false, subProgressMonitor);
604                         this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE); 
605                 } catch (CoreException e) {
606                         throw new JavaModelException(e);
607                 }
608         }
609         /**
610          * Creates and returns a new <code>IJavaElementDelta</code>
611          * on the Java Model.
612          */
613         public JavaElementDelta newJavaElementDelta() {
614                 return new JavaElementDelta(getJavaModel());
615         }
616         /*
617          * Removes the last pushed operation from the stack of running operations.
618          * Returns the poped operation or null if the stack was empty.
619          */
620         protected JavaModelOperation popOperation() {
621                 ArrayList stack = getCurrentOperationStack();
622                 int size = stack.size();
623                 if (size > 0) {
624                         if (size == 1) { // top level operation 
625                                 operationStacks.setCurrent(null); // release reference (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=33927)
626                         }
627                         return (JavaModelOperation)stack.remove(size-1);
628                 } else {
629                         return null;
630                 }
631         }
632         /*
633          * Registers the given action to be run when the outer most java model operation has finished.
634          * The insertion mode controls whether:
635          * - the action should discard all existing actions with the same id, and be queued at the end (REMOVEALL_APPEND),
636          * - the action should be ignored if there is already an action with the same id (KEEP_EXISTING),
637          * - the action should be queued at the end without looking at existing actions (APPEND)
638          */
639         protected void postAction(IPostAction action, int insertionMode) {
640                 if (POST_ACTION_VERBOSE) {
641                         System.out.print("(" + Thread.currentThread() + ") [JavaModelOperation.postAction(IPostAction, int)] Posting action " + action.getID()); //$NON-NLS-1$ //$NON-NLS-2$
642                         switch(insertionMode) {
643                                 case REMOVEALL_APPEND:
644                                         System.out.println(" (REMOVEALL_APPEND)"); //$NON-NLS-1$
645                                         break;
646                                 case KEEP_EXISTING:
647                                         System.out.println(" (KEEP_EXISTING)"); //$NON-NLS-1$
648                                         break;
649                                 case APPEND:
650                                         System.out.println(" (APPEND)"); //$NON-NLS-1$
651                                         break;
652                         }
653                 }
654                 
655                 JavaModelOperation topLevelOp = (JavaModelOperation)getCurrentOperationStack().get(0);
656                 IPostAction[] postActions = topLevelOp.actions;
657                 if (postActions == null) {
658                         topLevelOp.actions = postActions = new IPostAction[1];
659                         postActions[0] = action;
660                         topLevelOp.actionsEnd = 0;
661                 } else {
662                         String id = action.getID();
663                         switch (insertionMode) {
664                                 case REMOVEALL_APPEND :
665                                         int index = this.actionsStart-1;
666                                         while ((index = topLevelOp.firstActionWithID(id, index+1)) >= 0) {
667                                                 // remove action[index]
668                                                 System.arraycopy(postActions, index+1, postActions, index, topLevelOp.actionsEnd - index);
669                                                 postActions[topLevelOp.actionsEnd--] = null;
670                                         }
671                                         topLevelOp.addAction(action);
672                                         break;
673                                 case KEEP_EXISTING:
674                                         if (topLevelOp.firstActionWithID(id, 0) < 0) {
675                                                 topLevelOp.addAction(action);
676                                         }
677                                         break;
678                                 case APPEND:
679                                         topLevelOp.addAction(action);
680                                         break;
681                         }
682                 }
683         }
684         /*
685          * Returns whether the given path is the prefix of one of the given other paths.
686          */
687         protected boolean prefixesOneOf(IPath path, IPath[] otherPaths) {
688                 for (int i = 0, length = otherPaths.length; i < length; i++) {
689                         if (path.isPrefixOf(otherPaths[i])) {
690                                 return true;
691                         }
692                 }
693                 return false;
694         }       
695         /*
696          * Pushes the given operation on the stack of operations currently running in this thread.
697          */
698         protected void pushOperation(JavaModelOperation operation) {
699                 getCurrentOperationStack().add(operation);
700         }
701         
702         /**
703          * Main entry point for Java Model operations.  Executes this operation
704          * and registers any deltas created.
705          *
706          * @see IWorkspaceRunnable
707          * @exception CoreException if the operation fails
708          */
709         public void run(IProgressMonitor monitor) throws CoreException {
710                 JavaModelManager manager = JavaModelManager.getJavaModelManager();
711                 DeltaProcessor deltaProcessor = manager.getDeltaProcessor();
712                 int previousDeltaCount = deltaProcessor.javaModelDeltas.size();
713                 try {
714                         progressMonitor = monitor;
715                         pushOperation(this);
716                         try {
717                                 // computes the root infos before executing the operation
718                                 // noop if aready initialized
719                                 JavaModelManager.getJavaModelManager().deltaState.initializeRoots();
720                                 
721                                 executeOperation();
722                         } finally {
723                                 if (this.isTopLevelOperation()) {
724                                         this.runPostActions();
725                                 }
726                         }
727                 } finally {
728
729                         try {
730 //                              TODO jsurfer temp-del
731                                 // update JavaModel using deltas that were recorded during this operation
732 //                              for (int i = previousDeltaCount, size = manager.javaModelDeltas.size(); i < size; i++) {
733 //                                      manager.updateJavaModel((IJavaElementDelta)manager.javaModelDeltas.get(i));
734 //                              }
735                                 
736                                 // fire only iff:
737                                 // - the operation is a top level operation
738                                 // - the operation did produce some delta(s)
739                                 // - but the operation has not modified any resource
740                                 if (this.isTopLevelOperation()) {
741                                         if ((manager.javaModelDeltas.size() > previousDeltaCount || !manager.reconcileDeltas.isEmpty()) 
742                                                         && !this.hasModifiedResource()) {
743                                                 manager.fire(null, JavaModelManager.DEFAULT_CHANGE_EVENT);
744                                         } // else deltas are fired while processing the resource delta
745                                 }
746                         } finally {
747                                 popOperation();
748                         } 
749                 }
750         }
751         /**
752          * Main entry point for Java Model operations. Runs a Java Model Operation as an IWorkspaceRunnable
753          * if not read-only.
754          */
755         public void runOperation(IProgressMonitor monitor) throws JavaModelException {
756                 IJavaModelStatus status= verify();
757                 if (!status.isOK()) {
758                         throw new JavaModelException(status);
759                 }
760                 try {
761                         if (isReadOnly()) {
762                                 run(monitor);
763                         } else {
764                                 // Use IWorkspace.run(...) to ensure that a build will be done in autobuild mode.
765                                 // Note that if the tree is locked, this will throw a CoreException, but this is ok
766                                 // as this operation is modifying the tree (not read-only) and a CoreException will be thrown anyway.
767                 ResourcesPlugin.getWorkspace().run(this, getSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
768                         }
769                 } catch (CoreException ce) {
770                         if (ce instanceof JavaModelException) {
771                                 throw (JavaModelException)ce;
772                         } else {
773                                 if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
774                                         Throwable e= ce.getStatus().getException();
775                                         if (e instanceof JavaModelException) {
776                                                 throw (JavaModelException) e;
777                                         }
778                                 }
779                                 throw new JavaModelException(ce);
780                         }
781                 }
782         }
783         protected void runPostActions() throws JavaModelException {
784                 while (this.actionsStart <= this.actionsEnd) {
785                         IPostAction postAction = this.actions[this.actionsStart++];
786                         if (POST_ACTION_VERBOSE) {
787                                 System.out.println("(" + Thread.currentThread() + ") [JavaModelOperation.runPostActions()] Running action " + postAction.getID()); //$NON-NLS-1$ //$NON-NLS-2$
788                         }
789                         postAction.run();
790                 }
791         }
792         /*
793          * Registers the given attribute at the given key with the top level operation.
794          */
795         protected void setAttribute(Object key, Object attribute) {
796                 JavaModelOperation topLevelOp = (JavaModelOperation)this.getCurrentOperationStack().get(0);
797                 if (topLevelOp.attributes == null) {
798                         topLevelOp.attributes = new HashMap();
799                 }
800                 topLevelOp.attributes.put(key, attribute);
801         }
802         /**
803          * @see IProgressMonitor
804          */
805         public void setCanceled(boolean b) {
806                 if (progressMonitor != null) {
807                         progressMonitor.setCanceled(b);
808                 }
809         }
810         /**
811          * Sets whether this operation is nested or not.
812          * @see CreateElementInCUOperation#checkCanceled
813          */
814         protected void setNested(boolean nested) {
815                 isNested = nested;
816         }
817         /**
818          * @see IProgressMonitor
819          */
820         public void setTaskName(String name) {
821                 if (progressMonitor != null) {
822                         progressMonitor.setTaskName(name);
823                 }
824         }
825         /**
826          * @see IProgressMonitor
827          */
828         public void subTask(String name) {
829                 if (progressMonitor != null) {
830                         progressMonitor.subTask(name);
831                 }
832         }
833         /**
834          * Returns a status indicating if there is any known reason
835          * this operation will fail.  Operations are verified before they
836          * are run.
837          *
838          * Subclasses must override if they have any conditions to verify
839          * before this operation executes.
840          *
841          * @see IJavaModelStatus
842          */
843         protected IJavaModelStatus verify() {
844                 return commonVerify();
845         }
846         
847         /**
848          * @see IProgressMonitor
849          */
850         public void worked(int work) {
851                 if (progressMonitor != null) {
852                         progressMonitor.worked(work);
853                         checkCanceled();
854                 }
855         }
856 }