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;
20 import net.sourceforge.phpdt.internal.core.SimpleDelta;
24 * @see IJavaElementDelta
26 public class JavaElementDelta extends SimpleDelta implements IJavaElementDelta {
28 * The element that this delta describes the change to.
31 protected IJavaElement fChangedElement;
35 private int fKind = 0;
39 private int fChangeFlags = 0;
41 * @see #getAffectedChildren()
43 protected IJavaElementDelta[] fAffectedChildren = fgEmptyDelta;
46 * Collection of resource deltas that correspond to non java resources deltas.
48 protected IResourceDelta[] resourceDeltas = null;
51 * Counter of resource deltas
53 protected int resourceDeltasCounter;
55 * @see #getMovedFromHandle()
57 protected IJavaElement fMovedFromHandle = null;
59 * @see #getMovedToHandle()
61 protected IJavaElement fMovedToHandle = null;
63 * Empty array of IJavaElementDelta
65 protected static IJavaElementDelta[] fgEmptyDelta= new IJavaElementDelta[] {};
67 * Creates the root delta. To create the nested delta
68 * hierarchies use the following convenience methods. The root
69 * delta can be created at any level (for example: project, package root,
70 * package fragment...).
72 * <li><code>added(IJavaElement)</code>
73 * <li><code>changed(IJavaElement)</code>
74 * <li><code>moved(IJavaElement, IJavaElement)</code>
75 * <li><code>removed(IJavaElement)</code>
76 * <li><code>renamed(IJavaElement, IJavaElement)</code>
79 public JavaElementDelta(IJavaElement element) {
81 fChangedElement = element;
84 * Adds the child delta to the collection of affected children. If the
85 * child is already in the collection, walk down the hierarchy.
87 protected void addAffectedChild(JavaElementDelta child) {
91 // no need to add a child if this parent is added or removed
94 fChangeFlags |= F_CHILDREN;
98 fChangeFlags |= F_CHILDREN;
101 // if a child delta is added to a compilation unit delta or below,
102 // it's a fine grained delta
103 if (fChangedElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
107 if (fAffectedChildren.length == 0) {
108 fAffectedChildren = new IJavaElementDelta[] {child};
111 IJavaElementDelta existingChild = null;
112 int existingChildIndex = -1;
113 if (fAffectedChildren != null) {
114 for (int i = 0; i < fAffectedChildren.length; i++) {
115 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
116 existingChild = fAffectedChildren[i];
117 existingChildIndex = i;
122 if (existingChild == null) { //new affected child
123 fAffectedChildren= growAndAddToArray(fAffectedChildren, child);
125 switch (existingChild.getKind()) {
127 switch (child.getKind()) {
128 case ADDED: // child was added then added -> it is added
129 case CHANGED: // child was added then changed -> it is added
131 case REMOVED: // child was added then removed -> noop
132 fAffectedChildren = this.removeAndShrinkArray(fAffectedChildren, existingChildIndex);
137 switch (child.getKind()) {
138 case ADDED: // child was removed then added -> it is changed
139 child.fKind = CHANGED;
140 fAffectedChildren[existingChildIndex] = child;
142 case CHANGED: // child was removed then changed -> it is removed
143 case REMOVED: // child was removed then removed -> it is removed
148 switch (child.getKind()) {
149 case ADDED: // child was changed then added -> it is added
150 case REMOVED: // child was changed then removed -> it is removed
151 fAffectedChildren[existingChildIndex] = child;
153 case CHANGED: // child was changed then changed -> it is changed
154 IJavaElementDelta[] children = child.getAffectedChildren();
155 for (int i = 0; i < children.length; i++) {
156 JavaElementDelta childsChild = (JavaElementDelta) children[i];
157 ((JavaElementDelta) existingChild).addAffectedChild(childsChild);
160 // update flags if needed
161 switch (((JavaElementDelta) existingChild).fChangeFlags) {
162 case F_ADDED_TO_CLASSPATH:
163 case F_REMOVED_FROM_CLASSPATH:
164 case F_SOURCEATTACHED:
165 case F_SOURCEDETACHED:
166 ((JavaElementDelta) existingChild).fChangeFlags |= ((JavaElementDelta) child).fChangeFlags;
170 // add the non-java resource deltas if needed
171 // note that the child delta always takes precedence over this existing child delta
172 // as non-java resource deltas are always created last (by the DeltaProcessor)
173 IResourceDelta[] resDeltas = child.getResourceDeltas();
174 if (resDeltas != null) {
175 ((JavaElementDelta)existingChild).resourceDeltas = resDeltas;
176 ((JavaElementDelta)existingChild).resourceDeltasCounter = child.resourceDeltasCounter;
182 // unknown -> existing child becomes the child with the existing child's flags
183 int flags = existingChild.getFlags();
184 fAffectedChildren[existingChildIndex] = child;
185 child.fChangeFlags |= flags;
190 // * Creates the nested deltas resulting from an add operation.
191 // * Convenience method for creating add deltas.
192 // * The constructor should be used to create the root delta
193 // * and then an add operation should call this method.
195 //public void added(IJavaElement element) {
196 // JavaElementDelta addedDelta = new JavaElementDelta(element);
197 // addedDelta.fKind = ADDED;
198 // insertDeltaTree(element, addedDelta);
201 * Creates the nested deltas resulting from an add operation.
202 * Convenience method for creating add deltas.
203 * The constructor should be used to create the root delta
204 * and then an add operation should call this method.
206 public void added(IJavaElement element) {
209 public void added(IJavaElement element, int flags) {
210 JavaElementDelta addedDelta = new JavaElementDelta(element);
212 addedDelta.changeFlags |= flags;
213 insertDeltaTree(element, addedDelta);
216 * Adds the child delta to the collection of affected children. If the
217 * child is already in the collection, walk down the hierarchy.
219 protected void addResourceDelta(IResourceDelta child) {
223 // no need to add a child if this parent is added or removed
226 fChangeFlags |= F_CONTENT;
230 fChangeFlags |= F_CONTENT;
232 if (resourceDeltas == null) {
233 resourceDeltas = new IResourceDelta[5];
234 resourceDeltas[resourceDeltasCounter++] = child;
237 if (resourceDeltas.length == resourceDeltasCounter) {
239 System.arraycopy(resourceDeltas, 0, (resourceDeltas = new IResourceDelta[resourceDeltasCounter * 2]), 0, resourceDeltasCounter);
241 resourceDeltas[resourceDeltasCounter++] = child;
244 * Creates the nested deltas resulting from a change operation.
245 * Convenience method for creating change deltas.
246 * The constructor should be used to create the root delta
247 * and then a change operation should call this method.
249 public void changed(IJavaElement element, int changeFlag) {
250 JavaElementDelta changedDelta = new JavaElementDelta(element);
251 changedDelta.fKind = CHANGED;
252 changedDelta.fChangeFlags |= changeFlag;
253 insertDeltaTree(element, changedDelta);
256 * Mark this delta as a content changed delta.
258 public void contentChanged() {
259 fChangeFlags |= F_CONTENT;
262 // * Clone this delta so that its elements are rooted at the given project.
264 //public IJavaElementDelta clone(IJavaProject project) {
265 // JavaElementDelta clone =
266 // new JavaElementDelta(((JavaElement)fChangedElement).rootedAt(project));
267 // if (fAffectedChildren != fgEmptyDelta) {
268 // int length = fAffectedChildren.length;
269 // IJavaElementDelta[] cloneChildren = new IJavaElementDelta[length];
270 // for (int i= 0; i < length; i++) {
271 // cloneChildren[i] = ((JavaElementDelta)fAffectedChildren[i]).clone(project);
273 // clone.fAffectedChildren = cloneChildren;
275 // clone.fChangeFlags = fChangeFlags;
276 // clone.fKind = fKind;
277 // if (fMovedFromHandle != null) {
278 // clone.fMovedFromHandle = ((JavaElement)fMovedFromHandle).rootedAt(project);
280 // if (fMovedToHandle != null) {
281 // clone.fMovedToHandle = ((JavaElement)fMovedToHandle).rootedAt(project);
283 // clone.resourceDeltas = this.resourceDeltas;
284 // clone.resourceDeltasCounter = this.resourceDeltasCounter;
289 * Creates the nested deltas for a closed element.
291 public void closed(IJavaElement element) {
292 JavaElementDelta delta = new JavaElementDelta(element);
293 delta.fKind = CHANGED;
294 delta.fChangeFlags |= F_CLOSED;
295 insertDeltaTree(element, delta);
298 * Creates the nested delta deltas based on the affected element
299 * its delta, and the root of this delta tree. Returns the root
300 * of the created delta tree.
302 protected JavaElementDelta createDeltaTree(IJavaElement element, JavaElementDelta delta) {
303 JavaElementDelta childDelta = delta;
304 ArrayList ancestors= getAncestors(element);
305 if (ancestors == null) {
306 if (this.equalsAndSameParent(delta.getElement(), getElement())) { // handle case of two jars that can be equals but not in the same project
307 // the element being changed is the root element
309 fChangeFlags = delta.fChangeFlags;
310 fMovedToHandle = delta.fMovedToHandle;
311 fMovedFromHandle = delta.fMovedFromHandle;
314 for (int i = 0, size = ancestors.size(); i < size; i++) {
315 IJavaElement ancestor = (IJavaElement) ancestors.get(i);
316 JavaElementDelta ancestorDelta = new JavaElementDelta(ancestor);
317 ancestorDelta.addAffectedChild(childDelta);
318 childDelta = ancestorDelta;
324 * Returns whether the two java elements are equals and have the same parent.
326 protected boolean equalsAndSameParent(IJavaElement e1, IJavaElement e2) {
327 IJavaElement parent1;
328 return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent());
331 * Returns the <code>JavaElementDelta</code> for the given element
332 * in the delta tree, or null, if no delta for the given element is found.
334 protected JavaElementDelta find(IJavaElement e) {
335 if (this.equalsAndSameParent(fChangedElement, e)) { // handle case of two jars that can be equals but not in the same project
338 for (int i = 0; i < fAffectedChildren.length; i++) {
339 JavaElementDelta delta = ((JavaElementDelta)fAffectedChildren[i]).find(e);
348 * Mark this delta as a fine-grained delta.
350 public void fineGrained() {
351 if (fKind == 0) { // if not set yet
354 fChangeFlags |= F_FINE_GRAINED;
357 * @see IJavaElementDelta
359 public IJavaElementDelta[] getAddedChildren() {
360 return getChildrenOfType(ADDED);
363 * @see IJavaElementDelta
365 public IJavaElementDelta[] getAffectedChildren() {
366 return fAffectedChildren;
369 * Returns a collection of all the parents of this element up to (but
370 * not including) the root of this tree in bottom-up order. If the given
371 * element is not a descendant of the root of this tree, <code>null</code>
374 private ArrayList getAncestors(IJavaElement element) {
375 IJavaElement parent = element.getParent();
376 if (parent == null) {
379 ArrayList parents = new ArrayList();
380 while (!parent.equals(fChangedElement)) {
382 parent = parent.getParent();
383 if (parent == null) {
387 parents.trimToSize();
391 * @see IJavaElementDelta
393 public IJavaElementDelta[] getChangedChildren() {
394 return getChildrenOfType(CHANGED);
397 * @see IJavaElementDelta
399 protected IJavaElementDelta[] getChildrenOfType(int type) {
400 int length = fAffectedChildren.length;
402 return new IJavaElementDelta[] {};
404 ArrayList children= new ArrayList(length);
405 for (int i = 0; i < length; i++) {
406 if (fAffectedChildren[i].getKind() == type) {
407 children.add(fAffectedChildren[i]);
411 IJavaElementDelta[] childrenOfType = new IJavaElementDelta[children.size()];
412 children.toArray(childrenOfType);
414 return childrenOfType;
417 * Returns the delta for a given element. Only looks below this
420 protected JavaElementDelta getDeltaFor(IJavaElement element) {
421 if (this.equalsAndSameParent(getElement(), element)) // handle case of two jars that can be equals but not in the same project
423 if (fAffectedChildren.length == 0)
425 int childrenCount = fAffectedChildren.length;
426 for (int i = 0; i < childrenCount; i++) {
427 JavaElementDelta delta = (JavaElementDelta)fAffectedChildren[i];
428 if (this.equalsAndSameParent(delta.getElement(), element)) { // handle case of two jars that can be equals but not in the same project
431 delta = ((JavaElementDelta)delta).getDeltaFor(element);
439 * @see IJavaElementDelta
441 public IJavaElement getElement() {
442 return fChangedElement;
445 * @see IJavaElementDelta
447 public int getFlags() {
451 * @see IJavaElementDelta
453 public int getKind() {
457 * @see IJavaElementDelta
459 public IJavaElement getMovedFromElement() {
460 return fMovedFromHandle;
463 * @see IJavaElementDelta
465 public IJavaElement getMovedToElement() {
466 return fMovedToHandle;
469 * @see IJavaElementDelta
471 public IJavaElementDelta[] getRemovedChildren() {
472 return getChildrenOfType(REMOVED);
475 * Return the collection of resource deltas. Return null if none.
477 public IResourceDelta[] getResourceDeltas() {
478 if (resourceDeltas == null) return null;
479 if (resourceDeltas.length != resourceDeltasCounter) {
480 System.arraycopy(resourceDeltas, 0, resourceDeltas = new IResourceDelta[resourceDeltasCounter], 0, resourceDeltasCounter);
482 return resourceDeltas;
485 * Adds the new element to a new array that contains all of the elements of the old array.
486 * Returns the new array.
488 protected IJavaElementDelta[] growAndAddToArray(IJavaElementDelta[] array, IJavaElementDelta addition) {
489 IJavaElementDelta[] old = array;
490 array = new IJavaElementDelta[old.length + 1];
491 System.arraycopy(old, 0, array, 0, old.length);
492 array[old.length] = addition;
496 * Creates the delta tree for the given element and delta, and then
497 * inserts the tree as an affected child of this node.
499 protected void insertDeltaTree(IJavaElement element, JavaElementDelta delta) {
500 JavaElementDelta childDelta= createDeltaTree(element, delta);
501 if (!this.equalsAndSameParent(element, getElement())) { // handle case of two jars that can be equals but not in the same project
502 addAffectedChild(childDelta);
506 * Creates the nested deltas resulting from an move operation.
507 * Convenience method for creating the "move from" delta.
508 * The constructor should be used to create the root delta
509 * and then the move operation should call this method.
511 public void movedFrom(IJavaElement movedFromElement, IJavaElement movedToElement) {
512 JavaElementDelta removedDelta = new JavaElementDelta(movedFromElement);
513 removedDelta.fKind = REMOVED;
514 removedDelta.fChangeFlags |= F_MOVED_TO;
515 removedDelta.fMovedToHandle = movedToElement;
516 insertDeltaTree(movedFromElement, removedDelta);
519 * Creates the nested deltas resulting from an move operation.
520 * Convenience method for creating the "move to" delta.
521 * The constructor should be used to create the root delta
522 * and then the move operation should call this method.
524 public void movedTo(IJavaElement movedToElement, IJavaElement movedFromElement) {
525 JavaElementDelta addedDelta = new JavaElementDelta(movedToElement);
526 addedDelta.fKind = ADDED;
527 addedDelta.fChangeFlags |= F_MOVED_FROM;
528 addedDelta.fMovedFromHandle = movedFromElement;
529 insertDeltaTree(movedToElement, addedDelta);
532 * Creates the nested deltas for an opened element.
534 public void opened(IJavaElement element) {
535 JavaElementDelta delta = new JavaElementDelta(element);
536 delta.fKind = CHANGED;
537 delta.fChangeFlags |= F_OPENED;
538 insertDeltaTree(element, delta);
541 * Removes the child delta from the collection of affected children.
543 protected void removeAffectedChild(JavaElementDelta child) {
545 if (fAffectedChildren != null) {
546 for (int i = 0; i < fAffectedChildren.length; i++) {
547 if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
554 fAffectedChildren= removeAndShrinkArray(fAffectedChildren, index);
558 * Removes the element from the array.
559 * Returns the a new array which has shrunk.
561 protected IJavaElementDelta[] removeAndShrinkArray(IJavaElementDelta[] old, int index) {
562 IJavaElementDelta[] array = new IJavaElementDelta[old.length - 1];
564 System.arraycopy(old, 0, array, 0, index);
565 int rest = old.length - index - 1;
567 System.arraycopy(old, index + 1, array, index, rest);
571 * Creates the nested deltas resulting from an delete operation.
572 * Convenience method for creating removed deltas.
573 * The constructor should be used to create the root delta
574 * and then the delete operation should call this method.
576 public void removed(IJavaElement element) {
579 public void removed(IJavaElement element, int flags) {
580 JavaElementDelta removedDelta= new JavaElementDelta(element);
581 insertDeltaTree(element, removedDelta);
582 JavaElementDelta actualDelta = getDeltaFor(element);
583 if (actualDelta != null) {
584 actualDelta.removed();
585 actualDelta.changeFlags |= flags;
586 actualDelta.fAffectedChildren = fgEmptyDelta;
591 * Creates the nested deltas resulting from a change operation.
592 * Convenience method for creating change deltas.
593 * The constructor should be used to create the root delta
594 * and then a change operation should call this method.
596 public void sourceAttached(IJavaElement element) {
597 JavaElementDelta attachedDelta = new JavaElementDelta(element);
598 attachedDelta.fKind = CHANGED;
599 attachedDelta.fChangeFlags |= F_SOURCEATTACHED;
600 insertDeltaTree(element, attachedDelta);
603 * Creates the nested deltas resulting from a change operation.
604 * Convenience method for creating change deltas.
605 * The constructor should be used to create the root delta
606 * and then a change operation should call this method.
608 public void sourceDetached(IJavaElement element) {
609 JavaElementDelta detachedDelta = new JavaElementDelta(element);
610 detachedDelta.fKind = CHANGED;
611 detachedDelta.fChangeFlags |= F_SOURCEDETACHED;
612 insertDeltaTree(element, detachedDelta);
615 * Returns a string representation of this delta's
616 * structure suitable for debug purposes.
620 public String toDebugString(int depth) {
621 StringBuffer buffer = new StringBuffer();
622 for (int i= 0; i < depth; i++) {
625 buffer.append(((JavaElement)getElement()).toDebugString());
626 buffer.append("["); //$NON-NLS-1$
628 case IJavaElementDelta.ADDED :
631 case IJavaElementDelta.REMOVED :
634 case IJavaElementDelta.CHANGED :
641 buffer.append("]: {"); //$NON-NLS-1$
642 int changeFlags = getFlags();
643 boolean prev = false;
644 if ((changeFlags & IJavaElementDelta.F_CHILDREN) != 0) {
646 buffer.append(" | "); //$NON-NLS-1$
647 buffer.append("CHILDREN"); //$NON-NLS-1$
650 if ((changeFlags & IJavaElementDelta.F_CONTENT) != 0) {
652 buffer.append(" | "); //$NON-NLS-1$
653 buffer.append("CONTENT"); //$NON-NLS-1$
656 if ((changeFlags & IJavaElementDelta.F_MOVED_FROM) != 0) {
658 buffer.append(" | "); //$NON-NLS-1$
659 buffer.append("MOVED_FROM(" + ((JavaElement)getMovedFromElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
662 if ((changeFlags & IJavaElementDelta.F_MOVED_TO) != 0) {
664 buffer.append(" | "); //$NON-NLS-1$
665 buffer.append("MOVED_TO(" + ((JavaElement)getMovedToElement()).toStringWithAncestors() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
668 if ((changeFlags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0) {
670 buffer.append(" | "); //$NON-NLS-1$
671 buffer.append("ADDED TO CLASSPATH"); //$NON-NLS-1$
674 if ((changeFlags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) {
676 buffer.append(" | "); //$NON-NLS-1$
677 buffer.append("REMOVED FROM CLASSPATH"); //$NON-NLS-1$
680 if ((changeFlags & IJavaElementDelta.F_REORDER) != 0) {
682 buffer.append(" | "); //$NON-NLS-1$
683 buffer.append("REORDERED"); //$NON-NLS-1$
686 if ((changeFlags & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) {
688 buffer.append(" | "); //$NON-NLS-1$
689 buffer.append("ARCHIVE CONTENT CHANGED"); //$NON-NLS-1$
692 if ((changeFlags & IJavaElementDelta.F_SOURCEATTACHED) != 0) {
694 buffer.append(" | "); //$NON-NLS-1$
695 buffer.append("SOURCE ATTACHED"); //$NON-NLS-1$
698 if ((changeFlags & IJavaElementDelta.F_SOURCEDETACHED) != 0) {
700 buffer.append(" | "); //$NON-NLS-1$
701 buffer.append("SOURCE DETACHED"); //$NON-NLS-1$
704 if ((changeFlags & IJavaElementDelta.F_MODIFIERS) != 0) {
706 buffer.append(" | "); //$NON-NLS-1$
707 buffer.append("MODIFIERS CHANGED"); //$NON-NLS-1$
710 if ((changeFlags & IJavaElementDelta.F_SUPER_TYPES) != 0) {
712 buffer.append(" | "); //$NON-NLS-1$
713 buffer.append("SUPER TYPES CHANGED"); //$NON-NLS-1$
716 if ((changeFlags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
718 buffer.append(" | "); //$NON-NLS-1$
719 buffer.append("FINE GRAINED"); //$NON-NLS-1$
722 buffer.append("}"); //$NON-NLS-1$
723 IJavaElementDelta[] children = getAffectedChildren();
724 if (children != null) {
725 for (int i = 0; i < children.length; ++i) {
726 buffer.append("\n"); //$NON-NLS-1$
727 buffer.append(((JavaElementDelta) children[i]).toDebugString(depth + 1));
730 for (int i = 0; i < resourceDeltasCounter; i++) {
731 buffer.append("\n");//$NON-NLS-1$
732 for (int j = 0; j < depth+1; j++) {
735 IResourceDelta resourceDelta = resourceDeltas[i];
736 buffer.append(resourceDelta.toString());
737 buffer.append("["); //$NON-NLS-1$
738 switch (resourceDelta.getKind()) {
739 case IResourceDelta.ADDED :
742 case IResourceDelta.REMOVED :
745 case IResourceDelta.CHANGED :
752 buffer.append("]"); //$NON-NLS-1$
754 return buffer.toString();
757 * Returns a string representation of this delta's
758 * structure suitable for debug purposes.
760 public String toString() {
761 return toDebugString(0);