KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > JavaElementDeltaBuilder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.core;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import org.eclipse.jdt.core.IJavaElement;
21 import org.eclipse.jdt.core.IJavaElementDelta;
22 import org.eclipse.jdt.core.IParent;
23 import org.eclipse.jdt.core.JavaModelException;
24 import org.eclipse.jdt.core.compiler.CharOperation;
25 import org.eclipse.jdt.internal.core.util.Util;
26
27 /**
28  * A java element delta biulder creates a java element delta on
29  * a java element between the version of the java element
30  * at the time the comparator was created and the current version
31  * of the java element.
32  *
33  * It performs this operation by locally caching the contents of
34  * the java element when it is created. When the method
35  * createDeltas() is called, it creates a delta over the cached
36  * contents and the new contents.
37  */

38 public class JavaElementDeltaBuilder {
39     /**
40      * The java element handle
41      */

42     IJavaElement javaElement;
43
44     /**
45      * The maximum depth in the java element children we should look into
46      */

47     int maxDepth = Integer.MAX_VALUE;
48
49     /**
50      * The old handle to info relationships
51      */

52     Map JavaDoc infos;
53
54     /**
55      * The old position info
56      */

57     Map JavaDoc oldPositions;
58
59     /**
60      * The new position info
61      */

62     Map JavaDoc newPositions;
63
64     /**
65      * Change delta
66      */

67     public JavaElementDelta delta = null;
68
69     /**
70      * List of added elements
71      */

72     ArrayList JavaDoc added;
73
74     /**
75      * List of removed elements
76      */

77     ArrayList JavaDoc removed;
78     
79     /**
80      * Doubly linked list item
81      */

82     static class ListItem {
83         public IJavaElement previous;
84         public IJavaElement next;
85
86         public ListItem(IJavaElement previous, IJavaElement next) {
87             this.previous = previous;
88             this.next = next;
89         }
90     }
91 /**
92  * Creates a java element comparator on a java element
93  * looking as deep as necessary.
94  */

95 public JavaElementDeltaBuilder(IJavaElement javaElement) {
96     this.javaElement = javaElement;
97     this.initialize();
98     this.recordElementInfo(
99         javaElement,
100         (JavaModel)this.javaElement.getJavaModel(),
101         0);
102 }
103 /**
104  * Creates a java element comparator on a java element
105  * looking only 'maxDepth' levels deep.
106  */

107 public JavaElementDeltaBuilder(IJavaElement javaElement, int maxDepth) {
108     this.javaElement = javaElement;
109     this.maxDepth = maxDepth;
110     this.initialize();
111     this.recordElementInfo(
112         javaElement,
113         (JavaModel)this.javaElement.getJavaModel(),
114         0);
115 }
116 /**
117  * Repairs the positioning information
118  * after an element has been added
119  */

120 private void added(IJavaElement element) {
121     this.added.add(element);
122     ListItem current = this.getNewPosition(element);
123     ListItem previous = null, next = null;
124     if (current.previous != null)
125         previous = this.getNewPosition(current.previous);
126     if (current.next != null)
127         next = this.getNewPosition(current.next);
128     if (previous != null)
129         previous.next = current.next;
130     if (next != null)
131         next.previous = current.previous;
132 }
133 /**
134  * Builds the java element deltas between the old content of the compilation
135  * unit and its new content.
136  */

137 public void buildDeltas() {
138     this.delta = new JavaElementDelta(this.javaElement);
139     // if building a delta on a compilation unit or below,
140
// it's a fine grained delta
141
if (this.javaElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
142         this.delta.fineGrained();
143     }
144     this.recordNewPositions(this.javaElement, 0);
145     this.findAdditions(this.javaElement, 0);
146     this.findDeletions();
147     this.findChangesInPositioning(this.javaElement, 0);
148     this.trimDelta(this.delta);
149     if (this.delta.getAffectedChildren().length == 0) {
150         // this is a fine grained but not children affected -> mark as content changed
151
this.delta.contentChanged();
152     }
153 }
154 private boolean equals(char[][][] first, char[][][] second) {
155     if (first == second)
156         return true;
157     if (first == null || second == null)
158         return false;
159     if (first.length != second.length)
160         return false;
161
162     for (int i = first.length; --i >= 0;)
163         if (!CharOperation.equals(first[i], second[i]))
164             return false;
165     return true;
166 }
167 /**
168  * Finds elements which have been added or changed.
169  */

170 private void findAdditions(IJavaElement newElement, int depth) {
171     JavaElementInfo oldInfo = this.getElementInfo(newElement);
172     if (oldInfo == null && depth < this.maxDepth) {
173         this.delta.added(newElement);
174         added(newElement);
175     } else {
176         this.removeElementInfo(newElement);
177     }
178     
179     if (depth >= this.maxDepth) {
180         // mark element as changed
181
this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
182         return;
183     }
184
185     JavaElementInfo newInfo = null;
186     try {
187         newInfo = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
188     } catch (JavaModelException npe) {
189         return;
190     }
191     
192     this.findContentChange(oldInfo, newInfo, newElement);
193         
194     if (oldInfo != null && newElement instanceof IParent) {
195
196         IJavaElement[] children = newInfo.getChildren();
197         if (children != null) {
198             int length = children.length;
199             for(int i = 0; i < length; i++) {
200                 this.findAdditions(children[i], depth + 1);
201             }
202         }
203     }
204 }
205 /**
206  * Looks for changed positioning of elements.
207  */

208 private void findChangesInPositioning(IJavaElement element, int depth) {
209     if (depth >= this.maxDepth || this.added.contains(element) || this.removed.contains(element))
210         return;
211         
212     if (!isPositionedCorrectly(element)) {
213         this.delta.changed(element, IJavaElementDelta.F_REORDER);
214     }
215     
216     if (element instanceof IParent) {
217         JavaElementInfo info = null;
218         try {
219             info = (JavaElementInfo)((JavaElement)element).getElementInfo();
220         } catch (JavaModelException npe) {
221             return;
222         }
223
224         IJavaElement[] children = info.getChildren();
225         if (children != null) {
226             int length = children.length;
227             for(int i = 0; i < length; i++) {
228                 this.findChangesInPositioning(children[i], depth + 1);
229             }
230         }
231     }
232 }
233 /**
234  * The elements are equivalent, but might have content changes.
235  */

236 private void findContentChange(JavaElementInfo oldInfo, JavaElementInfo newInfo, IJavaElement newElement) {
237     if (oldInfo instanceof MemberElementInfo && newInfo instanceof MemberElementInfo) {
238         if (((MemberElementInfo)oldInfo).getModifiers() != ((MemberElementInfo)newInfo).getModifiers()) {
239             this.delta.changed(newElement, IJavaElementDelta.F_MODIFIERS);
240         } else if (oldInfo instanceof SourceMethodElementInfo && newInfo instanceof SourceMethodElementInfo) {
241             SourceMethodElementInfo oldSourceMethodInfo = (SourceMethodElementInfo)oldInfo;
242             SourceMethodElementInfo newSourceMethodInfo = (SourceMethodElementInfo)newInfo;
243             if (!CharOperation.equals(oldSourceMethodInfo.getReturnTypeName(), newSourceMethodInfo.getReturnTypeName())
244                     || !CharOperation.equals(oldSourceMethodInfo.getTypeParameterNames(), newSourceMethodInfo.getTypeParameterNames())
245                     || !equals(oldSourceMethodInfo.getTypeParameterBounds(), newSourceMethodInfo.getTypeParameterBounds())) {
246                 this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
247             }
248         } else if (oldInfo instanceof SourceFieldElementInfo && newInfo instanceof SourceFieldElementInfo) {
249             if (!CharOperation.equals(
250                     ((SourceFieldElementInfo)oldInfo).getTypeName(),
251                     ((SourceFieldElementInfo)newInfo).getTypeName())) {
252                 this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
253             }
254         }
255     }
256     if (oldInfo instanceof SourceTypeElementInfo && newInfo instanceof SourceTypeElementInfo) {
257         SourceTypeElementInfo oldSourceTypeInfo = (SourceTypeElementInfo)oldInfo;
258         SourceTypeElementInfo newSourceTypeInfo = (SourceTypeElementInfo)newInfo;
259         if (!CharOperation.equals(oldSourceTypeInfo.getSuperclassName(), newSourceTypeInfo.getSuperclassName())
260                 || !CharOperation.equals(oldSourceTypeInfo.getInterfaceNames(), newSourceTypeInfo.getInterfaceNames())) {
261             this.delta.changed(newElement, IJavaElementDelta.F_SUPER_TYPES);
262         }
263         if (!CharOperation.equals(oldSourceTypeInfo.getTypeParameterNames(), newSourceTypeInfo.getTypeParameterNames())
264                 || !equals(oldSourceTypeInfo.getTypeParameterBounds(), newSourceTypeInfo.getTypeParameterBounds())) {
265             this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
266         }
267         HashMap JavaDoc oldTypeCategories = oldSourceTypeInfo.categories;
268         HashMap JavaDoc newTypeCategories = newSourceTypeInfo.categories;
269         if (oldTypeCategories != null) {
270             // take the union of old and new categories elements (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=125675)
271
Set JavaDoc elements;
272             if (newTypeCategories != null) {
273                 elements = new HashSet JavaDoc(oldTypeCategories.keySet());
274                 elements.addAll(newTypeCategories.keySet());
275             } else
276                 elements = oldTypeCategories.keySet();
277             Iterator JavaDoc iterator = elements.iterator();
278             while (iterator.hasNext()) {
279                 IJavaElement element = (IJavaElement) iterator.next();
280                 String JavaDoc[] oldCategories = (String JavaDoc[]) oldTypeCategories.get(element);
281                 String JavaDoc[] newCategories = newTypeCategories == null ? null : (String JavaDoc[]) newTypeCategories.get(element);
282                 if (!Util.equalArraysOrNull(oldCategories, newCategories)) {
283                     this.delta.changed(element, IJavaElementDelta.F_CATEGORIES);
284                 }
285             }
286         } else if (newTypeCategories != null) {
287             Iterator JavaDoc elements = newTypeCategories.keySet().iterator();
288             while (elements.hasNext()) {
289                 IJavaElement element = (IJavaElement) elements.next();
290                 this.delta.changed(element, IJavaElementDelta.F_CATEGORIES); // all categories for this element were removed
291
}
292         }
293     }
294 }
295 /**
296  * Adds removed deltas for any handles left in the table
297  */

298 private void findDeletions() {
299     Iterator JavaDoc iter = this.infos.keySet().iterator();
300     while(iter.hasNext()) {
301         IJavaElement element = (IJavaElement)iter.next();
302         this.delta.removed(element);
303         this.removed(element);
304     }
305 }
306 private JavaElementInfo getElementInfo(IJavaElement element) {
307     return (JavaElementInfo)this.infos.get(element);
308 }
309 private ListItem getNewPosition(IJavaElement element) {
310     return (ListItem)this.newPositions.get(element);
311 }
312 private ListItem getOldPosition(IJavaElement element) {
313     return (ListItem)this.oldPositions.get(element);
314 }
315 private void initialize() {
316     this.infos = new HashMap JavaDoc(20);
317     this.oldPositions = new HashMap JavaDoc(20);
318     this.newPositions = new HashMap JavaDoc(20);
319     this.putOldPosition(this.javaElement, new ListItem(null, null));
320     this.putNewPosition(this.javaElement, new ListItem(null, null));
321     this.added = new ArrayList JavaDoc(5);
322     this.removed = new ArrayList JavaDoc(5);
323 }
324 /**
325  * Inserts position information for the elements into the new or old positions table
326  */

327 private void insertPositions(IJavaElement[] elements, boolean isNew) {
328     int length = elements.length;
329     IJavaElement previous = null, current = null, next = (length > 0) ? elements[0] : null;
330     for(int i = 0; i < length; i++) {
331         previous = current;
332         current = next;
333         next = (i + 1 < length) ? elements[i + 1] : null;
334         if (isNew) {
335             this.putNewPosition(current, new ListItem(previous, next));
336         } else {
337             this.putOldPosition(current, new ListItem(previous, next));
338         }
339     }
340 }
341 /**
342  * Returns whether the elements position has not changed.
343  */

344 private boolean isPositionedCorrectly(IJavaElement element) {
345     ListItem oldListItem = this.getOldPosition(element);
346     if (oldListItem == null) return false;
347     
348     ListItem newListItem = this.getNewPosition(element);
349     if (newListItem == null) return false;
350     
351     IJavaElement oldPrevious = oldListItem.previous;
352     IJavaElement newPrevious = newListItem.previous;
353     if (oldPrevious == null) {
354         return newPrevious == null;
355     } else {
356         return oldPrevious.equals(newPrevious);
357     }
358 }
359 private void putElementInfo(IJavaElement element, JavaElementInfo info) {
360     this.infos.put(element, info);
361 }
362 private void putNewPosition(IJavaElement element, ListItem position) {
363     this.newPositions.put(element, position);
364 }
365 private void putOldPosition(IJavaElement element, ListItem position) {
366     this.oldPositions.put(element, position);
367 }
368 /**
369  * Records this elements info, and attempts
370  * to record the info for the children.
371  */

372 private void recordElementInfo(IJavaElement element, JavaModel model, int depth) {
373     if (depth >= this.maxDepth) {
374         return;
375     }
376     JavaElementInfo info = (JavaElementInfo)JavaModelManager.getJavaModelManager().getInfo(element);
377     if (info == null) // no longer in the java model.
378
return;
379     this.putElementInfo(element, info);
380         
381     if (element instanceof IParent) {
382         IJavaElement[] children = info.getChildren();
383         if (children != null) {
384             insertPositions(children, false);
385             for(int i = 0, length = children.length; i < length; i++)
386                 recordElementInfo(children[i], model, depth + 1);
387         }
388     }
389 }
390 /**
391  * Fills the newPositions hashtable with the new position information
392  */

393 private void recordNewPositions(IJavaElement newElement, int depth) {
394     if (depth < this.maxDepth && newElement instanceof IParent) {
395         JavaElementInfo info = null;
396         try {
397             info = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
398         } catch (JavaModelException npe) {
399             return;
400         }
401
402         IJavaElement[] children = info.getChildren();
403         if (children != null) {
404             insertPositions(children, true);
405             for(int i = 0, length = children.length; i < length; i++) {
406                 recordNewPositions(children[i], depth + 1);
407             }
408         }
409     }
410 }
411 /**
412  * Repairs the positioning information
413  * after an element has been removed
414  */

415 private void removed(IJavaElement element) {
416     this.removed.add(element);
417     ListItem current = this.getOldPosition(element);
418     ListItem previous = null, next = null;
419     if (current.previous != null)
420         previous = this.getOldPosition(current.previous);
421     if (current.next != null)
422         next = this.getOldPosition(current.next);
423     if (previous != null)
424         previous.next = current.next;
425     if (next != null)
426         next.previous = current.previous;
427     
428 }
429 private void removeElementInfo(IJavaElement element) {
430     this.infos.remove(element);
431 }
432 public String JavaDoc toString() {
433     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
434     buffer.append("Built delta:\n"); //$NON-NLS-1$
435
buffer.append(this.delta == null ? "<null>" : this.delta.toString()); //$NON-NLS-1$
436
return buffer.toString();
437 }
438 /**
439  * Trims deletion deltas to only report the highest level of deletion
440  */

441 private void trimDelta(JavaElementDelta elementDelta) {
442     if (elementDelta.getKind() == IJavaElementDelta.REMOVED) {
443         IJavaElementDelta[] children = elementDelta.getAffectedChildren();
444         for(int i = 0, length = children.length; i < length; i++) {
445             elementDelta.removeAffectedChild((JavaElementDelta)children[i]);
446         }
447     } else {
448         IJavaElementDelta[] children = elementDelta.getAffectedChildren();
449         for(int i = 0, length = children.length; i < length; i++) {
450             trimDelta((JavaElementDelta)children[i]);
451         }
452     }
453 }
454 }
455
Popular Tags