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;
22 * @see IJavaElementDelta
24 public class JavaElementDelta extends SimpleDelta implements IJavaElementDelta {
26 * The element that this delta describes the change to.
29 protected IJavaElement fChangedElement;
33 private int fKind = 0;
37 private int fChangeFlags = 0;
39 * @see #getAffectedChildren()
41 protected IJavaElementDelta[] fAffectedChildren = fgEmptyDelta;
44 * Collection of resource deltas that correspond to non java resources deltas.
46 protected IResourceDelta[] resourceDeltas = null;
49 * Counter of resource deltas
51 protected int resourceDeltasCounter;
53 * @see #getMovedFromHandle()
55 protected IJavaElement fMovedFromHandle = null;
57 * @see #getMovedToHandle()
59 protected IJavaElement fMovedToHandle = null;
61 * Empty array of IJavaElementDelta
63 protected static IJavaElementDelta[] fgEmptyDelta= new IJavaElementDelta[] {};
65 * Creates the root delta. To create the nested delta
66 * hierarchies use the following convenience methods. The root
67 * delta can be created at any level (for example: project, package root,
68 * package fragment...).
70 * <li><code>added(IJavaElement)</code>
71 * <li><code>changed(IJavaElement)</code>
72 * <li><code>moved(IJavaElement, IJavaElement)</code>
73 * <li><code>removed(IJavaElement)</code>
74 * <li><code>renamed(IJavaElement, IJavaElement)</code>
77 public JavaElementDelta(IJavaElement element) {
79 fChangedElement = element;
82 * Adds the child delta to the collection of affected children. If the
83 * child is already in the collection, walk down the hierarchy.
85 protected void addAffectedChild(JavaElementDelta child) {
89 // no need to add a child if this parent is added or removed
92 fChangeFlags |= F_CHILDREN;
96 fChangeFlags |= F_CHILDREN;
99 // if a child delta is added to a compilation unit delta or below,
100 // it's a fine grained delta
101 if (fChangedElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
105 if (fAffectedChildren.length == 0) {
106 fAffectedChildren = new IJavaElementDelta[] {child};
109 IJavaElementDelta existingChild = null;
110 int existingChildIndex = -1;
111 if (fAffectedChildren != null) {
112 for (int i = 0; i < fAffectedChildren.length; i++) {
113 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
114 existingChild = fAffectedChildren[i];
115 existingChildIndex = i;
120 if (existingChild == null) { //new affected child
121 fAffectedChildren= growAndAddToArray(fAffectedChildren, child);
123 switch (existingChild.getKind()) {
125 switch (child.getKind()) {
126 case ADDED: // child was added then added -> it is added
127 case CHANGED: // child was added then changed -> it is added
129 case REMOVED: // child was added then removed -> noop
130 fAffectedChildren = this.removeAndShrinkArray(fAffectedChildren, existingChildIndex);
135 switch (child.getKind()) {
136 case ADDED: // child was removed then added -> it is changed
137 child.fKind = CHANGED;
138 fAffectedChildren[existingChildIndex] = child;
140 case CHANGED: // child was removed then changed -> it is removed
141 case REMOVED: // child was removed then removed -> it is removed
146 switch (child.getKind()) {
147 case ADDED: // child was changed then added -> it is added
148 case REMOVED: // child was changed then removed -> it is removed
149 fAffectedChildren[existingChildIndex] = child;
151 case CHANGED: // child was changed then changed -> it is changed
152 IJavaElementDelta[] children = child.getAffectedChildren();
153 for (int i = 0; i < children.length; i++) {
154 JavaElementDelta childsChild = (JavaElementDelta) children[i];
155 ((JavaElementDelta) existingChild).addAffectedChild(childsChild);
158 // update flags if needed
159 switch (((JavaElementDelta) existingChild).fChangeFlags) {
160 case F_ADDED_TO_CLASSPATH:
161 case F_REMOVED_FROM_CLASSPATH:
162 case F_SOURCEATTACHED:
163 case F_SOURCEDETACHED:
164 ((JavaElementDelta) existingChild).fChangeFlags |= ((JavaElementDelta) child).fChangeFlags;
168 // add the non-java resource deltas if needed
169 // note that the child delta always takes precedence over this existing child delta
170 // as non-java resource deltas are always created last (by the DeltaProcessor)
171 IResourceDelta[] resDeltas = child.getResourceDeltas();
172 if (resDeltas != null) {
173 ((JavaElementDelta)existingChild).resourceDeltas = resDeltas;
174 ((JavaElementDelta)existingChild).resourceDeltasCounter = child.resourceDeltasCounter;
180 // unknown -> existing child becomes the child with the existing child's flags
181 int flags = existingChild.getFlags();
182 fAffectedChildren[existingChildIndex] = child;
183 child.fChangeFlags |= flags;
188 // * Creates the nested deltas resulting from an add operation.
189 // * Convenience method for creating add deltas.
190 // * The constructor should be used to create the root delta
191 // * and then an add operation should call this method.
193 //public void added(IJavaElement element) {
194 // JavaElementDelta addedDelta = new JavaElementDelta(element);
195 // addedDelta.fKind = ADDED;
196 // insertDeltaTree(element, addedDelta);
199 * Creates the nested deltas resulting from an add operation.
200 * Convenience method for creating add deltas.
201 * The constructor should be used to create the root delta
202 * and then an add operation should call this method.
204 public void added(IJavaElement element) {
207 public void added(IJavaElement element, int flags) {
208 JavaElementDelta addedDelta = new JavaElementDelta(element);
210 addedDelta.changeFlags |= flags;
211 insertDeltaTree(element, addedDelta);
214 * Adds the child delta to the collection of affected children. If the
215 * child is already in the collection, walk down the hierarchy.
217 protected void addResourceDelta(IResourceDelta child) {
221 // no need to add a child if this parent is added or removed
224 fChangeFlags |= F_CONTENT;
228 fChangeFlags |= F_CONTENT;
230 if (resourceDeltas == null) {
231 resourceDeltas = new IResourceDelta[5];
232 resourceDeltas[resourceDeltasCounter++] = child;
235 if (resourceDeltas.length == resourceDeltasCounter) {
237 System.arraycopy(resourceDeltas, 0, (resourceDeltas = new IResourceDelta[resourceDeltasCounter * 2]), 0, resourceDeltasCounter);
239 resourceDeltas[resourceDeltasCounter++] = child;
242 * Creates the nested deltas resulting from a change operation.
243 * Convenience method for creating change deltas.
244 * The constructor should be used to create the root delta
245 * and then a change operation should call this method.
247 public void changed(IJavaElement element, int changeFlag) {
248 JavaElementDelta changedDelta = new JavaElementDelta(element);
249 changedDelta.fKind = CHANGED;
250 changedDelta.fChangeFlags |= changeFlag;
251 insertDeltaTree(element, changedDelta);
254 * Mark this delta as a content changed delta.
256 public void contentChanged() {
257 fChangeFlags |= F_CONTENT;
260 // * Clone this delta so that its elements are rooted at the given project.
262 //public IJavaElementDelta clone(IJavaProject project) {
263 // JavaElementDelta clone =
264 // new JavaElementDelta(((JavaElement)fChangedElement).rootedAt(project));
265 // if (fAffectedChildren != fgEmptyDelta) {
266 // int length = fAffectedChildren.length;
267 // IJavaElementDelta[] cloneChildren = new IJavaElementDelta[length];
268 // for (int i= 0; i < length; i++) {
269 // cloneChildren[i] = ((JavaElementDelta)fAffectedChildren[i]).clone(project);
271 // clone.fAffectedChildren = cloneChildren;
273 // clone.fChangeFlags = fChangeFlags;
274 // clone.fKind = fKind;
275 // if (fMovedFromHandle != null) {
276 // clone.fMovedFromHandle = ((JavaElement)fMovedFromHandle).rootedAt(project);
278 // if (fMovedToHandle != null) {
279 // clone.fMovedToHandle = ((JavaElement)fMovedToHandle).rootedAt(project);
281 // clone.resourceDeltas = this.resourceDeltas;
282 // clone.resourceDeltasCounter = this.resourceDeltasCounter;
287 * Creates the nested deltas for a closed element.
289 public void closed(IJavaElement element) {
290 JavaElementDelta delta = new JavaElementDelta(element);
291 delta.fKind = CHANGED;
292 delta.fChangeFlags |= F_CLOSED;
293 insertDeltaTree(element, delta);
296 * Creates the nested delta deltas based on the affected element
297 * its delta, and the root of this delta tree. Returns the root
298 * of the created delta tree.
300 protected JavaElementDelta createDeltaTree(IJavaElement element, JavaElementDelta delta) {
301 JavaElementDelta childDelta = delta;
302 ArrayList ancestors= getAncestors(element);
303 if (ancestors == null) {
304 if (this.equalsAndSameParent(delta.getElement(), getElement())) { // handle case of two jars that can be equals but not in the same project
305 // the element being changed is the root element
307 fChangeFlags = delta.fChangeFlags;
308 fMovedToHandle = delta.fMovedToHandle;
309 fMovedFromHandle = delta.fMovedFromHandle;
312 for (int i = 0, size = ancestors.size(); i < size; i++) {
313 IJavaElement ancestor = (IJavaElement) ancestors.get(i);
314 JavaElementDelta ancestorDelta = new JavaElementDelta(ancestor);
315 ancestorDelta.addAffectedChild(childDelta);
316 childDelta = ancestorDelta;
322 * Returns whether the two java elements are equals and have the same parent.
324 protected boolean equalsAndSameParent(IJavaElement e1, IJavaElement e2) {
325 IJavaElement parent1;
326 return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent());
329 * Returns the <code>JavaElementDelta</code> for the given element
330 * in the delta tree, or null, if no delta for the given element is found.
332 protected JavaElementDelta find(IJavaElement e) {
333 if (this.equalsAndSameParent(fChangedElement, e)) { // handle case of two jars that can be equals but not in the same project
336 for (int i = 0; i < fAffectedChildren.length; i++) {
337 JavaElementDelta delta = ((JavaElementDelta)fAffectedChildren[i]).find(e);
346 * Mark this delta as a fine-grained delta.
348 public void fineGrained() {
349 if (fKind == 0) { // if not set yet
352 fChangeFlags |= F_FINE_GRAINED;
355 * @see IJavaElementDelta
357 public IJavaElementDelta[] getAddedChildren() {
358 return getChildrenOfType(ADDED);
361 * @see IJavaElementDelta
363 public IJavaElementDelta[] getAffectedChildren() {
364 return fAffectedChildren;
367 * Returns a collection of all the parents of this element up to (but
368 * not including) the root of this tree in bottom-up order. If the given
369 * element is not a descendant of the root of this tree, <code>null</code>
372 private ArrayList getAncestors(IJavaElement element) {
373 IJavaElement parent = element.getParent();
374 if (parent == null) {
377 ArrayList parents = new ArrayList();
378 while (!parent.equals(fChangedElement)) {
380 parent = parent.getParent();
381 if (parent == null) {
385 parents.trimToSize();
389 * @see IJavaElementDelta
391 public IJavaElementDelta[] getChangedChildren() {
392 return getChildrenOfType(CHANGED);
395 * @see IJavaElementDelta
397 protected IJavaElementDelta[] getChildrenOfType(int type) {
398 int length = fAffectedChildren.length;
400 return new IJavaElementDelta[] {};
402 ArrayList children= new ArrayList(length);
403 for (int i = 0; i < length; i++) {
404 if (fAffectedChildren[i].getKind() == type) {
405 children.add(fAffectedChildren[i]);
409 IJavaElementDelta[] childrenOfType = new IJavaElementDelta[children.size()];
410 children.toArray(childrenOfType);
412 return childrenOfType;
415 * Returns the delta for a given element. Only looks below this
418 protected JavaElementDelta getDeltaFor(IJavaElement element) {
419 if (this.equalsAndSameParent(getElement(), element)) // handle case of two jars that can be equals but not in the same project
421 if (fAffectedChildren.length == 0)
423 int childrenCount = fAffectedChildren.length;
424 for (int i = 0; i < childrenCount; i++) {
425 JavaElementDelta delta = (JavaElementDelta)fAffectedChildren[i];
426 if (this.equalsAndSameParent(delta.getElement(), element)) { // handle case of two jars that can be equals but not in the same project
429 delta = ((JavaElementDelta)delta).getDeltaFor(element);
437 * @see IJavaElementDelta
439 public IJavaElement getElement() {
440 return fChangedElement;
443 * @see IJavaElementDelta
445 public int getFlags() {
449 * @see IJavaElementDelta
451 public int getKind() {
455 * @see IJavaElementDelta
457 public IJavaElement getMovedFromElement() {
458 return fMovedFromHandle;
461 * @see IJavaElementDelta
463 public IJavaElement getMovedToElement() {
464 return fMovedToHandle;
467 * @see IJavaElementDelta
469 public IJavaElementDelta[] getRemovedChildren() {
470 return getChildrenOfType(REMOVED);
473 * Return the collection of resource deltas. Return null if none.
475 public IResourceDelta[] getResourceDeltas() {
476 if (resourceDeltas == null) return null;
477 if (resourceDeltas.length != resourceDeltasCounter) {
478 System.arraycopy(resourceDeltas, 0, resourceDeltas = new IResourceDelta[resourceDeltasCounter], 0, resourceDeltasCounter);
480 return resourceDeltas;
483 * Adds the new element to a new array that contains all of the elements of the old array.
484 * Returns the new array.
486 protected IJavaElementDelta[] growAndAddToArray(IJavaElementDelta[] array, IJavaElementDelta addition) {
487 IJavaElementDelta[] old = array;
488 array = new IJavaElementDelta[old.length + 1];
489 System.arraycopy(old, 0, array, 0, old.length);
490 array[old.length] = addition;
494 * Creates the delta tree for the given element and delta, and then
495 * inserts the tree as an affected child of this node.
497 protected void insertDeltaTree(IJavaElement element, JavaElementDelta delta) {
498 JavaElementDelta childDelta= createDeltaTree(element, delta);
499 if (!this.equalsAndSameParent(element, getElement())) { // handle case of two jars that can be equals but not in the same project
500 addAffectedChild(childDelta);
504 * Creates the nested deltas resulting from an move operation.
505 * Convenience method for creating the "move from" delta.
506 * The constructor should be used to create the root delta
507 * and then the move operation should call this method.
509 public void movedFrom(IJavaElement movedFromElement, IJavaElement movedToElement) {
510 JavaElementDelta removedDelta = new JavaElementDelta(movedFromElement);
511 removedDelta.fKind = REMOVED;
512 removedDelta.fChangeFlags |= F_MOVED_TO;
513 removedDelta.fMovedToHandle = movedToElement;
514 insertDeltaTree(movedFromElement, removedDelta);
517 * Creates the nested deltas resulting from an move operation.
518 * Convenience method for creating the "move to" delta.
519 * The constructor should be used to create the root delta
520 * and then the move operation should call this method.
522 public void movedTo(IJavaElement movedToElement, IJavaElement movedFromElement) {
523 JavaElementDelta addedDelta = new JavaElementDelta(movedToElement);
524 addedDelta.fKind = ADDED;
525 addedDelta.fChangeFlags |= F_MOVED_FROM;
526 addedDelta.fMovedFromHandle = movedFromElement;
527 insertDeltaTree(movedToElement, addedDelta);
530 * Creates the nested deltas for an opened element.
532 public void opened(IJavaElement element) {
533 JavaElementDelta delta = new JavaElementDelta(element);
534 delta.fKind = CHANGED;
535 delta.fChangeFlags |= F_OPENED;
536 insertDeltaTree(element, delta);
539 * Removes the child delta from the collection of affected children.
541 protected void removeAffectedChild(JavaElementDelta child) {
543 if (fAffectedChildren != null) {
544 for (int i = 0; i < fAffectedChildren.length; i++) {
545 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
552 fAffectedChildren= removeAndShrinkArray(fAffectedChildren, index);
556 * Removes the element from the array.
557 * Returns the a new array which has shrunk.
559 protected IJavaElementDelta[] removeAndShrinkArray(IJavaElementDelta[] old, int index) {
560 IJavaElementDelta[] array = new IJavaElementDelta[old.length - 1];
562 System.arraycopy(old, 0, array, 0, index);
563 int rest = old.length - index - 1;
565 System.arraycopy(old, index + 1, array, index, rest);
569 * Creates the nested deltas resulting from an delete operation.
570 * Convenience method for creating removed deltas.
571 * The constructor should be used to create the root delta
572 * and then the delete operation should call this method.
574 public void removed(IJavaElement element) {
577 public void removed(IJavaElement element, int flags) {
578 JavaElementDelta removedDelta= new JavaElementDelta(element);
579 insertDeltaTree(element, removedDelta);
580 JavaElementDelta actualDelta = getDeltaFor(element);
581 if (actualDelta != null) {
582 actualDelta.removed();
583 actualDelta.changeFlags |= flags;
584 actualDelta.fAffectedChildren = fgEmptyDelta;
589 * Creates the nested deltas resulting from a change operation.
590 * Convenience method for creating change deltas.
591 * The constructor should be used to create the root delta
592 * and then a change operation should call this method.
594 public void sourceAttached(IJavaElement element) {
595 JavaElementDelta attachedDelta = new JavaElementDelta(element);
596 attachedDelta.fKind = CHANGED;
597 attachedDelta.fChangeFlags |= F_SOURCEATTACHED;
598 insertDeltaTree(element, attachedDelta);
601 * Creates the nested deltas resulting from a change operation.
602 * Convenience method for creating change deltas.
603 * The constructor should be used to create the root delta
604 * and then a change operation should call this method.
606 public void sourceDetached(IJavaElement element) {
607 JavaElementDelta detachedDelta = new JavaElementDelta(element);
608 detachedDelta.fKind = CHANGED;
609 detachedDelta.fChangeFlags |= F_SOURCEDETACHED;
610 insertDeltaTree(element, detachedDelta);
613 * Returns a string representation of this delta's
614 * structure suitable for debug purposes.
618 public String toDebugString(int depth) {
619 StringBuffer buffer = new StringBuffer();
620 for (int i= 0; i < depth; i++) {
623 buffer.append(((JavaElement)getElement()).toDebugString());
624 buffer.append("["); //$NON-NLS-1$
626 case IJavaElementDelta.ADDED :
629 case IJavaElementDelta.REMOVED :
632 case IJavaElementDelta.CHANGED :
639 buffer.append("]: {"); //$NON-NLS-1$
640 int changeFlags = getFlags();
641 boolean prev = false;
642 if ((changeFlags & IJavaElementDelta.F_CHILDREN) != 0) {
644 buffer.append(" | "); //$NON-NLS-1$
645 buffer.append("CHILDREN"); //$NON-NLS-1$
648 if ((changeFlags & IJavaElementDelta.F_CONTENT) != 0) {
650 buffer.append(" | "); //$NON-NLS-1$
651 buffer.append("CONTENT"); //$NON-NLS-1$
654 if ((changeFlags & IJavaElementDelta.F_MOVED_FROM) != 0) {
656 buffer.append(" | "); //$NON-NLS-1$
657 buffer.append("MOVED_FROM(" + ((JavaElement)getMovedFromElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
660 if ((changeFlags & IJavaElementDelta.F_MOVED_TO) != 0) {
662 buffer.append(" | "); //$NON-NLS-1$
663 buffer.append("MOVED_TO(" + ((JavaElement)getMovedToElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
666 if ((changeFlags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) {
668 buffer.append(" | "); //$NON-NLS-1$
669 buffer.append("ADDED TO CLASSPATH"); //$NON-NLS-1$
672 if ((changeFlags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) {
674 buffer.append(" | "); //$NON-NLS-1$
675 buffer.append("REMOVED FROM CLASSPATH"); //$NON-NLS-1$
678 if ((changeFlags & IJavaElementDelta.F_REORDER) != 0) {
680 buffer.append(" | "); //$NON-NLS-1$
681 buffer.append("REORDERED"); //$NON-NLS-1$
684 if ((changeFlags & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) {
686 buffer.append(" | "); //$NON-NLS-1$
687 buffer.append("ARCHIVE CONTENT CHANGED"); //$NON-NLS-1$
690 if ((changeFlags & IJavaElementDelta.F_SOURCEATTACHED) != 0) {
692 buffer.append(" | "); //$NON-NLS-1$
693 buffer.append("SOURCE ATTACHED"); //$NON-NLS-1$
696 if ((changeFlags & IJavaElementDelta.F_SOURCEDETACHED) != 0) {
698 buffer.append(" | "); //$NON-NLS-1$
699 buffer.append("SOURCE DETACHED"); //$NON-NLS-1$
702 if ((changeFlags & IJavaElementDelta.F_MODIFIERS) != 0) {
704 buffer.append(" | "); //$NON-NLS-1$
705 buffer.append("MODIFIERS CHANGED"); //$NON-NLS-1$
708 if ((changeFlags & IJavaElementDelta.F_SUPER_TYPES) != 0) {
710 buffer.append(" | "); //$NON-NLS-1$
711 buffer.append("SUPER TYPES CHANGED"); //$NON-NLS-1$
714 if ((changeFlags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
716 buffer.append(" | "); //$NON-NLS-1$
717 buffer.append("FINE GRAINED"); //$NON-NLS-1$
720 buffer.append("}"); //$NON-NLS-1$
721 IJavaElementDelta[] children = getAffectedChildren();
722 if (children != null) {
723 for (int i = 0; i < children.length; ++i) {
724 buffer.append("\n"); //$NON-NLS-1$
725 buffer.append(((JavaElementDelta) children[i]).toDebugString(depth + 1));
728 for (int i = 0; i < resourceDeltasCounter; i++) {
729 buffer.append("\n");//$NON-NLS-1$
730 for (int j = 0; j < depth+1; j++) {
733 IResourceDelta resourceDelta = resourceDeltas[i];
734 buffer.append(resourceDelta.toString());
735 buffer.append("["); //$NON-NLS-1$
736 switch (resourceDelta.getKind()) {
737 case IResourceDelta.ADDED :
740 case IResourceDelta.REMOVED :
743 case IResourceDelta.CHANGED :
750 buffer.append("]"); //$NON-NLS-1$
752 return buffer.toString();
755 * Returns a string representation of this delta's
756 * structure suitable for debug purposes.
758 public String toString() {
759 return toDebugString(0);