001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.hierarchy;
011:
012: import java.util.*;
013: import java.util.ArrayList;
014: import java.util.HashMap;
015: import java.util.Iterator;
016:
017: import org.eclipse.jdt.core.*;
018: import org.eclipse.jdt.core.IJavaElementDelta;
019: import org.eclipse.jdt.core.IType;
020: import org.eclipse.jdt.core.JavaModelException;
021: import org.eclipse.jdt.internal.core.JavaElement;
022: import org.eclipse.jdt.internal.core.SimpleDelta;
023:
024: /*
025: * Collects changes (reported through fine-grained deltas) that can affect a type hierarchy.
026: */
027: public class ChangeCollector {
028:
029: /*
030: * A table from ITypes to TypeDeltas
031: */
032: HashMap changes = new HashMap();
033:
034: TypeHierarchy hierarchy;
035:
036: public ChangeCollector(TypeHierarchy hierarchy) {
037: this .hierarchy = hierarchy;
038: }
039:
040: /*
041: * Adds the children of the given delta to the list of changes.
042: */
043: private void addAffectedChildren(IJavaElementDelta delta)
044: throws JavaModelException {
045: IJavaElementDelta[] children = delta.getAffectedChildren();
046: for (int i = 0, length = children.length; i < length; i++) {
047: IJavaElementDelta child = children[i];
048: IJavaElement childElement = child.getElement();
049: switch (childElement.getElementType()) {
050: case IJavaElement.IMPORT_CONTAINER:
051: addChange((IImportContainer) childElement, child);
052: break;
053: case IJavaElement.IMPORT_DECLARATION:
054: addChange((IImportDeclaration) childElement, child);
055: break;
056: case IJavaElement.TYPE:
057: addChange((IType) childElement, child);
058: break;
059: case IJavaElement.INITIALIZER:
060: case IJavaElement.FIELD:
061: case IJavaElement.METHOD:
062: addChange((IMember) childElement, child);
063: break;
064: }
065: }
066: }
067:
068: /*
069: * Adds the given delta on a compilation unit to the list of changes.
070: */
071: public void addChange(ICompilationUnit cu,
072: IJavaElementDelta newDelta) throws JavaModelException {
073: int newKind = newDelta.getKind();
074: switch (newKind) {
075: case IJavaElementDelta.ADDED:
076: ArrayList allTypes = new ArrayList();
077: getAllTypesFromElement(cu, allTypes);
078: for (int i = 0, length = allTypes.size(); i < length; i++) {
079: IType type = (IType) allTypes.get(i);
080: addTypeAddition(type, (SimpleDelta) this .changes
081: .get(type));
082: }
083: break;
084: case IJavaElementDelta.REMOVED:
085: allTypes = new ArrayList();
086: getAllTypesFromHierarchy((JavaElement) cu, allTypes);
087: for (int i = 0, length = allTypes.size(); i < length; i++) {
088: IType type = (IType) allTypes.get(i);
089: addTypeRemoval(type, (SimpleDelta) this .changes
090: .get(type));
091: }
092: break;
093: case IJavaElementDelta.CHANGED:
094: addAffectedChildren(newDelta);
095: break;
096: }
097: }
098:
099: private void addChange(IImportContainer importContainer,
100: IJavaElementDelta newDelta) throws JavaModelException {
101: int newKind = newDelta.getKind();
102: if (newKind == IJavaElementDelta.CHANGED) {
103: addAffectedChildren(newDelta);
104: return;
105: }
106: SimpleDelta existingDelta = (SimpleDelta) this .changes
107: .get(importContainer);
108: if (existingDelta != null) {
109: switch (newKind) {
110: case IJavaElementDelta.ADDED:
111: if (existingDelta.getKind() == IJavaElementDelta.REMOVED) {
112: // REMOVED then ADDED
113: this .changes.remove(importContainer);
114: }
115: break;
116: case IJavaElementDelta.REMOVED:
117: if (existingDelta.getKind() == IJavaElementDelta.ADDED) {
118: // ADDED then REMOVED
119: this .changes.remove(importContainer);
120: }
121: break;
122: // CHANGED handled above
123: }
124: } else {
125: SimpleDelta delta = new SimpleDelta();
126: switch (newKind) {
127: case IJavaElementDelta.ADDED:
128: delta.added();
129: break;
130: case IJavaElementDelta.REMOVED:
131: delta.removed();
132: break;
133: }
134: this .changes.put(importContainer, delta);
135: }
136: }
137:
138: private void addChange(IImportDeclaration importDecl,
139: IJavaElementDelta newDelta) {
140: SimpleDelta existingDelta = (SimpleDelta) this .changes
141: .get(importDecl);
142: int newKind = newDelta.getKind();
143: if (existingDelta != null) {
144: switch (newKind) {
145: case IJavaElementDelta.ADDED:
146: if (existingDelta.getKind() == IJavaElementDelta.REMOVED) {
147: // REMOVED then ADDED
148: this .changes.remove(importDecl);
149: }
150: break;
151: case IJavaElementDelta.REMOVED:
152: if (existingDelta.getKind() == IJavaElementDelta.ADDED) {
153: // ADDED then REMOVED
154: this .changes.remove(importDecl);
155: }
156: break;
157: // CHANGED cannot happen for import declaration
158: }
159: } else {
160: SimpleDelta delta = new SimpleDelta();
161: switch (newKind) {
162: case IJavaElementDelta.ADDED:
163: delta.added();
164: break;
165: case IJavaElementDelta.REMOVED:
166: delta.removed();
167: break;
168: }
169: this .changes.put(importDecl, delta);
170: }
171: }
172:
173: /*
174: * Adds a change for the given member (a method, a field or an initializer) and the types it defines.
175: */
176: private void addChange(IMember member, IJavaElementDelta newDelta)
177: throws JavaModelException {
178: int newKind = newDelta.getKind();
179: switch (newKind) {
180: case IJavaElementDelta.ADDED:
181: ArrayList allTypes = new ArrayList();
182: getAllTypesFromElement(member, allTypes);
183: for (int i = 0, length = allTypes.size(); i < length; i++) {
184: IType innerType = (IType) allTypes.get(i);
185: addTypeAddition(innerType, (SimpleDelta) this .changes
186: .get(innerType));
187: }
188: break;
189: case IJavaElementDelta.REMOVED:
190: allTypes = new ArrayList();
191: getAllTypesFromHierarchy((JavaElement) member, allTypes);
192: for (int i = 0, length = allTypes.size(); i < length; i++) {
193: IType type = (IType) allTypes.get(i);
194: addTypeRemoval(type, (SimpleDelta) this .changes
195: .get(type));
196: }
197: break;
198: case IJavaElementDelta.CHANGED:
199: addAffectedChildren(newDelta);
200: break;
201: }
202: }
203:
204: /*
205: * Adds a change for the given type and the types it defines.
206: */
207: private void addChange(IType type, IJavaElementDelta newDelta)
208: throws JavaModelException {
209: int newKind = newDelta.getKind();
210: SimpleDelta existingDelta = (SimpleDelta) this .changes
211: .get(type);
212: switch (newKind) {
213: case IJavaElementDelta.ADDED:
214: addTypeAddition(type, existingDelta);
215: ArrayList allTypes = new ArrayList();
216: getAllTypesFromElement(type, allTypes);
217: for (int i = 0, length = allTypes.size(); i < length; i++) {
218: IType innerType = (IType) allTypes.get(i);
219: addTypeAddition(innerType, (SimpleDelta) this .changes
220: .get(innerType));
221: }
222: break;
223: case IJavaElementDelta.REMOVED:
224: addTypeRemoval(type, existingDelta);
225: allTypes = new ArrayList();
226: getAllTypesFromHierarchy((JavaElement) type, allTypes);
227: for (int i = 0, length = allTypes.size(); i < length; i++) {
228: IType innerType = (IType) allTypes.get(i);
229: addTypeRemoval(innerType, (SimpleDelta) this .changes
230: .get(innerType));
231: }
232: break;
233: case IJavaElementDelta.CHANGED:
234: addTypeChange(type, newDelta.getFlags(), existingDelta);
235: addAffectedChildren(newDelta);
236: break;
237: }
238: }
239:
240: private void addTypeAddition(IType type, SimpleDelta existingDelta)
241: throws JavaModelException {
242: if (existingDelta != null) {
243: switch (existingDelta.getKind()) {
244: case IJavaElementDelta.REMOVED:
245: // REMOVED then ADDED
246: boolean hasChange = false;
247: if (hasSuperTypeChange(type)) {
248: existingDelta.super Types();
249: hasChange = true;
250: }
251: if (hasVisibilityChange(type)) {
252: existingDelta.modifiers();
253: hasChange = true;
254: }
255: if (!hasChange) {
256: this .changes.remove(type);
257: }
258: break;
259: // CHANGED then ADDED
260: // or ADDED then ADDED: should not happen
261: }
262: } else {
263: // check whether the type addition affects the hierarchy
264: String typeName = type.getElementName();
265: if (this .hierarchy.hasSupertype(typeName)
266: || this .hierarchy.subtypesIncludeSupertypeOf(type)
267: || this .hierarchy.missingTypes.contains(typeName)) {
268: SimpleDelta delta = new SimpleDelta();
269: delta.added();
270: this .changes.put(type, delta);
271: }
272: }
273: }
274:
275: private void addTypeChange(IType type, int newFlags,
276: SimpleDelta existingDelta) throws JavaModelException {
277: if (existingDelta != null) {
278: switch (existingDelta.getKind()) {
279: case IJavaElementDelta.CHANGED:
280: // CHANGED then CHANGED
281: int existingFlags = existingDelta.getFlags();
282: boolean hasChange = false;
283: if ((existingFlags & IJavaElementDelta.F_SUPER_TYPES) != 0
284: && hasSuperTypeChange(type)) {
285: existingDelta.super Types();
286: hasChange = true;
287: }
288: if ((existingFlags & IJavaElementDelta.F_MODIFIERS) != 0
289: && hasVisibilityChange(type)) {
290: existingDelta.modifiers();
291: hasChange = true;
292: }
293: if (!hasChange) {
294: // super types and visibility are back to the ones in the existing hierarchy
295: this .changes.remove(type);
296: }
297: break;
298: // ADDED then CHANGED: leave it as ADDED
299: // REMOVED then CHANGED: should not happen
300: }
301: } else {
302: // check whether the type change affects the hierarchy
303: SimpleDelta typeDelta = null;
304: if ((newFlags & IJavaElementDelta.F_SUPER_TYPES) != 0
305: && this .hierarchy.includesTypeOrSupertype(type)) {
306: typeDelta = new SimpleDelta();
307: typeDelta.super Types();
308: }
309: if ((newFlags & IJavaElementDelta.F_MODIFIERS) != 0
310: && (this .hierarchy.hasSupertype(type
311: .getElementName()) || type
312: .equals(this .hierarchy.focusType))) {
313: if (typeDelta == null) {
314: typeDelta = new SimpleDelta();
315: }
316: typeDelta.modifiers();
317: }
318: if (typeDelta != null) {
319: this .changes.put(type, typeDelta);
320: }
321: }
322: }
323:
324: private void addTypeRemoval(IType type, SimpleDelta existingDelta) {
325: if (existingDelta != null) {
326: switch (existingDelta.getKind()) {
327: case IJavaElementDelta.ADDED:
328: // ADDED then REMOVED
329: this .changes.remove(type);
330: break;
331: case IJavaElementDelta.CHANGED:
332: // CHANGED then REMOVED
333: existingDelta.removed();
334: break;
335: // REMOVED then REMOVED: should not happen
336: }
337: } else {
338: // check whether the type removal affects the hierarchy
339: if (this .hierarchy.contains(type)) {
340: SimpleDelta typeDelta = new SimpleDelta();
341: typeDelta.removed();
342: this .changes.put(type, typeDelta);
343: }
344: }
345: }
346:
347: /*
348: * Returns all types defined in the given element excluding the given element.
349: */
350: private void getAllTypesFromElement(IJavaElement element,
351: ArrayList allTypes) throws JavaModelException {
352: switch (element.getElementType()) {
353: case IJavaElement.COMPILATION_UNIT:
354: IType[] types = ((ICompilationUnit) element).getTypes();
355: for (int i = 0, length = types.length; i < length; i++) {
356: IType type = types[i];
357: allTypes.add(type);
358: getAllTypesFromElement(type, allTypes);
359: }
360: break;
361: case IJavaElement.TYPE:
362: types = ((IType) element).getTypes();
363: for (int i = 0, length = types.length; i < length; i++) {
364: IType type = types[i];
365: allTypes.add(type);
366: getAllTypesFromElement(type, allTypes);
367: }
368: break;
369: case IJavaElement.INITIALIZER:
370: case IJavaElement.FIELD:
371: case IJavaElement.METHOD:
372: IJavaElement[] children = ((IMember) element).getChildren();
373: for (int i = 0, length = children.length; i < length; i++) {
374: IType type = (IType) children[i];
375: allTypes.add(type);
376: getAllTypesFromElement(type, allTypes);
377: }
378: break;
379: }
380: }
381:
382: /*
383: * Returns all types in the existing hierarchy that have the given element as a parent.
384: */
385: private void getAllTypesFromHierarchy(JavaElement element,
386: ArrayList allTypes) {
387: switch (element.getElementType()) {
388: case IJavaElement.COMPILATION_UNIT:
389: ArrayList types = (ArrayList) this .hierarchy.files
390: .get(element);
391: if (types != null) {
392: allTypes.addAll(types);
393: }
394: break;
395: case IJavaElement.TYPE:
396: case IJavaElement.INITIALIZER:
397: case IJavaElement.FIELD:
398: case IJavaElement.METHOD:
399: types = (ArrayList) this .hierarchy.files
400: .get(((IMember) element).getCompilationUnit());
401: if (types != null) {
402: for (int i = 0, length = types.size(); i < length; i++) {
403: IType type = (IType) types.get(i);
404: if (element.isAncestorOf(type)) {
405: allTypes.add(type);
406: }
407: }
408: }
409: break;
410: }
411: }
412:
413: private boolean hasSuperTypeChange(IType type)
414: throws JavaModelException {
415: // check super class
416: IType super class = this .hierarchy.getSuperclass(type);
417: String existingSuperclassName = super class == null ? null
418: : super class.getElementName();
419: String newSuperclassName = type.getSuperclassName();
420: if (existingSuperclassName != null
421: && !existingSuperclassName.equals(newSuperclassName)) {
422: return true;
423: }
424:
425: // check super interfaces
426: IType[] existingSuperInterfaces = this .hierarchy
427: .getSuperInterfaces(type);
428: String[] newSuperInterfaces = type.getSuperInterfaceNames();
429: if (existingSuperInterfaces.length != newSuperInterfaces.length) {
430: return true;
431: }
432: for (int i = 0, length = newSuperInterfaces.length; i < length; i++) {
433: String super InterfaceName = newSuperInterfaces[i];
434: if (!super InterfaceName.equals(newSuperInterfaces[i])) {
435: return true;
436: }
437: }
438:
439: return false;
440: }
441:
442: private boolean hasVisibilityChange(IType type)
443: throws JavaModelException {
444: int existingFlags = this .hierarchy.getCachedFlags(type);
445: int newFlags = type.getFlags();
446: return existingFlags != newFlags;
447: }
448:
449: /*
450: * Whether the hierarchy needs refresh according to the changes collected so far.
451: */
452: public boolean needsRefresh() {
453: return changes.size() != 0;
454: }
455:
456: public String toString() {
457: StringBuffer buffer = new StringBuffer();
458: Iterator iterator = this .changes.entrySet().iterator();
459: while (iterator.hasNext()) {
460: Map.Entry entry = (Map.Entry) iterator.next();
461: buffer.append(((JavaElement) entry.getKey())
462: .toDebugString());
463: buffer.append(entry.getValue());
464: if (iterator.hasNext()) {
465: buffer.append('\n');
466: }
467: }
468: return buffer.toString();
469: }
470: }
|