/**
  * Returns a collection of all the parents of this element up to (but not including) the root of
  * this tree in bottom-up order. If the given element is not a descendant of the root of this
  * tree, <code>null</code> is returned.
  */
 private ArrayList getAncestors(IJavaScriptElement element) {
   IJavaScriptElement parent = element.getParent();
   if (parent == null) {
     return null;
   }
   ArrayList parents = new ArrayList();
   while (!parent.equals(this.changedElement)) {
     parents.add(parent);
     parent = parent.getParent();
     if (parent == null) {
       return null;
     }
   }
   parents.trimToSize();
   return parents;
 }
  /**
   * Processes a delta recursively. When more than two children are affected the tree is fully
   * refreshed starting at this node. The delta is processed in the current thread but the viewer
   * updates are posted to the UI thread.
   */
  protected void processDelta(IJavaScriptElementDelta delta) throws JavaScriptModelException {
    int kind = delta.getKind();
    int flags = delta.getFlags();
    final IJavaScriptElement element = delta.getElement();
    final boolean isElementValidForView = fBrowsingPart.isValidElement(element);

    if (!getProvideWorkingCopy()
        && element instanceof IJavaScriptUnit
        && ((IJavaScriptUnit) element).isWorkingCopy()) return;

    if (element != null
        && element.getElementType() == IJavaScriptElement.JAVASCRIPT_UNIT
        && !isOnClassPath((IJavaScriptUnit) element)) return;

    // handle open and closing of a solution or project
    if (((flags & IJavaScriptElementDelta.F_CLOSED) != 0)
        || ((flags & IJavaScriptElementDelta.F_OPENED) != 0)) {
      postRefresh(null);
      return;
    }

    if (kind == IJavaScriptElementDelta.REMOVED) {
      Object parent = internalGetParent(element);
      if (isElementValidForView) {
        if (element instanceof IClassFile) {
          postRemove(((IClassFile) element).getType());
        } else if (element instanceof IJavaScriptUnit
            && !((IJavaScriptUnit) element).isWorkingCopy()) {
          postRefresh(null);
        } else if (element instanceof IJavaScriptUnit
            && ((IJavaScriptUnit) element).isWorkingCopy()) {
          if (getProvideWorkingCopy()) postRefresh(null);
        } else if (parent instanceof IJavaScriptUnit
            && getProvideWorkingCopy()
            && !((IJavaScriptUnit) parent).isWorkingCopy()) {
          if (element instanceof IJavaScriptUnit && ((IJavaScriptUnit) element).isWorkingCopy()) {
            // working copy removed from system - refresh
            postRefresh(null);
          }
        } else if (element instanceof IJavaScriptUnit
            && ((IJavaScriptUnit) element).isWorkingCopy()
            && parent != null
            && parent.equals(fInput))
          // closed editor - removing working copy
          postRefresh(null);
        else postRemove(element);
      }

      if (fBrowsingPart.isAncestorOf(element, fInput)) {
        if (element instanceof IJavaScriptUnit && ((IJavaScriptUnit) element).isWorkingCopy()) {
          postAdjustInputAndSetSelection(((IJavaScriptElement) fInput).getPrimaryElement());
        } else postAdjustInputAndSetSelection(null);
      }

      if (fInput != null && fInput.equals(element)) postRefresh(null);

      if (parent instanceof IPackageFragment && fBrowsingPart.isValidElement(parent)) {
        // refresh if package gets empty (might be filtered)
        if (isPackageFragmentEmpty((IPackageFragment) parent)
            && fViewer.testFindItem(parent) != null) postRefresh(null);
      }

      return;
    }
    if (kind == IJavaScriptElementDelta.ADDED
        && delta.getMovedFromElement() != null
        && element instanceof IJavaScriptUnit) return;

    if (kind == IJavaScriptElementDelta.ADDED) {
      if (isElementValidForView) {
        Object parent = internalGetParent(element);
        if (element instanceof IClassFile) {
          postAdd(parent, ((IClassFile) element).getType());
        } else if (element instanceof IJavaScriptUnit
            && !((IJavaScriptUnit) element).isWorkingCopy()) {
          postAdd(parent, ((IJavaScriptUnit) element).getTypes());
        } else if (parent instanceof IJavaScriptUnit
            && getProvideWorkingCopy()
            && !((IJavaScriptUnit) parent).isWorkingCopy()) {
          //	do nothing
        } else if (element instanceof IJavaScriptUnit
            && ((IJavaScriptUnit) element).isWorkingCopy()) {
          // new working copy comes to live
          postRefresh(null);
        } else postAdd(parent, element);
      } else if (fInput == null) {
        IJavaScriptElement newInput = fBrowsingPart.findInputForJavaElement(element);
        if (newInput != null) postAdjustInputAndSetSelection(element);
      } else if (element instanceof IType && fBrowsingPart.isValidInput(element)) {
        IJavaScriptElement cu1 = element.getAncestor(IJavaScriptElement.JAVASCRIPT_UNIT);
        IJavaScriptElement cu2 =
            ((IJavaScriptElement) fInput).getAncestor(IJavaScriptElement.JAVASCRIPT_UNIT);
        if (cu1 != null && cu2 != null && cu1.equals(cu2)) postAdjustInputAndSetSelection(element);
      }
      return;
    }

    if (kind == IJavaScriptElementDelta.CHANGED) {
      if (fInput != null
          && fInput.equals(element)
          && (flags & IJavaScriptElementDelta.F_CHILDREN) != 0
          && (flags & IJavaScriptElementDelta.F_FINE_GRAINED) != 0) {
        postRefresh(null, true);
        return;
      }
      if (isElementValidForView && (flags & IJavaScriptElementDelta.F_MODIFIERS) != 0) {
        postUpdateIcon(element);
      }
    }

    if (isClassPathChange(delta))
      // throw the towel and do a full refresh
      postRefresh(null);

    if ((flags & IJavaScriptElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0
        && fInput instanceof IJavaScriptElement) {
      IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) element;
      IJavaScriptElement inputsParent =
          ((IJavaScriptElement) fInput).getAncestor(IJavaScriptElement.PACKAGE_FRAGMENT_ROOT);
      if (pkgRoot.equals(inputsParent)) postRefresh(null);
    }

    // the source attachment of a JAR has changed
    if (element instanceof IPackageFragmentRoot
        && (((flags & IJavaScriptElementDelta.F_SOURCEATTACHED) != 0
            || ((flags & IJavaScriptElementDelta.F_SOURCEDETACHED)) != 0))) postUpdateIcon(element);

    IJavaScriptElementDelta[] affectedChildren = delta.getAffectedChildren();
    if (affectedChildren.length > 1) {
      // a package fragment might become non empty refresh from the parent
      if (element instanceof IPackageFragment) {
        IJavaScriptElement parent = (IJavaScriptElement) internalGetParent(element);
        // avoid posting a refresh to an invisible parent
        if (element.equals(fInput)) {
          postRefresh(element);
        } else {
          postRefresh(parent);
        }
      }
      // more than one child changed, refresh from here downwards
      if (element instanceof IPackageFragmentRoot && isElementValidForView) {
        postRefresh(skipProjectPackageFragmentRoot((IPackageFragmentRoot) element));
        return;
      }
    }
    for (int i = 0; i < affectedChildren.length; i++) {
      processDelta(affectedChildren[i]);
    }
  }
 /** Returns whether the two java elements are equals and have the same parent. */
 protected boolean equalsAndSameParent(IJavaScriptElement e1, IJavaScriptElement e2) {
   IJavaScriptElement parent1;
   return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent());
 }