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;
26 * A java element delta biulder creates a java element delta on
27 * a java element between the version of the java element
28 * at the time the comparator was created and the current version
29 * of the java element.
31 * It performs this operation by locally caching the contents of
32 * the java element when it is created. When the method
33 * createDeltas() is called, it creates a delta over the cached
34 * contents and the new contents.
36 public class JavaElementDeltaBuilder {
38 * The java element handle
40 IJavaElement javaElement;
43 * The maximum depth in the java element children we should look into
45 int maxDepth = Integer.MAX_VALUE;
48 * The old handle to info relationships
53 * The old position info
58 * The new position info
65 JavaElementDelta delta;
68 * List of added elements
73 * List of removed elements
78 * Doubly linked list item
81 public IJavaElement previous;
82 public IJavaElement next;
84 public ListItem(IJavaElement previous, IJavaElement next) {
85 this.previous = previous;
90 * Creates a java element comparator on a java element
91 * looking as deep as necessary.
93 public JavaElementDeltaBuilder(IJavaElement javaElement) {
94 this.javaElement = javaElement;
96 this.recordElementInfo(
98 (JavaModel)this.javaElement.getJavaModel(),
102 * Creates a java element comparator on a java element
103 * looking only 'maxDepth' levels deep.
105 public JavaElementDeltaBuilder(IJavaElement javaElement, int maxDepth) {
106 this.javaElement = javaElement;
107 this.maxDepth = maxDepth;
109 this.recordElementInfo(
111 (JavaModel)this.javaElement.getJavaModel(),
115 * Repairs the positioning information
116 * after an element has been added
118 private void added(IJavaElement element) {
119 this.added.add(element);
120 ListItem current = this.getNewPosition(element);
121 ListItem previous = null, next = null;
122 if (current.previous != null)
123 previous = this.getNewPosition(current.previous);
124 if (current.next != null)
125 next = this.getNewPosition(current.next);
126 if (previous != null)
127 previous.next = current.next;
129 next.previous = current.previous;
132 * Builds the java element deltas between the old content of the compilation
133 * unit and its new content.
135 public void buildDeltas() {
136 this.recordNewPositions(this.javaElement, 0);
137 this.findAdditions(this.javaElement, 0);
138 this.findDeletions();
139 this.findChangesInPositioning(this.javaElement, 0);
140 this.trimDelta(this.delta);
141 if (this.delta.getAffectedChildren().length == 0) {
142 // this is a fine grained but not children affected -> mark as content changed
143 this.delta.contentChanged();
147 * Finds elements which have been added or changed.
149 private void findAdditions(IJavaElement newElement, int depth) {
150 JavaElementInfo oldInfo = this.getElementInfo(newElement);
151 if (oldInfo == null && depth < this.maxDepth) {
152 this.delta.added(newElement);
155 this.removeElementInfo(newElement);
158 if (depth >= this.maxDepth) {
159 // mark element as changed
160 this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
164 JavaElementInfo newInfo = null;
166 newInfo = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
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);
185 * Looks for changed positioning of elements.
187 private void findChangesInPositioning(IJavaElement element, int depth) {
188 if (depth >= this.maxDepth || this.added.contains(element) || this.removed.contains(element))
191 if (!isPositionedCorrectly(element)) {
192 this.delta.changed(element, IJavaElementDelta.F_REORDER);
195 if (element instanceof IParent) {
196 JavaElementInfo info = null;
198 info = (JavaElementInfo)((JavaElement)element).getElementInfo();
199 } catch (JavaModelException npe) {
203 IJavaElement[] children = info.getChildren();
204 if (children != null) {
205 int length = children.length;
206 for(int i = 0; i < length; i++) {
207 this.findChangesInPositioning(children[i], depth + 1);
213 * The elements are equivalent, but might have content changes.
215 private void findContentChange(JavaElementInfo oldInfo, JavaElementInfo newInfo, IJavaElement newElement) {
216 if (oldInfo instanceof MemberElementInfo && newInfo instanceof MemberElementInfo) {
217 if (((MemberElementInfo)oldInfo).getModifiers() != ((MemberElementInfo)newInfo).getModifiers()) {
218 this.delta.changed(newElement, IJavaElementDelta.F_MODIFIERS);
219 } else if (oldInfo instanceof SourceMethodElementInfo && newInfo instanceof SourceMethodElementInfo) {
220 if (!CharOperation.equals(
221 ((SourceMethodElementInfo)oldInfo).getReturnTypeName(),
222 ((SourceMethodElementInfo)newInfo).getReturnTypeName())) {
223 this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
225 } else if (oldInfo instanceof SourceFieldElementInfo && newInfo instanceof SourceFieldElementInfo) {
226 if (!CharOperation.equals(
227 ((SourceFieldElementInfo)oldInfo).getTypeName(),
228 ((SourceFieldElementInfo)newInfo).getTypeName())) {
229 this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
233 if (oldInfo instanceof SourceTypeElementInfo && newInfo instanceof SourceTypeElementInfo) {
234 SourceTypeElementInfo oldSourceTypeInfo = (SourceTypeElementInfo)oldInfo;
235 SourceTypeElementInfo newSourceTypeInfo = (SourceTypeElementInfo)newInfo;
236 if (!CharOperation.equals(oldSourceTypeInfo.getSuperclassName(), newSourceTypeInfo.getSuperclassName())
237 || !CharOperation.equals(oldSourceTypeInfo.getInterfaceNames(), newSourceTypeInfo.getInterfaceNames())) {
238 this.delta.changed(newElement, IJavaElementDelta.F_SUPER_TYPES);
243 * Adds removed deltas for any handles left in the table
245 private void findDeletions() {
246 Iterator iter = this.infos.keySet().iterator();
247 while(iter.hasNext()) {
248 IJavaElement element = (IJavaElement)iter.next();
249 this.delta.removed(element);
250 this.removed(element);
253 private JavaElementInfo getElementInfo(IJavaElement element) {
254 return (JavaElementInfo)this.infos.get(element);
256 private ListItem getNewPosition(IJavaElement element) {
257 return (ListItem)this.newPositions.get(element);
259 private ListItem getOldPosition(IJavaElement element) {
260 return (ListItem)this.oldPositions.get(element);
262 private void initialize() {
263 this.infos = new HashMap(20);
264 this.oldPositions = new HashMap(20);
265 this.newPositions = new HashMap(20);
266 this.putOldPosition(this.javaElement, new ListItem(null, null));
267 this.putNewPosition(this.javaElement, new ListItem(null, null));
268 this.delta = new JavaElementDelta(javaElement);
270 // if building a delta on a compilation unit or below,
271 // it's a fine grained delta
272 if (javaElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
273 this.delta.fineGrained();
276 this.added = new ArrayList(5);
277 this.removed = new ArrayList(5);
280 * Inserts position information for the elements into the new or old positions table
282 private void insertPositions(IJavaElement[] elements, boolean isNew) {
283 int length = elements.length;
284 IJavaElement previous = null, current = null, next = (length > 0) ? elements[0] : null;
285 for(int i = 0; i < length; i++) {
288 next = (i + 1 < length) ? elements[i + 1] : null;
290 this.putNewPosition(current, new ListItem(previous, next));
292 this.putOldPosition(current, new ListItem(previous, next));
297 * Returns whether the elements position has not changed.
299 private boolean isPositionedCorrectly(IJavaElement element) {
300 ListItem oldListItem = this.getOldPosition(element);
301 if (oldListItem == null) return false;
303 ListItem newListItem = this.getNewPosition(element);
304 if (newListItem == null) return false;
306 IJavaElement oldPrevious = oldListItem.previous;
307 IJavaElement newPrevious = newListItem.previous;
308 if (oldPrevious == null) {
309 return newPrevious == null;
311 return oldPrevious.equals(newPrevious);
314 private void putElementInfo(IJavaElement element, JavaElementInfo info) {
315 this.infos.put(element, info);
317 private void putNewPosition(IJavaElement element, ListItem position) {
318 this.newPositions.put(element, position);
320 private void putOldPosition(IJavaElement element, ListItem position) {
321 this.oldPositions.put(element, position);
324 * Records this elements info, and attempts
325 * to record the info for the children.
327 private void recordElementInfo(IJavaElement element, JavaModel model, int depth) {
328 if (depth >= this.maxDepth) {
331 JavaElementInfo info = null;//(JavaElementInfo)JavaModelManager.getJavaModelManager().getInfo(element);
332 if (info == null) // no longer in the java model.
334 this.putElementInfo(element, info);
336 if (element instanceof IParent) {
337 IJavaElement[] children = info.getChildren();
338 if (children != null) {
339 insertPositions(children, false);
340 for(int i = 0, length = children.length; i < length; i++)
341 recordElementInfo(children[i], model, depth + 1);
346 * Fills the newPositions hashtable with the new position information
348 private void recordNewPositions(IJavaElement newElement, int depth) {
349 if (depth < this.maxDepth && newElement instanceof IParent) {
350 JavaElementInfo info = null;
352 info = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
353 } catch (JavaModelException npe) {
357 IJavaElement[] children = info.getChildren();
358 if (children != null) {
359 insertPositions(children, true);
360 for(int i = 0, length = children.length; i < length; i++) {
361 recordNewPositions(children[i], depth + 1);
367 * Repairs the positioning information
368 * after an element has been removed
370 private void removed(IJavaElement element) {
371 this.removed.add(element);
372 ListItem current = this.getOldPosition(element);
373 ListItem previous = null, next = null;
374 if (current.previous != null)
375 previous = this.getOldPosition(current.previous);
376 if (current.next != null)
377 next = this.getOldPosition(current.next);
378 if (previous != null)
379 previous.next = current.next;
381 next.previous = current.previous;
384 private void removeElementInfo(IJavaElement element) {
385 this.infos.remove(element);
387 public String toString() {
388 StringBuffer buffer = new StringBuffer();
389 buffer.append("Built delta:\n"); //$NON-NLS-1$
390 buffer.append(this.delta.toString());
391 return buffer.toString();
394 * Trims deletion deltas to only report the highest level of deletion
396 private void trimDelta(JavaElementDelta delta) {
397 if (delta.getKind() == IJavaElementDelta.REMOVED) {
398 IJavaElementDelta[] children = delta.getAffectedChildren();
399 for(int i = 0, length = children.length; i < length; i++) {
400 delta.removeAffectedChild((JavaElementDelta)children[i]);
403 IJavaElementDelta[] children = delta.getAffectedChildren();
404 for(int i = 0, length = children.length; i < length; i++) {
405 trimDelta((JavaElementDelta)children[i]);