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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
13 import java.util.ArrayList;
15 import net.sourceforge.phpdt.core.IJavaElement;
16 import net.sourceforge.phpdt.core.IJavaElementDelta;
18 import org.eclipse.core.resources.IResourceDelta;
21 * @see IJavaElementDelta
23 public class JavaElementDelta extends SimpleDelta implements IJavaElementDelta {
25 * The element that this delta describes the change to.
29 protected IJavaElement fChangedElement;
34 private int fKind = 0;
39 private int fChangeFlags = 0;
42 * @see #getAffectedChildren()
44 protected IJavaElementDelta[] fAffectedChildren = fgEmptyDelta;
47 * Collection of resource deltas that correspond to non java resources
50 protected IResourceDelta[] resourceDeltas = null;
53 * Counter of resource deltas
55 protected int resourceDeltasCounter;
58 * @see #getMovedFromHandle()
60 protected IJavaElement fMovedFromHandle = null;
63 * @see #getMovedToHandle()
65 protected IJavaElement fMovedToHandle = null;
68 * Empty array of IJavaElementDelta
70 protected static IJavaElementDelta[] fgEmptyDelta = new IJavaElementDelta[] {};
73 * Creates the root delta. To create the nested delta hierarchies use the
74 * following convenience methods. The root delta can be created at any level
75 * (for example: project, package root, package fragment...).
77 * <li><code>added(IJavaElement)</code>
78 * <li><code>changed(IJavaElement)</code>
79 * <li><code>moved(IJavaElement, IJavaElement)</code>
80 * <li><code>removed(IJavaElement)</code>
81 * <li><code>renamed(IJavaElement, IJavaElement)</code>
84 public JavaElementDelta(IJavaElement element) {
86 fChangedElement = element;
90 * Adds the child delta to the collection of affected children. If the child
91 * is already in the collection, walk down the hierarchy.
93 protected void addAffectedChild(JavaElementDelta child) {
97 // no need to add a child if this parent is added or removed
100 fChangeFlags |= F_CHILDREN;
104 fChangeFlags |= F_CHILDREN;
107 // if a child delta is added to a compilation unit delta or below,
108 // it's a fine grained delta
109 if (fChangedElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
113 if (fAffectedChildren.length == 0) {
114 fAffectedChildren = new IJavaElementDelta[] { child };
117 IJavaElementDelta existingChild = null;
118 int existingChildIndex = -1;
119 if (fAffectedChildren != null) {
120 for (int i = 0; i < fAffectedChildren.length; i++) {
121 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(),
122 child.getElement())) { // handle case of two jars that
123 // can be equals but not in the
125 existingChild = fAffectedChildren[i];
126 existingChildIndex = i;
131 if (existingChild == null) { // new affected child
132 fAffectedChildren = growAndAddToArray(fAffectedChildren, child);
134 switch (existingChild.getKind()) {
136 switch (child.getKind()) {
137 case ADDED: // child was added then added -> it is added
138 case CHANGED: // child was added then changed -> it is added
140 case REMOVED: // child was added then removed -> noop
141 fAffectedChildren = this.removeAndShrinkArray(
142 fAffectedChildren, existingChildIndex);
147 switch (child.getKind()) {
148 case ADDED: // child was removed then added -> it is changed
149 child.fKind = CHANGED;
150 fAffectedChildren[existingChildIndex] = child;
152 case CHANGED: // child was removed then changed -> it is
154 case REMOVED: // child was removed then removed -> it is
160 switch (child.getKind()) {
161 case ADDED: // child was changed then added -> it is added
162 case REMOVED: // child was changed then removed -> it is
164 fAffectedChildren[existingChildIndex] = child;
166 case CHANGED: // child was changed then changed -> it is
168 IJavaElementDelta[] children = child.getAffectedChildren();
169 for (int i = 0; i < children.length; i++) {
170 JavaElementDelta childsChild = (JavaElementDelta) children[i];
171 ((JavaElementDelta) existingChild)
172 .addAffectedChild(childsChild);
175 // update flags if needed
176 switch (((JavaElementDelta) existingChild).fChangeFlags) {
177 case F_ADDED_TO_CLASSPATH:
178 case F_REMOVED_FROM_CLASSPATH:
179 case F_SOURCEATTACHED:
180 case F_SOURCEDETACHED:
181 ((JavaElementDelta) existingChild).fChangeFlags |= ((JavaElementDelta) child).fChangeFlags;
185 // add the non-java resource deltas if needed
186 // note that the child delta always takes precedence over
187 // this existing child delta
188 // as non-java resource deltas are always created last (by
189 // the DeltaProcessor)
190 IResourceDelta[] resDeltas = child.getResourceDeltas();
191 if (resDeltas != null) {
192 ((JavaElementDelta) existingChild).resourceDeltas = resDeltas;
193 ((JavaElementDelta) existingChild).resourceDeltasCounter = child.resourceDeltasCounter;
199 // unknown -> existing child becomes the child with the existing
201 int flags = existingChild.getFlags();
202 fAffectedChildren[existingChildIndex] = child;
203 child.fChangeFlags |= flags;
209 // * Creates the nested deltas resulting from an add operation.
210 // * Convenience method for creating add deltas.
211 // * The constructor should be used to create the root delta
212 // * and then an add operation should call this method.
214 // public void added(IJavaElement element) {
215 // JavaElementDelta addedDelta = new JavaElementDelta(element);
216 // addedDelta.fKind = ADDED;
217 // insertDeltaTree(element, addedDelta);
220 * Creates the nested deltas resulting from an add operation. Convenience
221 * method for creating add deltas. The constructor should be used to create
222 * the root delta and then an add operation should call this method.
224 public void added(IJavaElement element) {
228 public void added(IJavaElement element, int flags) {
229 JavaElementDelta addedDelta = new JavaElementDelta(element);
231 addedDelta.changeFlags |= flags;
232 insertDeltaTree(element, addedDelta);
236 * Adds the child delta to the collection of affected children. If the child
237 * is already in the collection, walk down the hierarchy.
239 protected void addResourceDelta(IResourceDelta child) {
243 // no need to add a child if this parent is added or removed
246 fChangeFlags |= F_CONTENT;
250 fChangeFlags |= F_CONTENT;
252 if (resourceDeltas == null) {
253 resourceDeltas = new IResourceDelta[5];
254 resourceDeltas[resourceDeltasCounter++] = child;
257 if (resourceDeltas.length == resourceDeltasCounter) {
263 (resourceDeltas = new IResourceDelta[resourceDeltasCounter * 2]),
264 0, resourceDeltasCounter);
266 resourceDeltas[resourceDeltasCounter++] = child;
270 * Creates the nested deltas resulting from a change operation. Convenience
271 * method for creating change deltas. The constructor should be used to
272 * create the root delta and then a change operation should call this
275 public JavaElementDelta changed(IJavaElement element, int changeFlag) {
276 JavaElementDelta changedDelta = new JavaElementDelta(element);
277 changedDelta.changed(changeFlag);
278 insertDeltaTree(element, changedDelta);
283 * Mark this delta as a content changed delta.
285 public void contentChanged() {
286 fChangeFlags |= F_CONTENT;
290 // * Clone this delta so that its elements are rooted at the given project.
292 // public IJavaElementDelta clone(IJavaProject project) {
293 // JavaElementDelta clone =
294 // new JavaElementDelta(((JavaElement)fChangedElement).rootedAt(project));
295 // if (fAffectedChildren != fgEmptyDelta) {
296 // int length = fAffectedChildren.length;
297 // IJavaElementDelta[] cloneChildren = new IJavaElementDelta[length];
298 // for (int i= 0; i < length; i++) {
299 // cloneChildren[i] =
300 // ((JavaElementDelta)fAffectedChildren[i]).clone(project);
302 // clone.fAffectedChildren = cloneChildren;
304 // clone.fChangeFlags = fChangeFlags;
305 // clone.fKind = fKind;
306 // if (fMovedFromHandle != null) {
307 // clone.fMovedFromHandle =
308 // ((JavaElement)fMovedFromHandle).rootedAt(project);
310 // if (fMovedToHandle != null) {
311 // clone.fMovedToHandle = ((JavaElement)fMovedToHandle).rootedAt(project);
313 // clone.resourceDeltas = this.resourceDeltas;
314 // clone.resourceDeltasCounter = this.resourceDeltasCounter;
319 * Creates the nested deltas for a closed element.
321 public void closed(IJavaElement element) {
322 JavaElementDelta delta = new JavaElementDelta(element);
323 delta.fKind = CHANGED;
324 delta.fChangeFlags |= F_CLOSED;
325 insertDeltaTree(element, delta);
329 * Creates the nested delta deltas based on the affected element its delta,
330 * and the root of this delta tree. Returns the root of the created delta
333 protected JavaElementDelta createDeltaTree(IJavaElement element,
334 JavaElementDelta delta) {
335 JavaElementDelta childDelta = delta;
336 ArrayList ancestors = getAncestors(element);
337 if (ancestors == null) {
338 if (this.equalsAndSameParent(delta.getElement(), getElement())) { // handle
353 // the element being changed is the root element
355 fChangeFlags = delta.fChangeFlags;
356 fMovedToHandle = delta.fMovedToHandle;
357 fMovedFromHandle = delta.fMovedFromHandle;
360 for (int i = 0, size = ancestors.size(); i < size; i++) {
361 IJavaElement ancestor = (IJavaElement) ancestors.get(i);
362 JavaElementDelta ancestorDelta = new JavaElementDelta(ancestor);
363 ancestorDelta.addAffectedChild(childDelta);
364 childDelta = ancestorDelta;
371 * Returns whether the two java elements are equals and have the same
374 protected boolean equalsAndSameParent(IJavaElement e1, IJavaElement e2) {
375 IJavaElement parent1;
376 return e1.equals(e2) && ((parent1 = e1.getParent()) != null)
377 && parent1.equals(e2.getParent());
381 * Returns the <code>JavaElementDelta</code> for the given element in the
382 * delta tree, or null, if no delta for the given element is found.
384 protected JavaElementDelta find(IJavaElement e) {
385 if (this.equalsAndSameParent(fChangedElement, e)) { // handle case of
392 for (int i = 0; i < fAffectedChildren.length; i++) {
393 JavaElementDelta delta = ((JavaElementDelta) fAffectedChildren[i])
404 * Mark this delta as a fine-grained delta.
406 public void fineGrained() {
407 if (fKind == 0) { // if not set yet
410 fChangeFlags |= F_FINE_GRAINED;
414 * @see IJavaElementDelta
416 public IJavaElementDelta[] getAddedChildren() {
417 return getChildrenOfType(ADDED);
421 * @see IJavaElementDelta
423 public IJavaElementDelta[] getAffectedChildren() {
424 return fAffectedChildren;
428 * Returns a collection of all the parents of this element up to (but not
429 * including) the root of this tree in bottom-up order. If the given element
430 * is not a descendant of the root of this tree, <code>null</code> is
433 private ArrayList getAncestors(IJavaElement element) {
434 IJavaElement parent = element.getParent();
435 if (parent == null) {
438 ArrayList parents = new ArrayList();
439 while (!parent.equals(fChangedElement)) {
441 parent = parent.getParent();
442 if (parent == null) {
446 parents.trimToSize();
451 * @see IJavaElementDelta
453 public IJavaElementDelta[] getChangedChildren() {
454 return getChildrenOfType(CHANGED);
458 * @see IJavaElementDelta
460 protected IJavaElementDelta[] getChildrenOfType(int type) {
461 int length = fAffectedChildren.length;
463 return new IJavaElementDelta[] {};
465 ArrayList children = new ArrayList(length);
466 for (int i = 0; i < length; i++) {
467 if (fAffectedChildren[i].getKind() == type) {
468 children.add(fAffectedChildren[i]);
472 IJavaElementDelta[] childrenOfType = new IJavaElementDelta[children
474 children.toArray(childrenOfType);
476 return childrenOfType;
480 * Returns the delta for a given element. Only looks below this delta.
482 protected JavaElementDelta getDeltaFor(IJavaElement element) {
483 if (this.equalsAndSameParent(getElement(), element)) // handle case
490 if (fAffectedChildren.length == 0)
492 int childrenCount = fAffectedChildren.length;
493 for (int i = 0; i < childrenCount; i++) {
494 JavaElementDelta delta = (JavaElementDelta) fAffectedChildren[i];
495 if (this.equalsAndSameParent(delta.getElement(), element)) { // handle
512 delta = ((JavaElementDelta) delta).getDeltaFor(element);
521 * @see IJavaElementDelta
523 public IJavaElement getElement() {
524 return fChangedElement;
528 * @see IJavaElementDelta
530 public int getFlags() {
535 * @see IJavaElementDelta
537 public int getKind() {
542 * @see IJavaElementDelta
544 public IJavaElement getMovedFromElement() {
545 return fMovedFromHandle;
549 * @see IJavaElementDelta
551 public IJavaElement getMovedToElement() {
552 return fMovedToHandle;
556 * @see IJavaElementDelta
558 public IJavaElementDelta[] getRemovedChildren() {
559 return getChildrenOfType(REMOVED);
563 * Return the collection of resource deltas. Return null if none.
565 public IResourceDelta[] getResourceDeltas() {
566 if (resourceDeltas == null)
568 if (resourceDeltas.length != resourceDeltasCounter) {
569 System.arraycopy(resourceDeltas, 0,
570 resourceDeltas = new IResourceDelta[resourceDeltasCounter],
571 0, resourceDeltasCounter);
573 return resourceDeltas;
577 * Adds the new element to a new array that contains all of the elements of
578 * the old array. Returns the new array.
580 protected IJavaElementDelta[] growAndAddToArray(IJavaElementDelta[] array,
581 IJavaElementDelta addition) {
582 IJavaElementDelta[] old = array;
583 array = new IJavaElementDelta[old.length + 1];
584 System.arraycopy(old, 0, array, 0, old.length);
585 array[old.length] = addition;
590 * Creates the delta tree for the given element and delta, and then inserts
591 * the tree as an affected child of this node.
593 protected void insertDeltaTree(IJavaElement element, JavaElementDelta delta) {
594 JavaElementDelta childDelta = createDeltaTree(element, delta);
595 if (!this.equalsAndSameParent(element, getElement())) { // handle case
601 addAffectedChild(childDelta);
606 * Creates the nested deltas resulting from an move operation. Convenience
607 * method for creating the "move from" delta. The constructor should be used
608 * to create the root delta and then the move operation should call this
611 public void movedFrom(IJavaElement movedFromElement,
612 IJavaElement movedToElement) {
613 JavaElementDelta removedDelta = new JavaElementDelta(movedFromElement);
614 removedDelta.fKind = REMOVED;
615 removedDelta.fChangeFlags |= F_MOVED_TO;
616 removedDelta.fMovedToHandle = movedToElement;
617 insertDeltaTree(movedFromElement, removedDelta);
621 * Creates the nested deltas resulting from an move operation. Convenience
622 * method for creating the "move to" delta. The constructor should be used
623 * to create the root delta and then the move operation should call this
626 public void movedTo(IJavaElement movedToElement,
627 IJavaElement movedFromElement) {
628 JavaElementDelta addedDelta = new JavaElementDelta(movedToElement);
629 addedDelta.fKind = ADDED;
630 addedDelta.fChangeFlags |= F_MOVED_FROM;
631 addedDelta.fMovedFromHandle = movedFromElement;
632 insertDeltaTree(movedToElement, addedDelta);
636 * Creates the nested deltas for an opened element.
638 public void opened(IJavaElement element) {
639 JavaElementDelta delta = new JavaElementDelta(element);
640 delta.fKind = CHANGED;
641 delta.fChangeFlags |= F_OPENED;
642 insertDeltaTree(element, delta);
646 * Removes the child delta from the collection of affected children.
648 protected void removeAffectedChild(JavaElementDelta child) {
650 if (fAffectedChildren != null) {
651 for (int i = 0; i < fAffectedChildren.length; i++) {
652 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(),
653 child.getElement())) { // handle case of two jars that
654 // can be equals but not in the
662 fAffectedChildren = removeAndShrinkArray(fAffectedChildren, index);
667 * Removes the element from the array. Returns the a new array which has
670 protected IJavaElementDelta[] removeAndShrinkArray(IJavaElementDelta[] old,
672 IJavaElementDelta[] array = new IJavaElementDelta[old.length - 1];
674 System.arraycopy(old, 0, array, 0, index);
675 int rest = old.length - index - 1;
677 System.arraycopy(old, index + 1, array, index, rest);
682 * Creates the nested deltas resulting from an delete operation. Convenience
683 * method for creating removed deltas. The constructor should be used to
684 * create the root delta and then the delete operation should call this
687 public void removed(IJavaElement element) {
691 public void removed(IJavaElement element, int flags) {
692 JavaElementDelta removedDelta = new JavaElementDelta(element);
693 insertDeltaTree(element, removedDelta);
694 JavaElementDelta actualDelta = getDeltaFor(element);
695 if (actualDelta != null) {
696 actualDelta.removed();
697 actualDelta.changeFlags |= flags;
698 actualDelta.fAffectedChildren = fgEmptyDelta;
703 * Creates the nested deltas resulting from a change operation. Convenience
704 * method for creating change deltas. The constructor should be used to
705 * create the root delta and then a change operation should call this
708 public void sourceAttached(IJavaElement element) {
709 JavaElementDelta attachedDelta = new JavaElementDelta(element);
710 attachedDelta.fKind = CHANGED;
711 attachedDelta.fChangeFlags |= F_SOURCEATTACHED;
712 insertDeltaTree(element, attachedDelta);
716 * Creates the nested deltas resulting from a change operation. Convenience
717 * method for creating change deltas. The constructor should be used to
718 * create the root delta and then a change operation should call this
721 public void sourceDetached(IJavaElement element) {
722 JavaElementDelta detachedDelta = new JavaElementDelta(element);
723 detachedDelta.fKind = CHANGED;
724 detachedDelta.fChangeFlags |= F_SOURCEDETACHED;
725 insertDeltaTree(element, detachedDelta);
729 * Returns a string representation of this delta's structure suitable for
734 public String toDebugString(int depth) {
735 StringBuffer buffer = new StringBuffer();
736 for (int i = 0; i < depth; i++) {
739 buffer.append(((JavaElement) getElement()).toDebugString());
740 buffer.append("["); //$NON-NLS-1$
742 case IJavaElementDelta.ADDED:
745 case IJavaElementDelta.REMOVED:
748 case IJavaElementDelta.CHANGED:
755 buffer.append("]: {"); //$NON-NLS-1$
756 int changeFlags = getFlags();
757 boolean prev = false;
758 if ((changeFlags & IJavaElementDelta.F_CHILDREN) != 0) {
760 buffer.append(" | "); //$NON-NLS-1$
761 buffer.append("CHILDREN"); //$NON-NLS-1$
764 if ((changeFlags & IJavaElementDelta.F_CONTENT) != 0) {
766 buffer.append(" | "); //$NON-NLS-1$
767 buffer.append("CONTENT"); //$NON-NLS-1$
770 if ((changeFlags & IJavaElementDelta.F_MOVED_FROM) != 0) {
772 buffer.append(" | "); //$NON-NLS-1$
774 .append("MOVED_FROM(" + ((JavaElement) getMovedFromElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
777 if ((changeFlags & IJavaElementDelta.F_MOVED_TO) != 0) {
779 buffer.append(" | "); //$NON-NLS-1$
781 .append("MOVED_TO(" + ((JavaElement) getMovedToElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
784 if ((changeFlags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) {
786 buffer.append(" | "); //$NON-NLS-1$
787 buffer.append("ADDED TO CLASSPATH"); //$NON-NLS-1$
790 if ((changeFlags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) {
792 buffer.append(" | "); //$NON-NLS-1$
793 buffer.append("REMOVED FROM CLASSPATH"); //$NON-NLS-1$
796 if ((changeFlags & IJavaElementDelta.F_REORDER) != 0) {
798 buffer.append(" | "); //$NON-NLS-1$
799 buffer.append("REORDERED"); //$NON-NLS-1$
802 if ((changeFlags & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) {
804 buffer.append(" | "); //$NON-NLS-1$
805 buffer.append("ARCHIVE CONTENT CHANGED"); //$NON-NLS-1$
808 if ((changeFlags & IJavaElementDelta.F_SOURCEATTACHED) != 0) {
810 buffer.append(" | "); //$NON-NLS-1$
811 buffer.append("SOURCE ATTACHED"); //$NON-NLS-1$
814 if ((changeFlags & IJavaElementDelta.F_SOURCEDETACHED) != 0) {
816 buffer.append(" | "); //$NON-NLS-1$
817 buffer.append("SOURCE DETACHED"); //$NON-NLS-1$
820 if ((changeFlags & IJavaElementDelta.F_MODIFIERS) != 0) {
822 buffer.append(" | "); //$NON-NLS-1$
823 buffer.append("MODIFIERS CHANGED"); //$NON-NLS-1$
826 if ((changeFlags & IJavaElementDelta.F_SUPER_TYPES) != 0) {
828 buffer.append(" | "); //$NON-NLS-1$
829 buffer.append("SUPER TYPES CHANGED"); //$NON-NLS-1$
832 if ((changeFlags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
834 buffer.append(" | "); //$NON-NLS-1$
835 buffer.append("FINE GRAINED"); //$NON-NLS-1$
838 buffer.append("}"); //$NON-NLS-1$
839 IJavaElementDelta[] children = getAffectedChildren();
840 if (children != null) {
841 for (int i = 0; i < children.length; ++i) {
842 buffer.append("\n"); //$NON-NLS-1$
843 buffer.append(((JavaElementDelta) children[i])
844 .toDebugString(depth + 1));
847 for (int i = 0; i < resourceDeltasCounter; i++) {
848 buffer.append("\n");//$NON-NLS-1$
849 for (int j = 0; j < depth + 1; j++) {
852 IResourceDelta resourceDelta = resourceDeltas[i];
853 buffer.append(resourceDelta.toString());
854 buffer.append("["); //$NON-NLS-1$
855 switch (resourceDelta.getKind()) {
856 case IResourceDelta.ADDED:
859 case IResourceDelta.REMOVED:
862 case IResourceDelta.CHANGED:
869 buffer.append("]"); //$NON-NLS-1$
871 return buffer.toString();
875 * Returns a string representation of this delta's structure suitable for
878 public String toString() {
879 return toDebugString(0);