Added
Link Here
|
1 |
package org.eclipse.jst.jsf.context.symbol.internal.typeinfocache; |
2 |
|
3 |
import java.util.ArrayList; |
4 |
import java.util.Collection; |
5 |
import java.util.HashMap; |
6 |
import java.util.HashSet; |
7 |
import java.util.Iterator; |
8 |
import java.util.List; |
9 |
import java.util.Map; |
10 |
import java.util.Set; |
11 |
|
12 |
import org.eclipse.core.runtime.IStatus; |
13 |
import org.eclipse.core.runtime.NullProgressMonitor; |
14 |
import org.eclipse.jdt.core.ElementChangedEvent; |
15 |
import org.eclipse.jdt.core.IClassFile; |
16 |
import org.eclipse.jdt.core.ICompilationUnit; |
17 |
import org.eclipse.jdt.core.IElementChangedListener; |
18 |
import org.eclipse.jdt.core.IJavaElement; |
19 |
import org.eclipse.jdt.core.IJavaElementDelta; |
20 |
import org.eclipse.jdt.core.IType; |
21 |
import org.eclipse.jdt.core.ITypeHierarchy; |
22 |
import org.eclipse.jdt.core.ITypeRoot; |
23 |
import org.eclipse.jdt.core.JavaCore; |
24 |
import org.eclipse.jdt.core.JavaModelException; |
25 |
import org.eclipse.jdt.internal.core.PackageFragment; |
26 |
import org.eclipse.jst.jsf.common.JSFCommonPlugin; |
27 |
import org.eclipse.jst.jsf.context.symbol.internal.provisional.IBeanMethodSymbol; |
28 |
import org.eclipse.jst.jsf.context.symbol.internal.provisional.IBeanPropertySymbol; |
29 |
|
30 |
/**Provides a cache for java IType properties. It can cache bean property symbols, method symbols, |
31 |
* supertypes and implemented interfaces per IType. The cache listens to changes in the java model |
32 |
* and invalidates affected properties, but does not update them. |
33 |
* |
34 |
* @author Matthias |
35 |
*/ |
36 |
public class TypeInfoCache implements IElementChangedListener { |
37 |
|
38 |
private static TypeInfoCache instance = null; |
39 |
|
40 |
/**Returns the TypeInfoCache instance. |
41 |
* |
42 |
* @return the TypeInfoCache instance |
43 |
*/ |
44 |
public static synchronized TypeInfoCache getInstance() { |
45 |
if (instance == null) { |
46 |
instance = new TypeInfoCache(); |
47 |
JavaCore.addElementChangedListener(instance, ElementChangedEvent.POST_CHANGE); |
48 |
} |
49 |
return instance; |
50 |
} |
51 |
|
52 |
private Map /*<IType, TypeInfo>*/ cachedInfo; |
53 |
private Map /*<ITypeRoot, Set<IType>>*/ cachedTypesByAffectingTypeRoot; |
54 |
private Map /*<String, Set<IType>>*/ cachedTypesByMissingSupertypename; |
55 |
|
56 |
private TypeInfoCache() { |
57 |
cachedInfo = new HashMap(); |
58 |
cachedTypesByAffectingTypeRoot = new HashMap(); |
59 |
cachedTypesByMissingSupertypename = new HashMap(10); |
60 |
} |
61 |
|
62 |
public void elementChanged(ElementChangedEvent event) { |
63 |
updateChangedJavaElement(event.getDelta()); |
64 |
} |
65 |
|
66 |
/**Returns the cached info({@link TypeInfo}) for a given type. Will |
67 |
* return <code>null</code> if no info has been cached or the the type/something it depends on |
68 |
* has changed since then. |
69 |
* |
70 |
* @param type - the type in question |
71 |
* @return a TypeInfo instance that contains all cached info for the given type. May be null. |
72 |
*/ |
73 |
protected TypeInfo getTypeInfo(IType type) { |
74 |
TypeInfo info = (TypeInfo) cachedInfo.get(type); |
75 |
return info; |
76 |
} |
77 |
|
78 |
/**Returns the cached bean property symbols for a given type. Will return null if no |
79 |
* bean property symbols have been cached or the type/something it depends on has changed since |
80 |
* then. |
81 |
* @param beanType - the bean type in question |
82 |
* @return the bean property symbols for the given type. May be null. |
83 |
* @see TypeInfoCache#cachePropertySymbols(IType, IBeanPropertySymbol[]) |
84 |
*/ |
85 |
public synchronized IBeanPropertySymbol[] getCachedPropertySymbols(IType beanType) { |
86 |
TypeInfo typeInfo = getTypeInfo(beanType); |
87 |
return typeInfo == null? null : typeInfo.getPropertySymbols(); |
88 |
} |
89 |
|
90 |
/**Returns the cached method symbols for a given type. Will return null if no |
91 |
* method symbols have been cached or the type/something it depends on has changed since |
92 |
* then. |
93 |
* @param beanType - the bean type in question |
94 |
* @return the method symbols for the given type. May be null. |
95 |
* @see TypeInfoCache#cacheMethodSymbols(IType, IBeanMethodSymbol[]) |
96 |
*/ |
97 |
public synchronized IBeanMethodSymbol[] getCachedMethodSymbols(IType beanType) { |
98 |
TypeInfo typeInfo = getTypeInfo(beanType); |
99 |
return typeInfo == null? null : typeInfo.getMethodSymbols(); |
100 |
} |
101 |
|
102 |
/**Returns the cached supertypes for a given type. Will return null if no supertypes |
103 |
* have been cached for this type or if the type/something it depends on has changed since |
104 |
* then. |
105 |
* @param type - the bean type in question |
106 |
* @return the supertypes for the given type. May be null. |
107 |
* @see TypeInfoCache#cacheSupertypesFor(IType) |
108 |
*/ |
109 |
public synchronized IType[] getCachedSupertypes(IType type) { |
110 |
TypeInfo typeInfo = getTypeInfo(type); |
111 |
return typeInfo == null? null : typeInfo.getSupertypes(); |
112 |
} |
113 |
|
114 |
/**Returns the cached implemented interfaces for a given type. Will return null if no interfaces |
115 |
* have been cached for this type or if the type/something it depends on has changed since |
116 |
* then. |
117 |
* @param type - the bean type in question |
118 |
* @return the interface types implemented by the given type. May be null. |
119 |
* @see TypeInfoCache#cacheInterfaceTypesFor(IType) |
120 |
*/ |
121 |
public synchronized IType[] getCachedInterfaceTypes(IType type) { |
122 |
TypeInfo typeInfo = getTypeInfo(type); |
123 |
return typeInfo == null? null : typeInfo.getInterfaceTypes(); |
124 |
} |
125 |
|
126 |
/**Caches the given method symbols for the given type. |
127 |
* @param beanType - the type |
128 |
* @param methods - the method symbols to cache |
129 |
*/ |
130 |
public synchronized void cacheMethodSymbols(IType beanType, IBeanMethodSymbol[] methods) { |
131 |
TypeInfo typeInfo = getOrCreateTypeInfo(beanType); |
132 |
if (typeInfo != null) { |
133 |
typeInfo.setMethodSymbols(methods); |
134 |
} |
135 |
} |
136 |
|
137 |
/**Caches the given property symbols for the given type. |
138 |
* @param beanType - the type |
139 |
* @param properties - the property symbols to cache |
140 |
*/ |
141 |
public synchronized void cachePropertySymbols(IType beanType, IBeanPropertySymbol[] properties) { |
142 |
TypeInfo typeInfo = getOrCreateTypeInfo(beanType); |
143 |
if (typeInfo != null) { |
144 |
typeInfo.setPropertySymbols(properties); |
145 |
} |
146 |
} |
147 |
|
148 |
/**Caches the supertypes for the given type. The supertypes will be calculated (and also returned) |
149 |
* by this method. |
150 |
* @param type - the type to cache supertypes for |
151 |
* @return the supertypes of the given type. |
152 |
*/ |
153 |
public synchronized IType[] cacheSupertypesFor(IType type) { |
154 |
TypeInfo typeInfo = getOrCreateTypeInfo(type); |
155 |
return typeInfo == null? null : typeInfo.getSupertypes(); |
156 |
} |
157 |
|
158 |
/**Caches the interface types for the given type. The interface types will be calculated (and also |
159 |
* returned) by this method. |
160 |
* @param type - the type to cache interface types for |
161 |
* @return the interface types implemented by the given type. |
162 |
*/ |
163 |
public synchronized IType[] cacheInterfaceTypesFor(IType type) { |
164 |
TypeInfo typeInfo = getOrCreateTypeInfo(type); |
165 |
return typeInfo == null? null : typeInfo.getInterfaceTypes(); |
166 |
} |
167 |
|
168 |
/**Returns the TypeInfo for the given type. If no TypeInfo exists for this type, an empty TypeInfo |
169 |
* will be created and cached. |
170 |
* @param type - the type in question |
171 |
* @return the (modifyable) TypeInfo for the given type |
172 |
*/ |
173 |
protected TypeInfo getOrCreateTypeInfo(IType type) { |
174 |
TypeInfo typeInfo = getTypeInfo(type); |
175 |
if (typeInfo == null) { |
176 |
try { |
177 |
final ITypeHierarchy hierarchy = |
178 |
type.newSupertypeHierarchy(new NullProgressMonitor()); |
179 |
final IType[] supertypes = hierarchy.getAllSuperclasses(type); |
180 |
final IType[] interfaceTypes = hierarchy.getAllInterfaces(); |
181 |
final IType[] rootClasses = hierarchy.getRootClasses(); |
182 |
List missingSupertypesList = null; |
183 |
for (int i = 0; i < rootClasses.length; i++) { |
184 |
String superclassName = rootClasses[i].getSuperclassName(); |
185 |
if (superclassName != null) { |
186 |
if (missingSupertypesList == null) { |
187 |
missingSupertypesList = new ArrayList(1); |
188 |
} |
189 |
superclassName = shortTypename(superclassName); |
190 |
missingSupertypesList.add(superclassName); |
191 |
} |
192 |
} |
193 |
String[] missingSupertypes = null; |
194 |
if (missingSupertypesList != null) { |
195 |
missingSupertypes = (String[]) missingSupertypesList.toArray(new String[missingSupertypesList.size()]); |
196 |
} else { |
197 |
missingSupertypes = TypeInfo.NO_NAMES; |
198 |
} |
199 |
typeInfo = new TypeInfo(); |
200 |
typeInfo.setSupertypes(supertypes); |
201 |
typeInfo.setInterfaceTypes(interfaceTypes); |
202 |
typeInfo.setMissingSupertypeNames(missingSupertypes); |
203 |
cachedInfo.put(type, typeInfo); |
204 |
registerCachedType(type, typeInfo); |
205 |
} catch (JavaModelException e) { |
206 |
JSFCommonPlugin.log(e); |
207 |
} |
208 |
} |
209 |
return typeInfo; |
210 |
} |
211 |
|
212 |
/**Returns the typename fragment after the last "." (which in most cases is identical to the |
213 |
* unqualified typename). |
214 |
* Used only to make sure that if n1 and n2 are names of the same type |
215 |
* shortname(n1) equals shortname(2) even if one name is qualified and one not. |
216 |
* @param typename |
217 |
* @return the typename fragment after the last "." |
218 |
*/ |
219 |
private String shortTypename(String typename) { |
220 |
int pos = typename.lastIndexOf('.'); |
221 |
if (pos >= 0) { |
222 |
typename = typename.substring(pos + 1); |
223 |
} |
224 |
return typename; |
225 |
} |
226 |
|
227 |
/**Registers the given type for all ITypeRoot's it depends on, so that it can be uncached if |
228 |
* one of this ITypeRoot's has changed. The type must be unregistered when it should not be watched |
229 |
* anymore. |
230 |
* @param type - the type |
231 |
* @param typeInfo - TypeInfo of the given type |
232 |
* @see TypeInfoCache#unregisterCachedType(IType, TypeInfo) |
233 |
*/ |
234 |
protected void registerCachedType(IType type, TypeInfo typeInfo) { |
235 |
registerTypeForTypeRoot(type, type.getTypeRoot()); |
236 |
IType[] supertypes = typeInfo.getSupertypes(); |
237 |
for (int i = 0; i < supertypes.length; i++) { |
238 |
registerTypeForTypeRoot(type, supertypes[i].getTypeRoot()); |
239 |
} |
240 |
String[] missingSupertypeNames = typeInfo.getMissingSupertypeNames(); |
241 |
if (missingSupertypeNames != null) { |
242 |
for (int i = 0; i < missingSupertypeNames.length; i++) { |
243 |
registerTypeForMissingSupertype(type, missingSupertypeNames[i]); |
244 |
} |
245 |
} |
246 |
} |
247 |
|
248 |
private void registerTypeForTypeRoot(IType type, ITypeRoot typeRoot) { |
249 |
Set dependentTypes = (Set) cachedTypesByAffectingTypeRoot.get(typeRoot); |
250 |
if (dependentTypes == null) { |
251 |
dependentTypes = new HashSet(5); |
252 |
cachedTypesByAffectingTypeRoot.put(typeRoot, dependentTypes); |
253 |
} |
254 |
dependentTypes.add(type); |
255 |
} |
256 |
|
257 |
private void registerTypeForMissingSupertype(IType type, String supertype) { |
258 |
Set dependentTypes = (Set) cachedTypesByMissingSupertypename.get(supertype); |
259 |
if (dependentTypes == null) { |
260 |
dependentTypes = new HashSet(5); |
261 |
cachedTypesByMissingSupertypename.put(supertype, dependentTypes); |
262 |
} |
263 |
dependentTypes.add(type); |
264 |
} |
265 |
|
266 |
/**Unregisters the given type for all ITypeRoot's it depended on. |
267 |
* @param type - the type |
268 |
* @param typeInfo - TypeInfo of the given type |
269 |
*/ |
270 |
protected void unregisterCachedType(IType type, TypeInfo typeInfo) { |
271 |
unregisterTypeForTypeRoot(type, type.getTypeRoot()); |
272 |
IType[] supertypes = typeInfo.getSupertypes(); |
273 |
for (int i = 0; i < supertypes.length; i++) { |
274 |
unregisterTypeForTypeRoot(type, supertypes[i].getTypeRoot()); |
275 |
} |
276 |
String[] missingSupertypeNames = typeInfo.getMissingSupertypeNames(); |
277 |
if (missingSupertypeNames != null) { |
278 |
for (int i = 0; i < missingSupertypeNames.length; i++) { |
279 |
unregisterTypeForMissingSupertype(type, missingSupertypeNames[i]); |
280 |
} |
281 |
} |
282 |
} |
283 |
|
284 |
private void unregisterTypeForTypeRoot(IType type, ITypeRoot typeRoot) { |
285 |
Set dependentTypes = (Set) cachedTypesByAffectingTypeRoot.get(typeRoot); |
286 |
if (dependentTypes != null) { |
287 |
dependentTypes.remove(type); |
288 |
if (dependentTypes.isEmpty()) { |
289 |
cachedTypesByAffectingTypeRoot.remove(typeRoot); |
290 |
} |
291 |
} |
292 |
} |
293 |
|
294 |
private void unregisterTypeForMissingSupertype(IType type, String supertype) { |
295 |
Set dependentTypes = (Set) cachedTypesByMissingSupertypename.get(supertype); |
296 |
if (dependentTypes != null) { |
297 |
dependentTypes.remove(type); |
298 |
if (dependentTypes.isEmpty()) { |
299 |
cachedTypesByMissingSupertypename.remove(supertype); |
300 |
} |
301 |
} |
302 |
} |
303 |
|
304 |
/**This will remove all cached info for all types. |
305 |
*/ |
306 |
protected synchronized void uncacheAllTypes() { |
307 |
cachedInfo.clear(); |
308 |
cachedTypesByAffectingTypeRoot.clear(); |
309 |
cachedTypesByMissingSupertypename.clear(); |
310 |
} |
311 |
|
312 |
/**Removes all cached info for all types that are subtypes of a type of the given ITypeRoot. |
313 |
* @param typeRoot |
314 |
*/ |
315 |
protected synchronized void uncacheAffectedTypes(ITypeRoot typeRoot) { |
316 |
Collection affectedTypes = (Collection) cachedTypesByAffectingTypeRoot.get(typeRoot); |
317 |
if (affectedTypes != null && !affectedTypes.isEmpty()) { |
318 |
List affectedTypesCopy = new ArrayList(affectedTypes); |
319 |
for (Iterator it = affectedTypesCopy.iterator(); it.hasNext(); ) { |
320 |
IType cachedType = (IType) it.next(); |
321 |
TypeInfo typeInfo = (TypeInfo) cachedInfo.remove(cachedType); |
322 |
unregisterCachedType(cachedType, typeInfo); |
323 |
} |
324 |
} |
325 |
} |
326 |
|
327 |
/**Removes all cached info for all types (or subtypes of types) that specify a supertype |
328 |
* that has a name similar to the given name. |
329 |
* @param supertypename - the missing supertype name. May be qualified or not |
330 |
*/ |
331 |
protected synchronized void uncacheTypesWithMissingSupertype(String supertypename) { |
332 |
Collection affectedTypes = (Collection) cachedTypesByMissingSupertypename.get(shortTypename(supertypename)); |
333 |
if (affectedTypes != null && !affectedTypes.isEmpty()) { |
334 |
List affectedTypesCopy = new ArrayList(affectedTypes); |
335 |
for (Iterator it = affectedTypesCopy.iterator(); it.hasNext(); ) { |
336 |
IType cachedType = (IType) it.next(); |
337 |
TypeInfo typeInfo = (TypeInfo) cachedInfo.remove(cachedType); |
338 |
unregisterCachedType(cachedType, typeInfo); |
339 |
} |
340 |
} |
341 |
} |
342 |
|
343 |
/**Removes all cached info that may be affected by the given change. |
344 |
* @param delta - the change in the java model |
345 |
*/ |
346 |
protected void updateChangedJavaElement(IJavaElementDelta delta) { |
347 |
IJavaElement element= delta.getElement(); |
348 |
switch (element.getElementType()) { |
349 |
case IJavaElement.JAVA_MODEL: |
350 |
updateChangedJavaModel(delta, element); |
351 |
break; |
352 |
case IJavaElement.JAVA_PROJECT: |
353 |
updateChangedJavaProject(delta, element); |
354 |
break; |
355 |
case IJavaElement.PACKAGE_FRAGMENT_ROOT: |
356 |
updateChangedPackageFragmentRoot(delta, element); |
357 |
break; |
358 |
case IJavaElement.PACKAGE_FRAGMENT: |
359 |
updateChangedPackageFragment(delta, (PackageFragment) element); |
360 |
break; |
361 |
case IJavaElement.CLASS_FILE: |
362 |
case IJavaElement.COMPILATION_UNIT: |
363 |
updateChangedOpenable(delta, element); |
364 |
break; |
365 |
} |
366 |
} |
367 |
|
368 |
private void updateChangedChildren(IJavaElementDelta delta) { |
369 |
if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) > 0) { |
370 |
IJavaElementDelta[] children= delta.getAffectedChildren(); |
371 |
for (int i= 0; i < children.length; i++) { |
372 |
updateChangedJavaElement(children[i]); |
373 |
} |
374 |
} |
375 |
} |
376 |
|
377 |
private void updateChangedJavaModel(IJavaElementDelta delta, IJavaElement element) { |
378 |
switch (delta.getKind()) { |
379 |
case IJavaElementDelta.ADDED : |
380 |
case IJavaElementDelta.REMOVED : |
381 |
uncacheAllTypes(); |
382 |
break; |
383 |
case IJavaElementDelta.CHANGED : |
384 |
updateChangedChildren(delta); |
385 |
break; |
386 |
} |
387 |
} |
388 |
|
389 |
private void updateChangedJavaProject(IJavaElementDelta delta, IJavaElement element) { |
390 |
int kind = delta.getKind(); |
391 |
int flags = delta.getFlags(); |
392 |
if ((flags & IJavaElementDelta.F_OPENED) != 0) { |
393 |
kind = IJavaElementDelta.ADDED; // affected in the same way |
394 |
} |
395 |
if ((flags & IJavaElementDelta.F_CLOSED) != 0) { |
396 |
kind = IJavaElementDelta.REMOVED; // affected in the same way |
397 |
} |
398 |
switch (kind) { |
399 |
case IJavaElementDelta.ADDED : |
400 |
case IJavaElementDelta.REMOVED : |
401 |
uncacheAllTypes(); |
402 |
break; |
403 |
case IJavaElementDelta.CHANGED : |
404 |
updateChangedChildren(delta); |
405 |
break; |
406 |
} |
407 |
} |
408 |
|
409 |
private void updateChangedPackageFragment(IJavaElementDelta delta, PackageFragment element) { |
410 |
switch (delta.getKind()) { |
411 |
case IJavaElementDelta.ADDED : |
412 |
// if the package fragment is in the projects being considered, this could |
413 |
// introduce new types, changing the hierarchy |
414 |
case IJavaElementDelta.REMOVED : |
415 |
// is a change if the package fragment contains supertypes? |
416 |
uncacheAllTypes(); |
417 |
break; |
418 |
case IJavaElementDelta.CHANGED : |
419 |
// look at the files in the package fragment |
420 |
updateChangedChildren(delta); |
421 |
} |
422 |
} |
423 |
|
424 |
private void updateChangedPackageFragmentRoot(IJavaElementDelta delta, IJavaElement element) { |
425 |
switch (delta.getKind()) { |
426 |
case IJavaElementDelta.ADDED : |
427 |
case IJavaElementDelta.REMOVED : |
428 |
uncacheAllTypes(); |
429 |
break; |
430 |
case IJavaElementDelta.CHANGED : |
431 |
int flags = delta.getFlags(); |
432 |
if (((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0)||(flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0) { |
433 |
uncacheAllTypes(); |
434 |
} else { |
435 |
updateChangedChildren(delta); |
436 |
} |
437 |
break; |
438 |
} |
439 |
} |
440 |
|
441 |
/**Removes all cached info that may be affected by the change in this IOpenable |
442 |
* @param delta - the change in the java model |
443 |
* @param element - the (changed) IOpenable considered |
444 |
*/ |
445 |
protected void updateChangedOpenable(IJavaElementDelta delta, IJavaElement element) { |
446 |
if (element instanceof ITypeRoot) { |
447 |
ITypeRoot typeRoot = (ITypeRoot) element; |
448 |
uncacheAffectedTypes(typeRoot); |
449 |
// Creates missing superclass for any cached type? |
450 |
if (delta.getKind() == IJavaElementDelta.ADDED) { |
451 |
if (typeRoot instanceof ICompilationUnit) { |
452 |
ICompilationUnit cu = (ICompilationUnit) typeRoot; |
453 |
try { |
454 |
IType[] types = cu.getAllTypes(); |
455 |
for (int i = 0; i < types.length; i++) { |
456 |
uncacheTypesWithMissingSupertype(types[i].getElementName()); |
457 |
} |
458 |
} catch (JavaModelException e) { |
459 |
JSFCommonPlugin.log(IStatus.INFO, "Unable to get types for compilation unit " + cu, e); |
460 |
uncacheAllTypes(); |
461 |
} |
462 |
} else if (typeRoot instanceof IClassFile) { |
463 |
IClassFile cf = (IClassFile) typeRoot; |
464 |
try { |
465 |
IType type = cf.getType(); |
466 |
uncacheTypesWithMissingSupertype(type.getElementName()); |
467 |
} catch (JavaModelException e) { |
468 |
JSFCommonPlugin.log(IStatus.INFO, "Unable to get types for classfile " + cf, e); |
469 |
uncacheAllTypes(); |
470 |
} |
471 |
} |
472 |
} |
473 |
} |
474 |
} |
475 |
|
476 |
} |