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;
17 import net.sourceforge.phpdt.core.IJavaProject;
19 import org.eclipse.core.resources.IResourceDelta;
23 * @see IJavaElementDelta
25 public class JavaElementDelta implements IJavaElementDelta {
27 * The element that this delta describes the change to.
30 protected IJavaElement fChangedElement;
34 private int fKind = 0;
38 private int fChangeFlags = 0;
40 * @see #getAffectedChildren()
42 protected IJavaElementDelta[] fAffectedChildren = fgEmptyDelta;
45 * Collection of resource deltas that correspond to non java resources deltas.
47 protected IResourceDelta[] resourceDeltas = null;
50 * Counter of resource deltas
52 protected int resourceDeltasCounter;
54 * @see #getMovedFromHandle()
56 protected IJavaElement fMovedFromHandle = null;
58 * @see #getMovedToHandle()
60 protected IJavaElement fMovedToHandle = null;
62 * Empty array of IJavaElementDelta
64 protected static IJavaElementDelta[] fgEmptyDelta= new IJavaElementDelta[] {};
66 * Creates the root delta. To create the nested delta
67 * hierarchies use the following convenience methods. The root
68 * delta can be created at any level (for example: project, package root,
69 * package fragment...).
71 * <li><code>added(IJavaElement)</code>
72 * <li><code>changed(IJavaElement)</code>
73 * <li><code>moved(IJavaElement, IJavaElement)</code>
74 * <li><code>removed(IJavaElement)</code>
75 * <li><code>renamed(IJavaElement, IJavaElement)</code>
78 public JavaElementDelta(IJavaElement element) {
80 fChangedElement = element;
83 * Adds the child delta to the collection of affected children. If the
84 * child is already in the collection, walk down the hierarchy.
86 protected void addAffectedChild(JavaElementDelta child) {
90 // no need to add a child if this parent is added or removed
93 fChangeFlags |= F_CHILDREN;
97 fChangeFlags |= F_CHILDREN;
100 // if a child delta is added to a compilation unit delta or below,
101 // it's a fine grained delta
102 if (fChangedElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
106 if (fAffectedChildren.length == 0) {
107 fAffectedChildren = new IJavaElementDelta[] {child};
110 IJavaElementDelta existingChild = null;
111 int existingChildIndex = -1;
112 if (fAffectedChildren != null) {
113 for (int i = 0; i < fAffectedChildren.length; i++) {
114 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
115 existingChild = fAffectedChildren[i];
116 existingChildIndex = i;
121 if (existingChild == null) { //new affected child
122 fAffectedChildren= growAndAddToArray(fAffectedChildren, child);
124 switch (existingChild.getKind()) {
126 switch (child.getKind()) {
127 case ADDED: // child was added then added -> it is added
128 case CHANGED: // child was added then changed -> it is added
130 case REMOVED: // child was added then removed -> noop
131 fAffectedChildren = this.removeAndShrinkArray(fAffectedChildren, existingChildIndex);
136 switch (child.getKind()) {
137 case ADDED: // child was removed then added -> it is changed
138 child.fKind = CHANGED;
139 fAffectedChildren[existingChildIndex] = child;
141 case CHANGED: // child was removed then changed -> it is removed
142 case REMOVED: // child was removed then removed -> it is removed
147 switch (child.getKind()) {
148 case ADDED: // child was changed then added -> it is added
149 case REMOVED: // child was changed then removed -> it is removed
150 fAffectedChildren[existingChildIndex] = child;
152 case CHANGED: // child was changed then changed -> it is changed
153 IJavaElementDelta[] children = child.getAffectedChildren();
154 for (int i = 0; i < children.length; i++) {
155 JavaElementDelta childsChild = (JavaElementDelta) children[i];
156 ((JavaElementDelta) existingChild).addAffectedChild(childsChild);
159 // update flags if needed
160 switch (((JavaElementDelta) existingChild).fChangeFlags) {
161 case F_ADDED_TO_CLASSPATH:
162 case F_REMOVED_FROM_CLASSPATH:
163 case F_SOURCEATTACHED:
164 case F_SOURCEDETACHED:
165 ((JavaElementDelta) existingChild).fChangeFlags |= ((JavaElementDelta) child).fChangeFlags;
169 // add the non-java resource deltas if needed
170 // note that the child delta always takes precedence over this existing child delta
171 // as non-java resource deltas are always created last (by the DeltaProcessor)
172 IResourceDelta[] resDeltas = child.getResourceDeltas();
173 if (resDeltas != null) {
174 ((JavaElementDelta)existingChild).resourceDeltas = resDeltas;
175 ((JavaElementDelta)existingChild).resourceDeltasCounter = child.resourceDeltasCounter;
181 // unknown -> existing child becomes the child with the existing child's flags
182 int flags = existingChild.getFlags();
183 fAffectedChildren[existingChildIndex] = child;
184 child.fChangeFlags |= flags;
189 * Creates the nested deltas resulting from an add operation.
190 * Convenience method for creating add deltas.
191 * The constructor should be used to create the root delta
192 * and then an add operation should call this method.
194 public void added(IJavaElement element) {
195 JavaElementDelta addedDelta = new JavaElementDelta(element);
196 addedDelta.fKind = ADDED;
197 insertDeltaTree(element, addedDelta);
200 * Adds the child delta to the collection of affected children. If the
201 * child is already in the collection, walk down the hierarchy.
203 protected void addResourceDelta(IResourceDelta child) {
207 // no need to add a child if this parent is added or removed
210 fChangeFlags |= F_CONTENT;
214 fChangeFlags |= F_CONTENT;
216 if (resourceDeltas == null) {
217 resourceDeltas = new IResourceDelta[5];
218 resourceDeltas[resourceDeltasCounter++] = child;
221 if (resourceDeltas.length == resourceDeltasCounter) {
223 System.arraycopy(resourceDeltas, 0, (resourceDeltas = new IResourceDelta[resourceDeltasCounter * 2]), 0, resourceDeltasCounter);
225 resourceDeltas[resourceDeltasCounter++] = child;
228 * Creates the nested deltas resulting from a change operation.
229 * Convenience method for creating change deltas.
230 * The constructor should be used to create the root delta
231 * and then a change operation should call this method.
233 public void changed(IJavaElement element, int changeFlag) {
234 JavaElementDelta changedDelta = new JavaElementDelta(element);
235 changedDelta.fKind = CHANGED;
236 changedDelta.fChangeFlags |= changeFlag;
237 insertDeltaTree(element, changedDelta);
240 * Mark this delta as a content changed delta.
242 public void contentChanged() {
243 fChangeFlags |= F_CONTENT;
246 * Clone this delta so that its elements are rooted at the given project.
248 public IJavaElementDelta clone(IJavaProject project) {
249 JavaElementDelta clone =
250 new JavaElementDelta(((JavaElement)fChangedElement).rootedAt(project));
251 if (fAffectedChildren != fgEmptyDelta) {
252 int length = fAffectedChildren.length;
253 IJavaElementDelta[] cloneChildren = new IJavaElementDelta[length];
254 for (int i= 0; i < length; i++) {
255 cloneChildren[i] = ((JavaElementDelta)fAffectedChildren[i]).clone(project);
257 clone.fAffectedChildren = cloneChildren;
259 clone.fChangeFlags = fChangeFlags;
261 if (fMovedFromHandle != null) {
262 clone.fMovedFromHandle = ((JavaElement)fMovedFromHandle).rootedAt(project);
264 if (fMovedToHandle != null) {
265 clone.fMovedToHandle = ((JavaElement)fMovedToHandle).rootedAt(project);
267 clone.resourceDeltas = this.resourceDeltas;
268 clone.resourceDeltasCounter = this.resourceDeltasCounter;
273 * Creates the nested deltas for a closed element.
275 public void closed(IJavaElement element) {
276 JavaElementDelta delta = new JavaElementDelta(element);
277 delta.fKind = CHANGED;
278 delta.fChangeFlags |= F_CLOSED;
279 insertDeltaTree(element, delta);
282 * Creates the nested delta deltas based on the affected element
283 * its delta, and the root of this delta tree. Returns the root
284 * of the created delta tree.
286 protected JavaElementDelta createDeltaTree(IJavaElement element, JavaElementDelta delta) {
287 JavaElementDelta childDelta = delta;
288 ArrayList ancestors= getAncestors(element);
289 if (ancestors == null) {
290 if (this.equalsAndSameParent(delta.getElement(), getElement())) { // handle case of two jars that can be equals but not in the same project
291 // the element being changed is the root element
293 fChangeFlags = delta.fChangeFlags;
294 fMovedToHandle = delta.fMovedToHandle;
295 fMovedFromHandle = delta.fMovedFromHandle;
298 for (int i = 0, size = ancestors.size(); i < size; i++) {
299 IJavaElement ancestor = (IJavaElement) ancestors.get(i);
300 JavaElementDelta ancestorDelta = new JavaElementDelta(ancestor);
301 ancestorDelta.addAffectedChild(childDelta);
302 childDelta = ancestorDelta;
308 * Returns whether the two java elements are equals and have the same parent.
310 protected boolean equalsAndSameParent(IJavaElement e1, IJavaElement e2) {
311 IJavaElement parent1;
312 return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent());
315 * Returns the <code>JavaElementDelta</code> for the given element
316 * in the delta tree, or null, if no delta for the given element is found.
318 protected JavaElementDelta find(IJavaElement e) {
319 if (this.equalsAndSameParent(fChangedElement, e)) { // handle case of two jars that can be equals but not in the same project
322 for (int i = 0; i < fAffectedChildren.length; i++) {
323 JavaElementDelta delta = ((JavaElementDelta)fAffectedChildren[i]).find(e);
332 * Mark this delta as a fine-grained delta.
334 public void fineGrained() {
335 if (fKind == 0) { // if not set yet
338 fChangeFlags |= F_FINE_GRAINED;
341 * @see IJavaElementDelta
343 public IJavaElementDelta[] getAddedChildren() {
344 return getChildrenOfType(ADDED);
347 * @see IJavaElementDelta
349 public IJavaElementDelta[] getAffectedChildren() {
350 return fAffectedChildren;
353 * Returns a collection of all the parents of this element up to (but
354 * not including) the root of this tree in bottom-up order. If the given
355 * element is not a descendant of the root of this tree, <code>null</code>
358 private ArrayList getAncestors(IJavaElement element) {
359 IJavaElement parent = element.getParent();
360 if (parent == null) {
363 ArrayList parents = new ArrayList();
364 while (!parent.equals(fChangedElement)) {
366 parent = parent.getParent();
367 if (parent == null) {
371 parents.trimToSize();
375 * @see IJavaElementDelta
377 public IJavaElementDelta[] getChangedChildren() {
378 return getChildrenOfType(CHANGED);
381 * @see IJavaElementDelta
383 protected IJavaElementDelta[] getChildrenOfType(int type) {
384 int length = fAffectedChildren.length;
386 return new IJavaElementDelta[] {};
388 ArrayList children= new ArrayList(length);
389 for (int i = 0; i < length; i++) {
390 if (fAffectedChildren[i].getKind() == type) {
391 children.add(fAffectedChildren[i]);
395 IJavaElementDelta[] childrenOfType = new IJavaElementDelta[children.size()];
396 children.toArray(childrenOfType);
398 return childrenOfType;
401 * Returns the delta for a given element. Only looks below this
404 protected JavaElementDelta getDeltaFor(IJavaElement element) {
405 if (this.equalsAndSameParent(getElement(), element)) // handle case of two jars that can be equals but not in the same project
407 if (fAffectedChildren.length == 0)
409 int childrenCount = fAffectedChildren.length;
410 for (int i = 0; i < childrenCount; i++) {
411 JavaElementDelta delta = (JavaElementDelta)fAffectedChildren[i];
412 if (this.equalsAndSameParent(delta.getElement(), element)) { // handle case of two jars that can be equals but not in the same project
415 delta = ((JavaElementDelta)delta).getDeltaFor(element);
423 * @see IJavaElementDelta
425 public IJavaElement getElement() {
426 return fChangedElement;
429 * @see IJavaElementDelta
431 public int getFlags() {
435 * @see IJavaElementDelta
437 public int getKind() {
441 * @see IJavaElementDelta
443 public IJavaElement getMovedFromElement() {
444 return fMovedFromHandle;
447 * @see IJavaElementDelta
449 public IJavaElement getMovedToElement() {
450 return fMovedToHandle;
453 * @see IJavaElementDelta
455 public IJavaElementDelta[] getRemovedChildren() {
456 return getChildrenOfType(REMOVED);
459 * Return the collection of resource deltas. Return null if none.
461 public IResourceDelta[] getResourceDeltas() {
462 if (resourceDeltas == null) return null;
463 if (resourceDeltas.length != resourceDeltasCounter) {
464 System.arraycopy(resourceDeltas, 0, resourceDeltas = new IResourceDelta[resourceDeltasCounter], 0, resourceDeltasCounter);
466 return resourceDeltas;
469 * Adds the new element to a new array that contains all of the elements of the old array.
470 * Returns the new array.
472 protected IJavaElementDelta[] growAndAddToArray(IJavaElementDelta[] array, IJavaElementDelta addition) {
473 IJavaElementDelta[] old = array;
474 array = new IJavaElementDelta[old.length + 1];
475 System.arraycopy(old, 0, array, 0, old.length);
476 array[old.length] = addition;
480 * Creates the delta tree for the given element and delta, and then
481 * inserts the tree as an affected child of this node.
483 protected void insertDeltaTree(IJavaElement element, JavaElementDelta delta) {
484 JavaElementDelta childDelta= createDeltaTree(element, delta);
485 if (!this.equalsAndSameParent(element, getElement())) { // handle case of two jars that can be equals but not in the same project
486 addAffectedChild(childDelta);
490 * Creates the nested deltas resulting from an move operation.
491 * Convenience method for creating the "move from" delta.
492 * The constructor should be used to create the root delta
493 * and then the move operation should call this method.
495 public void movedFrom(IJavaElement movedFromElement, IJavaElement movedToElement) {
496 JavaElementDelta removedDelta = new JavaElementDelta(movedFromElement);
497 removedDelta.fKind = REMOVED;
498 removedDelta.fChangeFlags |= F_MOVED_TO;
499 removedDelta.fMovedToHandle = movedToElement;
500 insertDeltaTree(movedFromElement, removedDelta);
503 * Creates the nested deltas resulting from an move operation.
504 * Convenience method for creating the "move to" delta.
505 * The constructor should be used to create the root delta
506 * and then the move operation should call this method.
508 public void movedTo(IJavaElement movedToElement, IJavaElement movedFromElement) {
509 JavaElementDelta addedDelta = new JavaElementDelta(movedToElement);
510 addedDelta.fKind = ADDED;
511 addedDelta.fChangeFlags |= F_MOVED_FROM;
512 addedDelta.fMovedFromHandle = movedFromElement;
513 insertDeltaTree(movedToElement, addedDelta);
516 * Creates the nested deltas for an opened element.
518 public void opened(IJavaElement element) {
519 JavaElementDelta delta = new JavaElementDelta(element);
520 delta.fKind = CHANGED;
521 delta.fChangeFlags |= F_OPENED;
522 insertDeltaTree(element, delta);
525 * Removes the child delta from the collection of affected children.
527 protected void removeAffectedChild(JavaElementDelta child) {
529 if (fAffectedChildren != null) {
530 for (int i = 0; i < fAffectedChildren.length; i++) {
531 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
538 fAffectedChildren= removeAndShrinkArray(fAffectedChildren, index);
542 * Removes the element from the array.
543 * Returns the a new array which has shrunk.
545 protected IJavaElementDelta[] removeAndShrinkArray(IJavaElementDelta[] old, int index) {
546 IJavaElementDelta[] array = new IJavaElementDelta[old.length - 1];
548 System.arraycopy(old, 0, array, 0, index);
549 int rest = old.length - index - 1;
551 System.arraycopy(old, index + 1, array, index, rest);
555 * Creates the nested deltas resulting from an delete operation.
556 * Convenience method for creating removed deltas.
557 * The constructor should be used to create the root delta
558 * and then the delete operation should call this method.
560 public void removed(IJavaElement element) {
561 JavaElementDelta removedDelta= new JavaElementDelta(element);
562 insertDeltaTree(element, removedDelta);
563 JavaElementDelta actualDelta = getDeltaFor(element);
564 if (actualDelta != null) {
565 actualDelta.fKind = REMOVED;
566 actualDelta.fChangeFlags = 0;
567 actualDelta.fAffectedChildren = fgEmptyDelta;
571 * Creates the nested deltas resulting from a change operation.
572 * Convenience method for creating change deltas.
573 * The constructor should be used to create the root delta
574 * and then a change operation should call this method.
576 public void sourceAttached(IJavaElement element) {
577 JavaElementDelta attachedDelta = new JavaElementDelta(element);
578 attachedDelta.fKind = CHANGED;
579 attachedDelta.fChangeFlags |= F_SOURCEATTACHED;
580 insertDeltaTree(element, attachedDelta);
583 * Creates the nested deltas resulting from a change operation.
584 * Convenience method for creating change deltas.
585 * The constructor should be used to create the root delta
586 * and then a change operation should call this method.
588 public void sourceDetached(IJavaElement element) {
589 JavaElementDelta detachedDelta = new JavaElementDelta(element);
590 detachedDelta.fKind = CHANGED;
591 detachedDelta.fChangeFlags |= F_SOURCEDETACHED;
592 insertDeltaTree(element, detachedDelta);
595 * Returns a string representation of this delta's
596 * structure suitable for debug purposes.
600 public String toDebugString(int depth) {
601 StringBuffer buffer = new StringBuffer();
602 for (int i= 0; i < depth; i++) {
605 buffer.append(((JavaElement)getElement()).toDebugString());
606 buffer.append("["); //$NON-NLS-1$
608 case IJavaElementDelta.ADDED :
611 case IJavaElementDelta.REMOVED :
614 case IJavaElementDelta.CHANGED :
621 buffer.append("]: {"); //$NON-NLS-1$
622 int changeFlags = getFlags();
623 boolean prev = false;
624 if ((changeFlags & IJavaElementDelta.F_CHILDREN) != 0) {
626 buffer.append(" | "); //$NON-NLS-1$
627 buffer.append("CHILDREN"); //$NON-NLS-1$
630 if ((changeFlags & IJavaElementDelta.F_CONTENT) != 0) {
632 buffer.append(" | "); //$NON-NLS-1$
633 buffer.append("CONTENT"); //$NON-NLS-1$
636 if ((changeFlags & IJavaElementDelta.F_MOVED_FROM) != 0) {
638 buffer.append(" | "); //$NON-NLS-1$
639 buffer.append("MOVED_FROM(" + ((JavaElement)getMovedFromElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
642 if ((changeFlags & IJavaElementDelta.F_MOVED_TO) != 0) {
644 buffer.append(" | "); //$NON-NLS-1$
645 buffer.append("MOVED_TO(" + ((JavaElement)getMovedToElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
648 if ((changeFlags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) {
650 buffer.append(" | "); //$NON-NLS-1$
651 buffer.append("ADDED TO CLASSPATH"); //$NON-NLS-1$
654 if ((changeFlags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) {
656 buffer.append(" | "); //$NON-NLS-1$
657 buffer.append("REMOVED FROM CLASSPATH"); //$NON-NLS-1$
660 if ((changeFlags & IJavaElementDelta.F_REORDER) != 0) {
662 buffer.append(" | "); //$NON-NLS-1$
663 buffer.append("REORDERED"); //$NON-NLS-1$
666 if ((changeFlags & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) {
668 buffer.append(" | "); //$NON-NLS-1$
669 buffer.append("ARCHIVE CONTENT CHANGED"); //$NON-NLS-1$
672 if ((changeFlags & IJavaElementDelta.F_SOURCEATTACHED) != 0) {
674 buffer.append(" | "); //$NON-NLS-1$
675 buffer.append("SOURCE ATTACHED"); //$NON-NLS-1$
678 if ((changeFlags & IJavaElementDelta.F_SOURCEDETACHED) != 0) {
680 buffer.append(" | "); //$NON-NLS-1$
681 buffer.append("SOURCE DETACHED"); //$NON-NLS-1$
684 if ((changeFlags & IJavaElementDelta.F_MODIFIERS) != 0) {
686 buffer.append(" | "); //$NON-NLS-1$
687 buffer.append("MODIFIERS CHANGED"); //$NON-NLS-1$
690 if ((changeFlags & IJavaElementDelta.F_SUPER_TYPES) != 0) {
692 buffer.append(" | "); //$NON-NLS-1$
693 buffer.append("SUPER TYPES CHANGED"); //$NON-NLS-1$
696 if ((changeFlags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
698 buffer.append(" | "); //$NON-NLS-1$
699 buffer.append("FINE GRAINED"); //$NON-NLS-1$
702 buffer.append("}"); //$NON-NLS-1$
703 IJavaElementDelta[] children = getAffectedChildren();
704 if (children != null) {
705 for (int i = 0; i < children.length; ++i) {
706 buffer.append("\n"); //$NON-NLS-1$
707 buffer.append(((JavaElementDelta) children[i]).toDebugString(depth + 1));
710 for (int i = 0; i < resourceDeltasCounter; i++) {
711 buffer.append("\n");//$NON-NLS-1$
712 for (int j = 0; j < depth+1; j++) {
715 IResourceDelta resourceDelta = resourceDeltas[i];
716 buffer.append(resourceDelta.toString());
717 buffer.append("["); //$NON-NLS-1$
718 switch (resourceDelta.getKind()) {
719 case IResourceDelta.ADDED :
722 case IResourceDelta.REMOVED :
725 case IResourceDelta.CHANGED :
732 buffer.append("]"); //$NON-NLS-1$
734 return buffer.toString();
737 * Returns a string representation of this delta's
738 * structure suitable for debug purposes.
740 public String toString() {
741 return toDebugString(0);