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 = (JavaElementInfo) JavaModelManager
363 .getJavaModelManager().getInfo(element);
364 if (info == null) // no longer in the java model.
366 this.putElementInfo(element, info);
368 if (element instanceof IParent) {
369 IJavaElement[] children = info.getChildren();
370 if (children != null) {
371 insertPositions(children, false);
372 for (int i = 0, length = children.length; i < length; i++)
373 recordElementInfo(children[i], model, depth + 1);
379 * Fills the newPositions hashtable with the new position information
381 private void recordNewPositions(IJavaElement newElement, int depth) {
382 if (depth < this.maxDepth && newElement instanceof IParent) {
383 JavaElementInfo info = null;
385 info = (JavaElementInfo) ((JavaElement) newElement)
387 } catch (JavaModelException npe) {
391 IJavaElement[] children = info.getChildren();
392 if (children != null) {
393 insertPositions(children, true);
394 for (int i = 0, length = children.length; i < length; i++) {
395 recordNewPositions(children[i], depth + 1);
402 * Repairs the positioning information after an element has been removed
404 private void removed(IJavaElement element) {
405 this.removed.add(element);
406 ListItem current = this.getOldPosition(element);
407 ListItem previous = null, next = null;
408 if (current.previous != null)
409 previous = this.getOldPosition(current.previous);
410 if (current.next != null)
411 next = this.getOldPosition(current.next);
412 if (previous != null)
413 previous.next = current.next;
415 next.previous = current.previous;
419 private void removeElementInfo(IJavaElement element) {
420 this.infos.remove(element);
423 public String toString() {
424 StringBuffer buffer = new StringBuffer();
425 buffer.append("Built delta:\n"); //$NON-NLS-1$
426 buffer.append(this.delta.toString());
427 return buffer.toString();
431 * Trims deletion deltas to only report the highest level of deletion
433 private void trimDelta(JavaElementDelta delta) {
434 if (delta.getKind() == IJavaElementDelta.REMOVED) {
435 IJavaElementDelta[] children = delta.getAffectedChildren();
436 for (int i = 0, length = children.length; i < length; i++) {
437 delta.removeAffectedChild((JavaElementDelta) children[i]);
440 IJavaElementDelta[] children = delta.getAffectedChildren();
441 for (int i = 0, length = children.length; i < length; i++) {
442 trimDelta((JavaElementDelta) children[i]);