A massive organize imports and formatting of the sources using default Eclipse code...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaModelOperation.java
index 752990c..89a9a40 100644 (file)
@@ -45,845 +45,928 @@ import org.eclipse.core.runtime.jobs.ISchedulingRule;
 /**
  * Defines behavior common to all Java Model operations
  */
-public abstract class JavaModelOperation implements IWorkspaceRunnable, IProgressMonitor {
-  protected interface IPostAction {
-    /*
-     * Returns the id of this action.
-     * 
-     * @see JavaModelOperation#postAction
-     */
-    String getID();
-
-    /*
-     * Run this action.
-     */
-    void run() throws JavaModelException;
-  }
-
-  /*
-   * Constants controlling the insertion mode of an action.
-   * 
-   * @see JavaModelOperation#postAction
-   */
-  protected static final int APPEND = 1; // insert at the end
-
-  protected static final int REMOVEALL_APPEND = 2; // remove all existing ones with same ID, and add new one at the end
-
-  protected static final int KEEP_EXISTING = 3; // do not insert if already existing with same ID
-
-  /*
-   * Whether tracing post actions is enabled.
-   */
-  protected static boolean POST_ACTION_VERBOSE;
-
-  /*
-   * A list of IPostActions.
-   */
-  protected IPostAction[] actions;
-
-  protected int actionsStart = 0;
-
-  protected int actionsEnd = -1;
-
-  /*
-   * A HashMap of attributes that can be used by operations
-   */
-  protected HashMap attributes;
-
-  public static final String HAS_MODIFIED_RESOURCE_ATTR = "hasModifiedResource"; //$NON-NLS-1$
-
-  public static final String TRUE = "true"; //$NON-NLS-1$
-
-  //public static final String FALSE = "false"; //$NON-NLS-1$
-
-  /**
-   * The elements this operation operates on, or <code>null</code> if this operation does not operate on specific elements.
-   */
-  protected IJavaElement[] fElementsToProcess;
-
-  /**
-   * The parent elements this operation operates with or <code>null</code> if this operation does not operate with specific parent
-   * elements.
-   */
-  protected IJavaElement[] fParentElements;
-
-  /**
-   * An empty collection of <code>IJavaElement</code> s - the common empty result if no elements are created, or if this operation
-   * is not actually executed.
-   */
-  protected static IJavaElement[] NO_ELEMENTS = new IJavaElement[] {};
-
-  /**
-   * The elements created by this operation - empty until the operation actually creates elements.
-   */
-  protected IJavaElement[] resultElements = NO_ELEMENTS;
-
-  /**
-   * The progress monitor passed into this operation
-   */
-  protected IProgressMonitor progressMonitor = null;
-
-  /**
-   * A flag indicating whether this operation is nested.
-   */
-  protected boolean isNested = false;
-
-  /**
-   * Conflict resolution policy - by default do not force (fail on a conflict).
-   */
-  protected boolean force = false;
-
-  /*
-   * A per thread stack of java model operations (PerThreadObject of ArrayList).
-   */
-  protected static PerThreadObject operationStacks = new PerThreadObject();
-
-  protected JavaModelOperation() {
-  }
-
-  /**
-   * A common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement[] elements) {
-    fElementsToProcess = elements;
-  }
-
-  /**
-   * Common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements) {
-    fElementsToProcess = elementsToProcess;
-    fParentElements = parentElements;
-  }
-
-  /**
-   * A common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements, boolean force) {
-    fElementsToProcess = elementsToProcess;
-    fParentElements = parentElements;
-    this.force = force;
-  }
-
-  /**
-   * A common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement[] elements, boolean force) {
-    fElementsToProcess = elements;
-    this.force = force;
-  }
-
-  /**
-   * Common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement element) {
-    fElementsToProcess = new IJavaElement[] { element };
-  }
-
-  /**
-   * A common constructor for all Java Model operations.
-   */
-  protected JavaModelOperation(IJavaElement element, boolean force) {
-    fElementsToProcess = new IJavaElement[] { element };
-    this.force = force;
-  }
-
-  /*
-   * Registers the given action at the end of the list of actions to run.
-   */
-  protected void addAction(IPostAction action) {
-    int length = this.actions.length;
-    if (length == ++this.actionsEnd) {
-      System.arraycopy(this.actions, 0, this.actions = new IPostAction[length * 2], 0, length);
-    }
-    this.actions[this.actionsEnd] = action;
-  }
-
-  /*
-   * Registers the given delta with the Java Model Manager.
-   */
-  protected void addDelta(IJavaElementDelta delta) {
-    JavaModelManager.getJavaModelManager().registerJavaModelDelta(delta);
-  }
-
-  /*
-   * Registers the given reconcile delta with the Java Model Manager.
-   */
-  protected void addReconcileDelta(IWorkingCopy workingCopy, IJavaElementDelta delta) {
-    HashMap reconcileDeltas = JavaModelManager.getJavaModelManager().reconcileDeltas;
-    JavaElementDelta previousDelta = (JavaElementDelta) reconcileDeltas.get(workingCopy);
-    if (previousDelta != null) {
-      IJavaElementDelta[] children = delta.getAffectedChildren();
-      for (int i = 0, length = children.length; i < length; i++) {
-        JavaElementDelta child = (JavaElementDelta) children[i];
-        previousDelta.insertDeltaTree(child.getElement(), child);
-      }
-    } else {
-      reconcileDeltas.put(workingCopy, delta);
-    }
-  }
-
-  /*
-   * Deregister the reconcile delta for the given working copy
-   */
-  protected void removeReconcileDelta(IWorkingCopy workingCopy) {
-    JavaModelManager.getJavaModelManager().reconcileDeltas.remove(workingCopy);
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void beginTask(String name, int totalWork) {
-    if (progressMonitor != null) {
-      progressMonitor.beginTask(name, totalWork);
-    }
-  }
-
-  /**
-   * Checks with the progress monitor to see whether this operation should be canceled. An operation should regularly call this
-   * method during its operation so that the user can cancel it.
-   * 
-   * @exception OperationCanceledException
-   *              if cancelling the operation has been requested
-   * @see IProgressMonitor#isCanceled
-   */
-  protected void checkCanceled() {
-    if (isCanceled()) {
-      throw new OperationCanceledException(Util.bind("operation.cancelled")); //$NON-NLS-1$
-    }
-  }
-
-  /**
-   * Common code used to verify the elements this operation is processing.
-   * 
-   * @see JavaModelOperation#verify()
-   */
-  protected IJavaModelStatus commonVerify() {
-    if (fElementsToProcess == null || fElementsToProcess.length == 0) {
-      return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
-    }
-    for (int i = 0; i < fElementsToProcess.length; i++) {
-      if (fElementsToProcess[i] == null) {
-        return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
-      }
-    }
-    return JavaModelStatus.VERIFIED_OK;
-  }
-
-  /**
-   * Convenience method to copy resources
-   */
-  protected void copyResources(IResource[] resources, IPath destinationPath) throws JavaModelException {
-    IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
-    IWorkspace workspace = resources[0].getWorkspace();
-    try {
-      workspace.copy(resources, destinationPath, false, subProgressMonitor);
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Convenience method to create a file
-   */
-  protected void createFile(IContainer folder, String name, InputStream contents, boolean force) throws JavaModelException {
-    IFile file = folder.getFile(new Path(name));
-    try {
-      file.create(contents, force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, getSubProgressMonitor(1));
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Convenience method to create a folder
-   */
-  protected void createFolder(IContainer parentFolder, String name, boolean force) throws JavaModelException {
-    IFolder folder = parentFolder.getFolder(new Path(name));
-    try {
-      // we should use true to create the file locally. Only VCM should use tru/false
-      folder.create(force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, true, // local
-          getSubProgressMonitor(1));
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Convenience method to delete an empty package fragment
-   */
-  protected void deleteEmptyPackageFragment(IPackageFragment fragment, boolean force, IResource rootResource)
-      throws JavaModelException {
-
-    IContainer resource = (IContainer) fragment.getResource();
-
-    try {
-      resource.delete(force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, getSubProgressMonitor(1));
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-      while (resource instanceof IFolder) {
-        // deleting a package: delete the parent if it is empty (eg. deleting x.y where folder x doesn't have resources but y)
-        // without deleting the package fragment root
-        resource = resource.getParent();
-        if (!resource.equals(rootResource) && resource.members().length == 0) {
-          resource.delete(force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, getSubProgressMonitor(1));
-          this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-        }
-      }
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Convenience method to delete a resource
-   */
-  protected void deleteResource(IResource resource, int flags) throws JavaModelException {
-    try {
-      resource.delete(flags, getSubProgressMonitor(1));
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Convenience method to delete resources
-   */
-  protected void deleteResources(IResource[] resources, boolean force) throws JavaModelException {
-    if (resources == null || resources.length == 0)
-      return;
-    IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
-    IWorkspace workspace = resources[0].getWorkspace();
-    try {
-      workspace.delete(resources, force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, subProgressMonitor);
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void done() {
-    if (progressMonitor != null) {
-      progressMonitor.done();
-    }
-  }
-
-  /*
-   * Returns whether the given path is equals to one of the given other paths.
-   */
-  protected boolean equalsOneOf(IPath path, IPath[] otherPaths) {
-    for (int i = 0, length = otherPaths.length; i < length; i++) {
-      if (path.equals(otherPaths[i])) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Verifies the operation can proceed and executes the operation. Subclasses should override <code>#verify</code> and
-   * <code>executeOperation</code> to implement the specific operation behavior.
-   * 
-   * @exception JavaModelException
-   *              The operation has failed.
-   */
-  //   protected void execute() throws JavaModelException {
-  //           IJavaModelStatus status= verify();
-  //           if (status.isOK()) {
-  //                   // if first time here, computes the root infos before executing the operation
-  //                   DeltaProcessor deltaProcessor = JavaModelManager.getJavaModelManager().deltaProcessor;
-  //                   if (deltaProcessor.roots == null) {
-  //                     deltaProcessor.initializeRoots();
-  //                   }
-  //                   executeOperation();
-  //           } else {
-  //                   throw new JavaModelException(status);
-  //           }
-  //   }
-  /**
-   * Convenience method to run an operation within this operation
-   */
-  public void executeNestedOperation(JavaModelOperation operation, int subWorkAmount) throws JavaModelException {
-    IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
-    // fix for 1FW7IKC, part (1)
-    try {
-      operation.setNested(true);
-      operation.run(subProgressMonitor);
-    } catch (CoreException ce) {
-      if (ce instanceof JavaModelException) {
-        throw (JavaModelException) ce;
-      } else {
-        // translate the core exception to a java model exception
-        if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
-          Throwable e = ce.getStatus().getException();
-          if (e instanceof JavaModelException) {
-            throw (JavaModelException) e;
-          }
-        }
-        throw new JavaModelException(ce);
-      }
-    }
-  }
-
-  /**
-   * Performs the operation specific behavior. Subclasses must override.
-   */
-  protected abstract void executeOperation() throws JavaModelException;
-
-  /*
-   * Returns the attribute registered at the given key with the top level operation. Returns null if no such attribute is found.
-   */
-  protected Object getAttribute(Object key) {
-    ArrayList stack = this.getCurrentOperationStack();
-    if (stack.size() == 0)
-      return null;
-    JavaModelOperation topLevelOp = (JavaModelOperation) stack.get(0);
-    if (topLevelOp.attributes == null) {
-      return null;
-    } else {
-      return topLevelOp.attributes.get(key);
-    }
-  }
-
-  /**
-   * Returns the compilation unit the given element is contained in, or the element itself (if it is a compilation unit), otherwise
-   * <code>null</code>.
-   */
-  protected ICompilationUnit getCompilationUnitFor(IJavaElement element) {
-
-    return ((JavaElement) element).getCompilationUnit();
-  }
-
-  /*
-   * Returns the stack of operations running in the current thread. Returns an empty stack if no operations are currently running in
-   * this thread.
-   */
-  protected ArrayList getCurrentOperationStack() {
-    ArrayList stack = (ArrayList) operationStacks.getCurrent();
-    if (stack == null) {
-      stack = new ArrayList();
-      operationStacks.setCurrent(stack);
-    }
-    return stack;
-  }
-
-  /**
-   * Returns the elements to which this operation applies, or <code>null</code> if not applicable.
-   */
-  protected IJavaElement[] getElementsToProcess() {
-    return fElementsToProcess;
-  }
-
-  /**
-   * Returns the element to which this operation applies, or <code>null</code> if not applicable.
-   */
-  protected IJavaElement getElementToProcess() {
-    if (fElementsToProcess == null || fElementsToProcess.length == 0) {
-      return null;
-    }
-    return fElementsToProcess[0];
-  }
-
-  /**
-   * Returns the Java Model this operation is operating in.
-   */
-  public IJavaModel getJavaModel() {
-    if (fElementsToProcess == null || fElementsToProcess.length == 0) {
-      return getParentElement().getJavaModel();
-    } else {
-      return fElementsToProcess[0].getJavaModel();
-    }
-  }
-
-  //   protected IPath[] getNestedFolders(IPackageFragmentRoot root) throws JavaModelException {
-  //           IPath rootPath = root.getPath();
-  //           IClasspathEntry[] classpath = root.getJavaProject().getRawClasspath();
-  //           int length = classpath.length;
-  //           IPath[] result = new IPath[length];
-  //           int index = 0;
-  //           for (int i = 0; i < length; i++) {
-  //                   IPath path = classpath[i].getPath();
-  //                   if (rootPath.isPrefixOf(path) && !rootPath.equals(path)) {
-  //                           result[index++] = path;
-  //                   }
-  //           }
-  //           if (index < length) {
-  //                   System.arraycopy(result, 0, result = new IPath[index], 0, index);
-  //           }
-  //           return result;
-  //   }
-  /**
-   * Returns the parent element to which this operation applies, or <code>null</code> if not applicable.
-   */
-  protected IJavaElement getParentElement() {
-    if (fParentElements == null || fParentElements.length == 0) {
-      return null;
-    }
-    return fParentElements[0];
-  }
-
-  /**
-   * Returns the parent elements to which this operation applies, or <code>null</code> if not applicable.
-   */
-  protected IJavaElement[] getParentElements() {
-    return fParentElements;
-  }
-
-  /**
-   * Returns the elements created by this operation.
-   */
-  public IJavaElement[] getResultElements() {
-    return resultElements;
-  }
-
-  /*
-   * Returns the scheduling rule for this operation (i.e. the resource that needs to be locked while this operation is running.
-   * Subclasses can override.
-   */
-  protected ISchedulingRule getSchedulingRule() {
-    return ResourcesPlugin.getWorkspace().getRoot();
-  }
-
-  /**
-   * Creates and returns a subprogress monitor if appropriate.
-   */
-  protected IProgressMonitor getSubProgressMonitor(int workAmount) {
-    IProgressMonitor sub = null;
-    if (progressMonitor != null) {
-      sub = new SubProgressMonitor(progressMonitor, workAmount, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
-    }
-    return sub;
-  }
-
-  /**
-   * Returns whether this operation has performed any resource modifications. Returns false if this operation has not been executed
-   * yet.
-   */
-  public boolean hasModifiedResource() {
-    return !this.isReadOnly() && this.getAttribute(HAS_MODIFIED_RESOURCE_ATTR) == TRUE;
-  }
-
-  public void internalWorked(double work) {
-    if (progressMonitor != null) {
-      progressMonitor.internalWorked(work);
-    }
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public boolean isCanceled() {
-    if (progressMonitor != null) {
-      return progressMonitor.isCanceled();
-    }
-    return false;
-  }
-
-  /**
-   * Returns <code>true</code> if this operation performs no resource modifications, otherwise <code>false</code>. Subclasses
-   * must override.
-   */
-  public boolean isReadOnly() {
-    return false;
-  }
-
-  /*
-   * Returns whether this operation is the first operation to run in the current thread.
-   */
-  protected boolean isTopLevelOperation() {
-    ArrayList stack;
-    return (stack = this.getCurrentOperationStack()).size() > 0 && stack.get(0) == this;
-  }
-
-  /*
-   * Returns the index of the first registered action with the given id, starting from a given position. Returns -1 if not found.
-   */
-  protected int firstActionWithID(String id, int start) {
-    for (int i = start; i <= this.actionsEnd; i++) {
-      if (this.actions[i].getID().equals(id)) {
-        return i;
-      }
-    }
-    return -1;
-  }
-
-  /**
-   * Convenience method to move resources
-   */
-  protected void moveResources(IResource[] resources, IPath destinationPath) throws JavaModelException {
-    IProgressMonitor subProgressMonitor = null;
-    if (progressMonitor != null) {
-      subProgressMonitor = new SubProgressMonitor(progressMonitor, resources.length,
-          SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
-    }
-    IWorkspace workspace = resources[0].getWorkspace();
-    try {
-      workspace.move(resources, destinationPath, false, subProgressMonitor);
-      this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
-    } catch (CoreException e) {
-      throw new JavaModelException(e);
-    }
-  }
-
-  /**
-   * Creates and returns a new <code>IJavaElementDelta</code> on the Java Model.
-   */
-  public JavaElementDelta newJavaElementDelta() {
-    return new JavaElementDelta(getJavaModel());
-  }
-
-  /*
-   * Removes the last pushed operation from the stack of running operations. Returns the poped operation or null if the stack was
-   * empty.
-   */
-  protected JavaModelOperation popOperation() {
-    ArrayList stack = getCurrentOperationStack();
-    int size = stack.size();
-    if (size > 0) {
-      if (size == 1) { // top level operation
-        operationStacks.setCurrent(null); // release reference (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=33927)
-      }
-      return (JavaModelOperation) stack.remove(size - 1);
-    } else {
-      return null;
-    }
-  }
-
-  /*
-   * Registers the given action to be run when the outer most java model operation has finished. The insertion mode controls
-   * whether: - the action should discard all existing actions with the same id, and be queued at the end (REMOVEALL_APPEND), - the
-   * action should be ignored if there is already an action with the same id (KEEP_EXISTING), - the action should be queued at the
-   * end without looking at existing actions (APPEND)
-   */
-  protected void postAction(IPostAction action, int insertionMode) {
-    if (POST_ACTION_VERBOSE) {
-      System.out
-          .print("(" + Thread.currentThread() + ") [JavaModelOperation.postAction(IPostAction, int)] Posting action " + action.getID()); //$NON-NLS-1$ //$NON-NLS-2$
-      switch (insertionMode) {
-      case REMOVEALL_APPEND:
-        System.out.println(" (REMOVEALL_APPEND)"); //$NON-NLS-1$
-        break;
-      case KEEP_EXISTING:
-        System.out.println(" (KEEP_EXISTING)"); //$NON-NLS-1$
-        break;
-      case APPEND:
-        System.out.println(" (APPEND)"); //$NON-NLS-1$
-        break;
-      }
-    }
-
-    JavaModelOperation topLevelOp = (JavaModelOperation) getCurrentOperationStack().get(0);
-    IPostAction[] postActions = topLevelOp.actions;
-    if (postActions == null) {
-      topLevelOp.actions = postActions = new IPostAction[1];
-      postActions[0] = action;
-      topLevelOp.actionsEnd = 0;
-    } else {
-      String id = action.getID();
-      switch (insertionMode) {
-      case REMOVEALL_APPEND:
-        int index = this.actionsStart - 1;
-        while ((index = topLevelOp.firstActionWithID(id, index + 1)) >= 0) {
-          // remove action[index]
-          System.arraycopy(postActions, index + 1, postActions, index, topLevelOp.actionsEnd - index);
-          postActions[topLevelOp.actionsEnd--] = null;
-        }
-        topLevelOp.addAction(action);
-        break;
-      case KEEP_EXISTING:
-        if (topLevelOp.firstActionWithID(id, 0) < 0) {
-          topLevelOp.addAction(action);
-        }
-        break;
-      case APPEND:
-        topLevelOp.addAction(action);
-        break;
-      }
-    }
-  }
-
-  /*
-   * Returns whether the given path is the prefix of one of the given other paths.
-   */
-  protected boolean prefixesOneOf(IPath path, IPath[] otherPaths) {
-    for (int i = 0, length = otherPaths.length; i < length; i++) {
-      if (path.isPrefixOf(otherPaths[i])) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /*
-   * Pushes the given operation on the stack of operations currently running in this thread.
-   */
-  protected void pushOperation(JavaModelOperation operation) {
-    getCurrentOperationStack().add(operation);
-  }
-
-  /**
-   * Main entry point for Java Model operations. Executes this operation and registers any deltas created.
-   * 
-   * @see IWorkspaceRunnable
-   * @exception CoreException
-   *              if the operation fails
-   */
-  public void run(IProgressMonitor monitor) throws CoreException {
-    JavaModelManager manager = JavaModelManager.getJavaModelManager();
-    DeltaProcessor deltaProcessor = manager.getDeltaProcessor();
-    int previousDeltaCount = deltaProcessor.javaModelDeltas.size();
-    try {
-      progressMonitor = monitor;
-      pushOperation(this);
-      try {
-        // computes the root infos before executing the operation
-        // noop if aready initialized
-        JavaModelManager.getJavaModelManager().deltaState.initializeRoots();
-
-        executeOperation();
-      } finally {
-        if (this.isTopLevelOperation()) {
-          this.runPostActions();
-        }
-      }
-    } finally {
-
-      try {
-        // update JavaModel using deltas that were recorded during this operation
-         for (int i = previousDeltaCount, size = manager.javaModelDeltas.size(); i < size; i++) {
-           manager.updateJavaModel((IJavaElementDelta)manager.javaModelDeltas.get(i));
-         }
-
-        // fire only iff:
-        // - the operation is a top level operation
-        // - the operation did produce some delta(s)
-        // - but the operation has not modified any resource
-        if (this.isTopLevelOperation()) {
-          if ((manager.javaModelDeltas.size() > previousDeltaCount || !manager.reconcileDeltas.isEmpty())
-              && !this.hasModifiedResource()) {
-            manager.fire(null, JavaModelManager.DEFAULT_CHANGE_EVENT);
-          } // else deltas are fired while processing the resource delta
-        }
-      } finally {
-        popOperation();
-      }
-    }
-  }
-
-  /**
-   * Main entry point for Java Model operations. Runs a Java Model Operation as an IWorkspaceRunnable if not read-only.
-   */
-  public void runOperation(IProgressMonitor monitor) throws JavaModelException {
-    IJavaModelStatus status = verify();
-    if (!status.isOK()) {
-      throw new JavaModelException(status);
-    }
-    try {
-      if (isReadOnly()) {
-        run(monitor);
-      } else {
-        // Use IWorkspace.run(...) to ensure that a build will be done in autobuild mode.
-        // Note that if the tree is locked, this will throw a CoreException, but this is ok
-        // as this operation is modifying the tree (not read-only) and a CoreException will be thrown anyway.
-        ResourcesPlugin.getWorkspace().run(this, getSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
-      }
-    } catch (CoreException ce) {
-      if (ce instanceof JavaModelException) {
-        throw (JavaModelException) ce;
-      } else {
-        if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
-          Throwable e = ce.getStatus().getException();
-          if (e instanceof JavaModelException) {
-            throw (JavaModelException) e;
-          }
-        }
-        throw new JavaModelException(ce);
-      }
-    }
-  }
-
-  protected void runPostActions() throws JavaModelException {
-    while (this.actionsStart <= this.actionsEnd) {
-      IPostAction postAction = this.actions[this.actionsStart++];
-      if (POST_ACTION_VERBOSE) {
-        System.out
-            .println("(" + Thread.currentThread() + ") [JavaModelOperation.runPostActions()] Running action " + postAction.getID()); //$NON-NLS-1$ //$NON-NLS-2$
-      }
-      postAction.run();
-    }
-  }
-
-  /*
-   * Registers the given attribute at the given key with the top level operation.
-   */
-  protected void setAttribute(Object key, Object attribute) {
-    JavaModelOperation topLevelOp = (JavaModelOperation) this.getCurrentOperationStack().get(0);
-    if (topLevelOp.attributes == null) {
-      topLevelOp.attributes = new HashMap();
-    }
-    topLevelOp.attributes.put(key, attribute);
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void setCanceled(boolean b) {
-    if (progressMonitor != null) {
-      progressMonitor.setCanceled(b);
-    }
-  }
-
-  /**
-   * Sets whether this operation is nested or not.
-   * 
-   * @see CreateElementInCUOperation#checkCanceled
-   */
-  protected void setNested(boolean nested) {
-    isNested = nested;
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void setTaskName(String name) {
-    if (progressMonitor != null) {
-      progressMonitor.setTaskName(name);
-    }
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void subTask(String name) {
-    if (progressMonitor != null) {
-      progressMonitor.subTask(name);
-    }
-  }
-
-  /**
-   * Returns a status indicating if there is any known reason this operation will fail. Operations are verified before they are run.
-   * 
-   * Subclasses must override if they have any conditions to verify before this operation executes.
-   * 
-   * @see IJavaModelStatus
-   */
-  protected IJavaModelStatus verify() {
-    return commonVerify();
-  }
-
-  /**
-   * @see IProgressMonitor
-   */
-  public void worked(int work) {
-    if (progressMonitor != null) {
-      progressMonitor.worked(work);
-      checkCanceled();
-    }
-  }
+public abstract class JavaModelOperation implements IWorkspaceRunnable,
+               IProgressMonitor {
+       protected interface IPostAction {
+               /*
+                * Returns the id of this action.
+                * 
+                * @see JavaModelOperation#postAction
+                */
+               String getID();
+
+               /*
+                * Run this action.
+                */
+               void run() throws JavaModelException;
+       }
+
+       /*
+        * Constants controlling the insertion mode of an action.
+        * 
+        * @see JavaModelOperation#postAction
+        */
+       protected static final int APPEND = 1; // insert at the end
+
+       protected static final int REMOVEALL_APPEND = 2; // remove all existing
+                                                                                                               // ones with same ID,
+                                                                                                               // and add new one at
+                                                                                                               // the end
+
+       protected static final int KEEP_EXISTING = 3; // do not insert if already
+                                                                                                       // existing with same ID
+
+       /*
+        * Whether tracing post actions is enabled.
+        */
+       protected static boolean POST_ACTION_VERBOSE;
+
+       /*
+        * A list of IPostActions.
+        */
+       protected IPostAction[] actions;
+
+       protected int actionsStart = 0;
+
+       protected int actionsEnd = -1;
+
+       /*
+        * A HashMap of attributes that can be used by operations
+        */
+       protected HashMap attributes;
+
+       public static final String HAS_MODIFIED_RESOURCE_ATTR = "hasModifiedResource"; //$NON-NLS-1$
+
+       public static final String TRUE = "true"; //$NON-NLS-1$
+
+       // public static final String FALSE = "false"; //$NON-NLS-1$
+
+       /**
+        * The elements this operation operates on, or <code>null</code> if this
+        * operation does not operate on specific elements.
+        */
+       protected IJavaElement[] fElementsToProcess;
+
+       /**
+        * The parent elements this operation operates with or <code>null</code>
+        * if this operation does not operate with specific parent elements.
+        */
+       protected IJavaElement[] fParentElements;
+
+       /**
+        * An empty collection of <code>IJavaElement</code> s - the common empty
+        * result if no elements are created, or if this operation is not actually
+        * executed.
+        */
+       protected static IJavaElement[] NO_ELEMENTS = new IJavaElement[] {};
+
+       /**
+        * The elements created by this operation - empty until the operation
+        * actually creates elements.
+        */
+       protected IJavaElement[] resultElements = NO_ELEMENTS;
+
+       /**
+        * The progress monitor passed into this operation
+        */
+       protected IProgressMonitor progressMonitor = null;
+
+       /**
+        * A flag indicating whether this operation is nested.
+        */
+       protected boolean isNested = false;
+
+       /**
+        * Conflict resolution policy - by default do not force (fail on a
+        * conflict).
+        */
+       protected boolean force = false;
+
+       /*
+        * A per thread stack of java model operations (PerThreadObject of
+        * ArrayList).
+        */
+       protected static PerThreadObject operationStacks = new PerThreadObject();
+
+       protected JavaModelOperation() {
+       }
+
+       /**
+        * A common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement[] elements) {
+               fElementsToProcess = elements;
+       }
+
+       /**
+        * Common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement[] elementsToProcess,
+                       IJavaElement[] parentElements) {
+               fElementsToProcess = elementsToProcess;
+               fParentElements = parentElements;
+       }
+
+       /**
+        * A common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement[] elementsToProcess,
+                       IJavaElement[] parentElements, boolean force) {
+               fElementsToProcess = elementsToProcess;
+               fParentElements = parentElements;
+               this.force = force;
+       }
+
+       /**
+        * A common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement[] elements, boolean force) {
+               fElementsToProcess = elements;
+               this.force = force;
+       }
+
+       /**
+        * Common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement element) {
+               fElementsToProcess = new IJavaElement[] { element };
+       }
+
+       /**
+        * A common constructor for all Java Model operations.
+        */
+       protected JavaModelOperation(IJavaElement element, boolean force) {
+               fElementsToProcess = new IJavaElement[] { element };
+               this.force = force;
+       }
+
+       /*
+        * Registers the given action at the end of the list of actions to run.
+        */
+       protected void addAction(IPostAction action) {
+               int length = this.actions.length;
+               if (length == ++this.actionsEnd) {
+                       System.arraycopy(this.actions, 0,
+                                       this.actions = new IPostAction[length * 2], 0, length);
+               }
+               this.actions[this.actionsEnd] = action;
+       }
+
+       /*
+        * Registers the given delta with the Java Model Manager.
+        */
+       protected void addDelta(IJavaElementDelta delta) {
+               JavaModelManager.getJavaModelManager().registerJavaModelDelta(delta);
+       }
+
+       /*
+        * Registers the given reconcile delta with the Java Model Manager.
+        */
+       protected void addReconcileDelta(IWorkingCopy workingCopy,
+                       IJavaElementDelta delta) {
+               HashMap reconcileDeltas = JavaModelManager.getJavaModelManager().reconcileDeltas;
+               JavaElementDelta previousDelta = (JavaElementDelta) reconcileDeltas
+                               .get(workingCopy);
+               if (previousDelta != null) {
+                       IJavaElementDelta[] children = delta.getAffectedChildren();
+                       for (int i = 0, length = children.length; i < length; i++) {
+                               JavaElementDelta child = (JavaElementDelta) children[i];
+                               previousDelta.insertDeltaTree(child.getElement(), child);
+                       }
+               } else {
+                       reconcileDeltas.put(workingCopy, delta);
+               }
+       }
+
+       /*
+        * Deregister the reconcile delta for the given working copy
+        */
+       protected void removeReconcileDelta(IWorkingCopy workingCopy) {
+               JavaModelManager.getJavaModelManager().reconcileDeltas
+                               .remove(workingCopy);
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void beginTask(String name, int totalWork) {
+               if (progressMonitor != null) {
+                       progressMonitor.beginTask(name, totalWork);
+               }
+       }
+
+       /**
+        * Checks with the progress monitor to see whether this operation should be
+        * canceled. An operation should regularly call this method during its
+        * operation so that the user can cancel it.
+        * 
+        * @exception OperationCanceledException
+        *                if cancelling the operation has been requested
+        * @see IProgressMonitor#isCanceled
+        */
+       protected void checkCanceled() {
+               if (isCanceled()) {
+                       throw new OperationCanceledException(Util
+                                       .bind("operation.cancelled")); //$NON-NLS-1$
+               }
+       }
+
+       /**
+        * Common code used to verify the elements this operation is processing.
+        * 
+        * @see JavaModelOperation#verify()
+        */
+       protected IJavaModelStatus commonVerify() {
+               if (fElementsToProcess == null || fElementsToProcess.length == 0) {
+                       return new JavaModelStatus(
+                                       IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
+               }
+               for (int i = 0; i < fElementsToProcess.length; i++) {
+                       if (fElementsToProcess[i] == null) {
+                               return new JavaModelStatus(
+                                               IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
+                       }
+               }
+               return JavaModelStatus.VERIFIED_OK;
+       }
+
+       /**
+        * Convenience method to copy resources
+        */
+       protected void copyResources(IResource[] resources, IPath destinationPath)
+                       throws JavaModelException {
+               IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
+               IWorkspace workspace = resources[0].getWorkspace();
+               try {
+                       workspace.copy(resources, destinationPath, false,
+                                       subProgressMonitor);
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Convenience method to create a file
+        */
+       protected void createFile(IContainer folder, String name,
+                       InputStream contents, boolean force) throws JavaModelException {
+               IFile file = folder.getFile(new Path(name));
+               try {
+                       file.create(contents, force ? IResource.FORCE
+                                       | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
+                                       getSubProgressMonitor(1));
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Convenience method to create a folder
+        */
+       protected void createFolder(IContainer parentFolder, String name,
+                       boolean force) throws JavaModelException {
+               IFolder folder = parentFolder.getFolder(new Path(name));
+               try {
+                       // we should use true to create the file locally. Only VCM should
+                       // use tru/false
+                       folder.create(force ? IResource.FORCE | IResource.KEEP_HISTORY
+                                       : IResource.KEEP_HISTORY, true, // local
+                                       getSubProgressMonitor(1));
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Convenience method to delete an empty package fragment
+        */
+       protected void deleteEmptyPackageFragment(IPackageFragment fragment,
+                       boolean force, IResource rootResource) throws JavaModelException {
+
+               IContainer resource = (IContainer) fragment.getResource();
+
+               try {
+                       resource.delete(force ? IResource.FORCE | IResource.KEEP_HISTORY
+                                       : IResource.KEEP_HISTORY, getSubProgressMonitor(1));
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+                       while (resource instanceof IFolder) {
+                               // deleting a package: delete the parent if it is empty (eg.
+                               // deleting x.y where folder x doesn't have resources but y)
+                               // without deleting the package fragment root
+                               resource = resource.getParent();
+                               if (!resource.equals(rootResource)
+                                               && resource.members().length == 0) {
+                                       resource.delete(force ? IResource.FORCE
+                                                       | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
+                                                       getSubProgressMonitor(1));
+                                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+                               }
+                       }
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Convenience method to delete a resource
+        */
+       protected void deleteResource(IResource resource, int flags)
+                       throws JavaModelException {
+               try {
+                       resource.delete(flags, getSubProgressMonitor(1));
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Convenience method to delete resources
+        */
+       protected void deleteResources(IResource[] resources, boolean force)
+                       throws JavaModelException {
+               if (resources == null || resources.length == 0)
+                       return;
+               IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
+               IWorkspace workspace = resources[0].getWorkspace();
+               try {
+                       workspace.delete(resources, force ? IResource.FORCE
+                                       | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
+                                       subProgressMonitor);
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void done() {
+               if (progressMonitor != null) {
+                       progressMonitor.done();
+               }
+       }
+
+       /*
+        * Returns whether the given path is equals to one of the given other paths.
+        */
+       protected boolean equalsOneOf(IPath path, IPath[] otherPaths) {
+               for (int i = 0, length = otherPaths.length; i < length; i++) {
+                       if (path.equals(otherPaths[i])) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Verifies the operation can proceed and executes the operation. Subclasses
+        * should override <code>#verify</code> and <code>executeOperation</code>
+        * to implement the specific operation behavior.
+        * 
+        * @exception JavaModelException
+        *                The operation has failed.
+        */
+       // protected void execute() throws JavaModelException {
+       // IJavaModelStatus status= verify();
+       // if (status.isOK()) {
+       // // if first time here, computes the root infos before executing the
+       // operation
+       // DeltaProcessor deltaProcessor =
+       // JavaModelManager.getJavaModelManager().deltaProcessor;
+       // if (deltaProcessor.roots == null) {
+       // deltaProcessor.initializeRoots();
+       // }
+       // executeOperation();
+       // } else {
+       // throw new JavaModelException(status);
+       // }
+       // }
+       /**
+        * Convenience method to run an operation within this operation
+        */
+       public void executeNestedOperation(JavaModelOperation operation,
+                       int subWorkAmount) throws JavaModelException {
+               IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
+               // fix for 1FW7IKC, part (1)
+               try {
+                       operation.setNested(true);
+                       operation.run(subProgressMonitor);
+               } catch (CoreException ce) {
+                       if (ce instanceof JavaModelException) {
+                               throw (JavaModelException) ce;
+                       } else {
+                               // translate the core exception to a java model exception
+                               if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
+                                       Throwable e = ce.getStatus().getException();
+                                       if (e instanceof JavaModelException) {
+                                               throw (JavaModelException) e;
+                                       }
+                               }
+                               throw new JavaModelException(ce);
+                       }
+               }
+       }
+
+       /**
+        * Performs the operation specific behavior. Subclasses must override.
+        */
+       protected abstract void executeOperation() throws JavaModelException;
+
+       /*
+        * Returns the attribute registered at the given key with the top level
+        * operation. Returns null if no such attribute is found.
+        */
+       protected Object getAttribute(Object key) {
+               ArrayList stack = this.getCurrentOperationStack();
+               if (stack.size() == 0)
+                       return null;
+               JavaModelOperation topLevelOp = (JavaModelOperation) stack.get(0);
+               if (topLevelOp.attributes == null) {
+                       return null;
+               } else {
+                       return topLevelOp.attributes.get(key);
+               }
+       }
+
+       /**
+        * Returns the compilation unit the given element is contained in, or the
+        * element itself (if it is a compilation unit), otherwise <code>null</code>.
+        */
+       protected ICompilationUnit getCompilationUnitFor(IJavaElement element) {
+
+               return ((JavaElement) element).getCompilationUnit();
+       }
+
+       /*
+        * Returns the stack of operations running in the current thread. Returns an
+        * empty stack if no operations are currently running in this thread.
+        */
+       protected ArrayList getCurrentOperationStack() {
+               ArrayList stack = (ArrayList) operationStacks.getCurrent();
+               if (stack == null) {
+                       stack = new ArrayList();
+                       operationStacks.setCurrent(stack);
+               }
+               return stack;
+       }
+
+       /**
+        * Returns the elements to which this operation applies, or
+        * <code>null</code> if not applicable.
+        */
+       protected IJavaElement[] getElementsToProcess() {
+               return fElementsToProcess;
+       }
+
+       /**
+        * Returns the element to which this operation applies, or <code>null</code>
+        * if not applicable.
+        */
+       protected IJavaElement getElementToProcess() {
+               if (fElementsToProcess == null || fElementsToProcess.length == 0) {
+                       return null;
+               }
+               return fElementsToProcess[0];
+       }
+
+       /**
+        * Returns the Java Model this operation is operating in.
+        */
+       public IJavaModel getJavaModel() {
+               if (fElementsToProcess == null || fElementsToProcess.length == 0) {
+                       return getParentElement().getJavaModel();
+               } else {
+                       return fElementsToProcess[0].getJavaModel();
+               }
+       }
+
+       // protected IPath[] getNestedFolders(IPackageFragmentRoot root) throws
+       // JavaModelException {
+       // IPath rootPath = root.getPath();
+       // IClasspathEntry[] classpath = root.getJavaProject().getRawClasspath();
+       // int length = classpath.length;
+       // IPath[] result = new IPath[length];
+       // int index = 0;
+       // for (int i = 0; i < length; i++) {
+       // IPath path = classpath[i].getPath();
+       // if (rootPath.isPrefixOf(path) && !rootPath.equals(path)) {
+       // result[index++] = path;
+       // }
+       // }
+       // if (index < length) {
+       // System.arraycopy(result, 0, result = new IPath[index], 0, index);
+       // }
+       // return result;
+       // }
+       /**
+        * Returns the parent element to which this operation applies, or
+        * <code>null</code> if not applicable.
+        */
+       protected IJavaElement getParentElement() {
+               if (fParentElements == null || fParentElements.length == 0) {
+                       return null;
+               }
+               return fParentElements[0];
+       }
+
+       /**
+        * Returns the parent elements to which this operation applies, or
+        * <code>null</code> if not applicable.
+        */
+       protected IJavaElement[] getParentElements() {
+               return fParentElements;
+       }
+
+       /**
+        * Returns the elements created by this operation.
+        */
+       public IJavaElement[] getResultElements() {
+               return resultElements;
+       }
+
+       /*
+        * Returns the scheduling rule for this operation (i.e. the resource that
+        * needs to be locked while this operation is running. Subclasses can
+        * override.
+        */
+       protected ISchedulingRule getSchedulingRule() {
+               return ResourcesPlugin.getWorkspace().getRoot();
+       }
+
+       /**
+        * Creates and returns a subprogress monitor if appropriate.
+        */
+       protected IProgressMonitor getSubProgressMonitor(int workAmount) {
+               IProgressMonitor sub = null;
+               if (progressMonitor != null) {
+                       sub = new SubProgressMonitor(progressMonitor, workAmount,
+                                       SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+               }
+               return sub;
+       }
+
+       /**
+        * Returns whether this operation has performed any resource modifications.
+        * Returns false if this operation has not been executed yet.
+        */
+       public boolean hasModifiedResource() {
+               return !this.isReadOnly()
+                               && this.getAttribute(HAS_MODIFIED_RESOURCE_ATTR) == TRUE;
+       }
+
+       public void internalWorked(double work) {
+               if (progressMonitor != null) {
+                       progressMonitor.internalWorked(work);
+               }
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public boolean isCanceled() {
+               if (progressMonitor != null) {
+                       return progressMonitor.isCanceled();
+               }
+               return false;
+       }
+
+       /**
+        * Returns <code>true</code> if this operation performs no resource
+        * modifications, otherwise <code>false</code>. Subclasses must override.
+        */
+       public boolean isReadOnly() {
+               return false;
+       }
+
+       /*
+        * Returns whether this operation is the first operation to run in the
+        * current thread.
+        */
+       protected boolean isTopLevelOperation() {
+               ArrayList stack;
+               return (stack = this.getCurrentOperationStack()).size() > 0
+                               && stack.get(0) == this;
+       }
+
+       /*
+        * Returns the index of the first registered action with the given id,
+        * starting from a given position. Returns -1 if not found.
+        */
+       protected int firstActionWithID(String id, int start) {
+               for (int i = start; i <= this.actionsEnd; i++) {
+                       if (this.actions[i].getID().equals(id)) {
+                               return i;
+                       }
+               }
+               return -1;
+       }
+
+       /**
+        * Convenience method to move resources
+        */
+       protected void moveResources(IResource[] resources, IPath destinationPath)
+                       throws JavaModelException {
+               IProgressMonitor subProgressMonitor = null;
+               if (progressMonitor != null) {
+                       subProgressMonitor = new SubProgressMonitor(progressMonitor,
+                                       resources.length,
+                                       SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+               }
+               IWorkspace workspace = resources[0].getWorkspace();
+               try {
+                       workspace.move(resources, destinationPath, false,
+                                       subProgressMonitor);
+                       this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
+               } catch (CoreException e) {
+                       throw new JavaModelException(e);
+               }
+       }
+
+       /**
+        * Creates and returns a new <code>IJavaElementDelta</code> on the Java
+        * Model.
+        */
+       public JavaElementDelta newJavaElementDelta() {
+               return new JavaElementDelta(getJavaModel());
+       }
+
+       /*
+        * Removes the last pushed operation from the stack of running operations.
+        * Returns the poped operation or null if the stack was empty.
+        */
+       protected JavaModelOperation popOperation() {
+               ArrayList stack = getCurrentOperationStack();
+               int size = stack.size();
+               if (size > 0) {
+                       if (size == 1) { // top level operation
+                               operationStacks.setCurrent(null); // release reference (see
+                                                                                                       // http://bugs.eclipse.org/bugs/show_bug.cgi?id=33927)
+                       }
+                       return (JavaModelOperation) stack.remove(size - 1);
+               } else {
+                       return null;
+               }
+       }
+
+       /*
+        * Registers the given action to be run when the outer most java model
+        * operation has finished. The insertion mode controls whether: - the action
+        * should discard all existing actions with the same id, and be queued at
+        * the end (REMOVEALL_APPEND), - the action should be ignored if there is
+        * already an action with the same id (KEEP_EXISTING), - the action should
+        * be queued at the end without looking at existing actions (APPEND)
+        */
+       protected void postAction(IPostAction action, int insertionMode) {
+               if (POST_ACTION_VERBOSE) {
+                       System.out
+                                       .print("(" + Thread.currentThread() + ") [JavaModelOperation.postAction(IPostAction, int)] Posting action " + action.getID()); //$NON-NLS-1$ //$NON-NLS-2$
+                       switch (insertionMode) {
+                       case REMOVEALL_APPEND:
+                               System.out.println(" (REMOVEALL_APPEND)"); //$NON-NLS-1$
+                               break;
+                       case KEEP_EXISTING:
+                               System.out.println(" (KEEP_EXISTING)"); //$NON-NLS-1$
+                               break;
+                       case APPEND:
+                               System.out.println(" (APPEND)"); //$NON-NLS-1$
+                               break;
+                       }
+               }
+
+               JavaModelOperation topLevelOp = (JavaModelOperation) getCurrentOperationStack()
+                               .get(0);
+               IPostAction[] postActions = topLevelOp.actions;
+               if (postActions == null) {
+                       topLevelOp.actions = postActions = new IPostAction[1];
+                       postActions[0] = action;
+                       topLevelOp.actionsEnd = 0;
+               } else {
+                       String id = action.getID();
+                       switch (insertionMode) {
+                       case REMOVEALL_APPEND:
+                               int index = this.actionsStart - 1;
+                               while ((index = topLevelOp.firstActionWithID(id, index + 1)) >= 0) {
+                                       // remove action[index]
+                                       System.arraycopy(postActions, index + 1, postActions,
+                                                       index, topLevelOp.actionsEnd - index);
+                                       postActions[topLevelOp.actionsEnd--] = null;
+                               }
+                               topLevelOp.addAction(action);
+                               break;
+                       case KEEP_EXISTING:
+                               if (topLevelOp.firstActionWithID(id, 0) < 0) {
+                                       topLevelOp.addAction(action);
+                               }
+                               break;
+                       case APPEND:
+                               topLevelOp.addAction(action);
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Returns whether the given path is the prefix of one of the given other
+        * paths.
+        */
+       protected boolean prefixesOneOf(IPath path, IPath[] otherPaths) {
+               for (int i = 0, length = otherPaths.length; i < length; i++) {
+                       if (path.isPrefixOf(otherPaths[i])) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /*
+        * Pushes the given operation on the stack of operations currently running
+        * in this thread.
+        */
+       protected void pushOperation(JavaModelOperation operation) {
+               getCurrentOperationStack().add(operation);
+       }
+
+       /**
+        * Main entry point for Java Model operations. Executes this operation and
+        * registers any deltas created.
+        * 
+        * @see IWorkspaceRunnable
+        * @exception CoreException
+        *                if the operation fails
+        */
+       public void run(IProgressMonitor monitor) throws CoreException {
+               JavaModelManager manager = JavaModelManager.getJavaModelManager();
+               DeltaProcessor deltaProcessor = manager.getDeltaProcessor();
+               int previousDeltaCount = deltaProcessor.javaModelDeltas.size();
+               try {
+                       progressMonitor = monitor;
+                       pushOperation(this);
+                       try {
+                               // computes the root infos before executing the operation
+                               // noop if aready initialized
+                               JavaModelManager.getJavaModelManager().deltaState
+                                               .initializeRoots();
+
+                               executeOperation();
+                       } finally {
+                               if (this.isTopLevelOperation()) {
+                                       this.runPostActions();
+                               }
+                       }
+               } finally {
+
+                       try {
+                               // update JavaModel using deltas that were recorded during this
+                               // operation
+                               for (int i = previousDeltaCount, size = manager.javaModelDeltas
+                                               .size(); i < size; i++) {
+                                       manager
+                                                       .updateJavaModel((IJavaElementDelta) manager.javaModelDeltas
+                                                                       .get(i));
+                               }
+
+                               // fire only iff:
+                               // - the operation is a top level operation
+                               // - the operation did produce some delta(s)
+                               // - but the operation has not modified any resource
+                               if (this.isTopLevelOperation()) {
+                                       if ((manager.javaModelDeltas.size() > previousDeltaCount || !manager.reconcileDeltas
+                                                       .isEmpty())
+                                                       && !this.hasModifiedResource()) {
+                                               manager.fire(null,
+                                                               JavaModelManager.DEFAULT_CHANGE_EVENT);
+                                       } // else deltas are fired while processing the resource
+                                               // delta
+                               }
+                       } finally {
+                               popOperation();
+                       }
+               }
+       }
+
+       /**
+        * Main entry point for Java Model operations. Runs a Java Model Operation
+        * as an IWorkspaceRunnable if not read-only.
+        */
+       public void runOperation(IProgressMonitor monitor)
+                       throws JavaModelException {
+               IJavaModelStatus status = verify();
+               if (!status.isOK()) {
+                       throw new JavaModelException(status);
+               }
+               try {
+                       if (isReadOnly()) {
+                               run(monitor);
+                       } else {
+                               // Use IWorkspace.run(...) to ensure that a build will be done
+                               // in autobuild mode.
+                               // Note that if the tree is locked, this will throw a
+                               // CoreException, but this is ok
+                               // as this operation is modifying the tree (not read-only) and a
+                               // CoreException will be thrown anyway.
+                               ResourcesPlugin.getWorkspace().run(this, getSchedulingRule(),
+                                               IWorkspace.AVOID_UPDATE, monitor);
+                       }
+               } catch (CoreException ce) {
+                       if (ce instanceof JavaModelException) {
+                               throw (JavaModelException) ce;
+                       } else {
+                               if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
+                                       Throwable e = ce.getStatus().getException();
+                                       if (e instanceof JavaModelException) {
+                                               throw (JavaModelException) e;
+                                       }
+                               }
+                               throw new JavaModelException(ce);
+                       }
+               }
+       }
+
+       protected void runPostActions() throws JavaModelException {
+               while (this.actionsStart <= this.actionsEnd) {
+                       IPostAction postAction = this.actions[this.actionsStart++];
+                       if (POST_ACTION_VERBOSE) {
+                               System.out
+                                               .println("(" + Thread.currentThread() + ") [JavaModelOperation.runPostActions()] Running action " + postAction.getID()); //$NON-NLS-1$ //$NON-NLS-2$
+                       }
+                       postAction.run();
+               }
+       }
+
+       /*
+        * Registers the given attribute at the given key with the top level
+        * operation.
+        */
+       protected void setAttribute(Object key, Object attribute) {
+               JavaModelOperation topLevelOp = (JavaModelOperation) this
+                               .getCurrentOperationStack().get(0);
+               if (topLevelOp.attributes == null) {
+                       topLevelOp.attributes = new HashMap();
+               }
+               topLevelOp.attributes.put(key, attribute);
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void setCanceled(boolean b) {
+               if (progressMonitor != null) {
+                       progressMonitor.setCanceled(b);
+               }
+       }
+
+       /**
+        * Sets whether this operation is nested or not.
+        * 
+        * @see CreateElementInCUOperation#checkCanceled
+        */
+       protected void setNested(boolean nested) {
+               isNested = nested;
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void setTaskName(String name) {
+               if (progressMonitor != null) {
+                       progressMonitor.setTaskName(name);
+               }
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void subTask(String name) {
+               if (progressMonitor != null) {
+                       progressMonitor.subTask(name);
+               }
+       }
+
+       /**
+        * Returns a status indicating if there is any known reason this operation
+        * will fail. Operations are verified before they are run.
+        * 
+        * Subclasses must override if they have any conditions to verify before
+        * this operation executes.
+        * 
+        * @see IJavaModelStatus
+        */
+       protected IJavaModelStatus verify() {
+               return commonVerify();
+       }
+
+       /**
+        * @see IProgressMonitor
+        */
+       public void worked(int work) {
+               if (progressMonitor != null) {
+                       progressMonitor.worked(work);
+                       checkCanceled();
+               }
+       }
 }
\ No newline at end of file