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;
 
  14 import java.util.HashMap;
 
  15 import java.util.Iterator;
 
  18 import net.sourceforge.phpdt.core.IJavaElement;
 
  19 import net.sourceforge.phpdt.core.IJavaElementDelta;
 
  20 import net.sourceforge.phpdt.core.IParent;
 
  21 import net.sourceforge.phpdt.core.JavaModelException;
 
  22 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  25  * A java element delta biulder creates a java element delta on a java element
 
  26  * between the version of the java element at the time the comparator was
 
  27  * created and the current version of the java element.
 
  29  * It performs this operation by locally caching the contents of the java
 
  30  * element when it is created. When the method createDeltas() is called, it
 
  31  * creates a delta over the cached contents and the new contents.
 
  33 public class JavaElementDeltaBuilder {
 
  35          * The java element handle
 
  37         IJavaElement javaElement;
 
  40          * The maximum depth in the java element children we should look into
 
  42         int maxDepth = Integer.MAX_VALUE;
 
  45          * The old handle to info relationships
 
  50          * The old position info
 
  55          * The new position info
 
  62         JavaElementDelta delta;
 
  65          * List of added elements
 
  70          * List of removed elements
 
  75          * Doubly linked list item
 
  78                 public IJavaElement previous;
 
  80                 public IJavaElement next;
 
  82                 public ListItem(IJavaElement previous, IJavaElement next) {
 
  83                         this.previous = previous;
 
  89          * Creates a java element comparator on a java element looking as deep as
 
  92         public JavaElementDeltaBuilder(IJavaElement javaElement) {
 
  93                 this.javaElement = javaElement;
 
  95                 this.recordElementInfo(javaElement, (JavaModel) this.javaElement
 
 100          * Creates a java element comparator on a java element looking only
 
 101          * 'maxDepth' levels deep.
 
 103         public JavaElementDeltaBuilder(IJavaElement javaElement, int maxDepth) {
 
 104                 this.javaElement = javaElement;
 
 105                 this.maxDepth = maxDepth;
 
 107                 this.recordElementInfo(javaElement, (JavaModel) this.javaElement
 
 112          * Repairs the positioning information after an element has been added
 
 114         private void added(IJavaElement element) {
 
 115                 this.added.add(element);
 
 116                 ListItem current = this.getNewPosition(element);
 
 117                 ListItem previous = null, next = null;
 
 118                 if (current.previous != null)
 
 119                         previous = this.getNewPosition(current.previous);
 
 120                 if (current.next != null)
 
 121                         next = this.getNewPosition(current.next);
 
 122                 if (previous != null)
 
 123                         previous.next = current.next;
 
 125                         next.previous = current.previous;
 
 129          * Builds the java element deltas between the old content of the compilation
 
 130          * unit and its new content.
 
 132         public void buildDeltas() {
 
 133                 this.recordNewPositions(this.javaElement, 0);
 
 134                 this.findAdditions(this.javaElement, 0);
 
 135                 this.findDeletions();
 
 136                 this.findChangesInPositioning(this.javaElement, 0);
 
 137                 this.trimDelta(this.delta);
 
 138                 if (this.delta.getAffectedChildren().length == 0) {
 
 139                         // this is a fine grained but not children affected -> mark as
 
 141                         this.delta.contentChanged();
 
 146          * Finds elements which have been added or changed.
 
 148         private void findAdditions(IJavaElement newElement, int depth) {
 
 149                 JavaElementInfo oldInfo = this.getElementInfo(newElement);
 
 150                 if (oldInfo == null && depth < this.maxDepth) {
 
 151                         this.delta.added(newElement);
 
 154                         this.removeElementInfo(newElement);
 
 157                 if (depth >= this.maxDepth) {
 
 158                         // mark element as changed
 
 159                         this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
 
 163                 JavaElementInfo newInfo = null;
 
 165                         newInfo = (JavaElementInfo) ((JavaElement) newElement)
 
 167                 } catch (JavaModelException npe) {
 
 171                 this.findContentChange(oldInfo, newInfo, newElement);
 
 173                 if (oldInfo != null && newElement instanceof IParent) {
 
 175                         IJavaElement[] children = newInfo.getChildren();
 
 176                         if (children != null) {
 
 177                                 int length = children.length;
 
 178                                 for (int i = 0; i < length; i++) {
 
 179                                         this.findAdditions(children[i], depth + 1);
 
 186          * Looks for changed positioning of elements.
 
 188         private void findChangesInPositioning(IJavaElement element, int depth) {
 
 189                 if (depth >= this.maxDepth || this.added.contains(element)
 
 190                                 || this.removed.contains(element))
 
 193                 if (!isPositionedCorrectly(element)) {
 
 194                         this.delta.changed(element, IJavaElementDelta.F_REORDER);
 
 197                 if (element instanceof IParent) {
 
 198                         JavaElementInfo info = null;
 
 200                                 info = (JavaElementInfo) ((JavaElement) element)
 
 202                         } catch (JavaModelException npe) {
 
 206                         IJavaElement[] children = info.getChildren();
 
 207                         if (children != null) {
 
 208                                 int length = children.length;
 
 209                                 for (int i = 0; i < length; i++) {
 
 210                                         this.findChangesInPositioning(children[i], depth + 1);
 
 217          * The elements are equivalent, but might have content changes.
 
 219         private void findContentChange(JavaElementInfo oldInfo,
 
 220                         JavaElementInfo newInfo, IJavaElement newElement) {
 
 221                 if (oldInfo instanceof MemberElementInfo
 
 222                                 && newInfo instanceof MemberElementInfo) {
 
 223                         if (((MemberElementInfo) oldInfo).getModifiers() != ((MemberElementInfo) newInfo)
 
 225                                 this.delta.changed(newElement, IJavaElementDelta.F_MODIFIERS);
 
 226                         } else if (oldInfo instanceof SourceMethodElementInfo
 
 227                                         && newInfo instanceof SourceMethodElementInfo) {
 
 229                                                 .equals(((SourceMethodElementInfo) oldInfo)
 
 230                                                                 .getReturnTypeName(),
 
 231                                                                 ((SourceMethodElementInfo) newInfo)
 
 232                                                                                 .getReturnTypeName())) {
 
 233                                         this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
 
 235                         } else if (oldInfo instanceof SourceFieldElementInfo
 
 236                                         && newInfo instanceof SourceFieldElementInfo) {
 
 237                                 if (!CharOperation.equals(((SourceFieldElementInfo) oldInfo)
 
 238                                                 .getTypeName(), ((SourceFieldElementInfo) newInfo)
 
 240                                         this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
 
 244                 if (oldInfo instanceof SourceTypeElementInfo
 
 245                                 && newInfo instanceof SourceTypeElementInfo) {
 
 246                         SourceTypeElementInfo oldSourceTypeInfo = (SourceTypeElementInfo) oldInfo;
 
 247                         SourceTypeElementInfo newSourceTypeInfo = (SourceTypeElementInfo) newInfo;
 
 248                         if (!CharOperation.equals(oldSourceTypeInfo.getSuperclassName(),
 
 249                                         newSourceTypeInfo.getSuperclassName())
 
 250                                         || !CharOperation.equals(oldSourceTypeInfo
 
 251                                                         .getInterfaceNames(), newSourceTypeInfo
 
 252                                                         .getInterfaceNames())) {
 
 253                                 this.delta.changed(newElement, IJavaElementDelta.F_SUPER_TYPES);
 
 259          * Adds removed deltas for any handles left in the table
 
 261         private void findDeletions() {
 
 262                 Iterator iter = this.infos.keySet().iterator();
 
 263                 while (iter.hasNext()) {
 
 264                         IJavaElement element = (IJavaElement) iter.next();
 
 265                         this.delta.removed(element);
 
 266                         this.removed(element);
 
 270         private JavaElementInfo getElementInfo(IJavaElement element) {
 
 271                 return (JavaElementInfo) this.infos.get(element);
 
 274         private ListItem getNewPosition(IJavaElement element) {
 
 275                 return (ListItem) this.newPositions.get(element);
 
 278         private ListItem getOldPosition(IJavaElement element) {
 
 279                 return (ListItem) this.oldPositions.get(element);
 
 282         private void initialize() {
 
 283                 this.infos = new HashMap(20);
 
 284                 this.oldPositions = new HashMap(20);
 
 285                 this.newPositions = new HashMap(20);
 
 286                 this.putOldPosition(this.javaElement, new ListItem(null, null));
 
 287                 this.putNewPosition(this.javaElement, new ListItem(null, null));
 
 288                 this.delta = new JavaElementDelta(javaElement);
 
 290                 // if building a delta on a compilation unit or below,
 
 291                 // it's a fine grained delta
 
 292                 if (javaElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
 
 293                         this.delta.fineGrained();
 
 296                 this.added = new ArrayList(5);
 
 297                 this.removed = new ArrayList(5);
 
 301          * Inserts position information for the elements into the new or old
 
 304         private void insertPositions(IJavaElement[] elements, boolean isNew) {
 
 305                 int length = elements.length;
 
 306                 IJavaElement previous = null, current = null, next = (length > 0) ? elements[0]
 
 308                 for (int i = 0; i < length; i++) {
 
 311                         next = (i + 1 < length) ? elements[i + 1] : null;
 
 313                                 this.putNewPosition(current, new ListItem(previous, next));
 
 315                                 this.putOldPosition(current, new ListItem(previous, next));
 
 321          * Returns whether the elements position has not changed.
 
 323         private boolean isPositionedCorrectly(IJavaElement element) {
 
 324                 ListItem oldListItem = this.getOldPosition(element);
 
 325                 if (oldListItem == null)
 
 328                 ListItem newListItem = this.getNewPosition(element);
 
 329                 if (newListItem == null)
 
 332                 IJavaElement oldPrevious = oldListItem.previous;
 
 333                 IJavaElement newPrevious = newListItem.previous;
 
 334                 if (oldPrevious == null) {
 
 335                         return newPrevious == null;
 
 337                         return oldPrevious.equals(newPrevious);
 
 341         private void putElementInfo(IJavaElement element, JavaElementInfo info) {
 
 342                 this.infos.put(element, info);
 
 345         private void putNewPosition(IJavaElement element, ListItem position) {
 
 346                 this.newPositions.put(element, position);
 
 349         private void putOldPosition(IJavaElement element, ListItem position) {
 
 350                 this.oldPositions.put(element, position);
 
 354          * Records this elements info, and attempts to record the info for the
 
 357         private void recordElementInfo(IJavaElement element, JavaModel model,
 
 359                 if (depth >= this.maxDepth) {
 
 362                 JavaElementInfo info = null;// (JavaElementInfo)JavaModelManager.getJavaModelManager().getInfo(element);
 
 363                 if (info == null) // no longer in the java model.
 
 365                 this.putElementInfo(element, info);
 
 367                 if (element instanceof IParent) {
 
 368                         IJavaElement[] children = info.getChildren();
 
 369                         if (children != null) {
 
 370                                 insertPositions(children, false);
 
 371                                 for (int i = 0, length = children.length; i < length; i++)
 
 372                                         recordElementInfo(children[i], model, depth + 1);
 
 378          * Fills the newPositions hashtable with the new position information
 
 380         private void recordNewPositions(IJavaElement newElement, int depth) {
 
 381                 if (depth < this.maxDepth && newElement instanceof IParent) {
 
 382                         JavaElementInfo info = null;
 
 384                                 info = (JavaElementInfo) ((JavaElement) newElement)
 
 386                         } catch (JavaModelException npe) {
 
 390                         IJavaElement[] children = info.getChildren();
 
 391                         if (children != null) {
 
 392                                 insertPositions(children, true);
 
 393                                 for (int i = 0, length = children.length; i < length; i++) {
 
 394                                         recordNewPositions(children[i], depth + 1);
 
 401          * Repairs the positioning information after an element has been removed
 
 403         private void removed(IJavaElement element) {
 
 404                 this.removed.add(element);
 
 405                 ListItem current = this.getOldPosition(element);
 
 406                 ListItem previous = null, next = null;
 
 407                 if (current.previous != null)
 
 408                         previous = this.getOldPosition(current.previous);
 
 409                 if (current.next != null)
 
 410                         next = this.getOldPosition(current.next);
 
 411                 if (previous != null)
 
 412                         previous.next = current.next;
 
 414                         next.previous = current.previous;
 
 418         private void removeElementInfo(IJavaElement element) {
 
 419                 this.infos.remove(element);
 
 422         public String toString() {
 
 423                 StringBuffer buffer = new StringBuffer();
 
 424                 buffer.append("Built delta:\n"); //$NON-NLS-1$
 
 425                 buffer.append(this.delta.toString());
 
 426                 return buffer.toString();
 
 430          * Trims deletion deltas to only report the highest level of deletion
 
 432         private void trimDelta(JavaElementDelta delta) {
 
 433                 if (delta.getKind() == IJavaElementDelta.REMOVED) {
 
 434                         IJavaElementDelta[] children = delta.getAffectedChildren();
 
 435                         for (int i = 0, length = children.length; i < length; i++) {
 
 436                                 delta.removeAffectedChild((JavaElementDelta) children[i]);
 
 439                         IJavaElementDelta[] children = delta.getAffectedChildren();
 
 440                         for (int i = 0, length = children.length; i < length; i++) {
 
 441                                 trimDelta((JavaElementDelta) children[i]);