1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.util;
13 import java.util.ArrayList;
14 import java.util.Iterator;
17 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jdt.core.IType;
20 import org.eclipse.jdt.core.ITypeHierarchy;
21 import org.eclipse.jdt.core.ITypeHierarchyChangedListener;
22 import org.eclipse.jdt.core.JavaModelException;
25 * A thread-safe cache for super type hierarchies.
27 public class SuperTypeHierarchyCache {
29 private static class HierarchyCacheEntry implements ITypeHierarchyChangedListener {
31 private ITypeHierarchy fTypeHierarchy;
32 private long fLastAccess;
34 public HierarchyCacheEntry(ITypeHierarchy hierarchy) {
35 fTypeHierarchy= hierarchy;
36 fTypeHierarchy.addTypeHierarchyChangedListener(this);
40 public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) {
41 removeHierarchyEntryFromCache(this);
44 public ITypeHierarchy getTypeHierarchy() {
45 return fTypeHierarchy;
48 public void markAsAccessed() {
49 fLastAccess= System.currentTimeMillis();
52 public long getLastAccess() {
56 public void dispose() {
57 if (fTypeHierarchy != null) {
58 fTypeHierarchy.removeTypeHierarchyChangedListener(this);
64 * @see java.lang.Object#toString()
67 public String toString() {
68 return "Super hierarchy of: " + fTypeHierarchy.getType().getElementName(); //$NON-NLS-1$
74 private static final int CACHE_SIZE= 8;
76 private static ArrayList<HierarchyCacheEntry> fgHierarchyCache= new ArrayList<HierarchyCacheEntry>(CACHE_SIZE);
77 private static Map<IType, MethodOverrideTester> fgMethodOverrideTesterCache= new LRUMap<IType, MethodOverrideTester>(CACHE_SIZE);
79 private static int fgCacheHits= 0;
80 private static int fgCacheMisses= 0;
83 * Returns a super type hierarchy that contains the given type.
84 * The returned hierarchy may actually be based on a subtype of the
85 * requested type. Therefore, queries such as {@link ITypeHierarchy#getAllClasses()}
86 * or {@link ITypeHierarchy#getRootInterfaces()} may return more types than the same
87 * queries on a type hierarchy for just the given type.
89 * @param type the focus type
90 * @return a supertype hierarchy that contains <code>type</code>
91 * @throws JavaModelException if a problem occurs
93 public static ITypeHierarchy getTypeHierarchy(IType type) throws JavaModelException {
94 return getTypeHierarchy(type, null);
97 public static MethodOverrideTester getMethodOverrideTester(IType type) throws JavaModelException {
98 MethodOverrideTester test= null;
99 synchronized (fgMethodOverrideTesterCache) {
100 test= fgMethodOverrideTesterCache.get(type);
103 ITypeHierarchy hierarchy= getTypeHierarchy(type); // don't nest the locks
104 synchronized (fgMethodOverrideTesterCache) {
105 test= fgMethodOverrideTesterCache.get(type); // test again after waiting a long time for 'getTypeHierarchy'
107 test= new MethodOverrideTester(type, hierarchy);
108 fgMethodOverrideTesterCache.put(type, test);
115 private static void removeMethodOverrideTester(ITypeHierarchy hierarchy) {
116 synchronized (fgMethodOverrideTesterCache) {
117 for (Iterator<MethodOverrideTester> iter= fgMethodOverrideTesterCache.values().iterator(); iter.hasNext();) {
118 MethodOverrideTester curr= iter.next();
119 if (curr.getTypeHierarchy().equals(hierarchy)) {
127 * Returns a super type hierarchy that contains the given type.
128 * The returned hierarchy may actually be based on a subtype of the
129 * requested type. Therefore, queries such as {@link ITypeHierarchy#getAllClasses()}
130 * or {@link ITypeHierarchy#getRootInterfaces()} may return more types than the same
131 * queries on a type hierarchy for just the given type.
133 * @param type the focus type
134 * @param progressMonitor progress monitor
135 * @return a supertype hierarchy that contains <code>type</code>
136 * @throws JavaModelException if a problem occurs
138 public static ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor progressMonitor) throws JavaModelException {
139 ITypeHierarchy hierarchy= findTypeHierarchyInCache(type);
140 if (hierarchy == null) {
142 hierarchy= type.newSupertypeHierarchy(progressMonitor);
143 addTypeHierarchyToCache(hierarchy);
150 private static void addTypeHierarchyToCache(ITypeHierarchy hierarchy) {
151 synchronized (fgHierarchyCache) {
152 int nEntries= fgHierarchyCache.size();
153 if (nEntries >= CACHE_SIZE) {
154 // find obsolete entries or remove entry that was least recently accessed
155 HierarchyCacheEntry oldest= null;
156 ArrayList<HierarchyCacheEntry> obsoleteHierarchies= new ArrayList<HierarchyCacheEntry>(CACHE_SIZE);
157 for (int i= 0; i < nEntries; i++) {
158 HierarchyCacheEntry entry= fgHierarchyCache.get(i);
159 ITypeHierarchy curr= entry.getTypeHierarchy();
160 if (!curr.exists() || hierarchy.contains(curr.getType())) {
161 obsoleteHierarchies.add(entry);
163 if (oldest == null || entry.getLastAccess() < oldest.getLastAccess()) {
168 if (!obsoleteHierarchies.isEmpty()) {
169 for (int i= 0; i < obsoleteHierarchies.size(); i++) {
170 removeHierarchyEntryFromCache(obsoleteHierarchies.get(i));
172 } else if (oldest != null) {
173 removeHierarchyEntryFromCache(oldest);
176 HierarchyCacheEntry newEntry= new HierarchyCacheEntry(hierarchy);
177 fgHierarchyCache.add(newEntry);
183 * Check if the given type is in the hierarchy cache.
185 * @return <code>true</code> if a hierarchy for the given type is cached
187 public static boolean hasInCache(IType type) {
188 return findTypeHierarchyInCache(type) != null;
192 private static ITypeHierarchy findTypeHierarchyInCache(IType type) {
193 synchronized (fgHierarchyCache) {
194 for (int i= fgHierarchyCache.size() - 1; i>= 0; i--) {
195 HierarchyCacheEntry curr= fgHierarchyCache.get(i);
196 ITypeHierarchy hierarchy= curr.getTypeHierarchy();
197 if (!hierarchy.exists()) {
198 removeHierarchyEntryFromCache(curr);
200 if (hierarchy.contains(type)) {
201 curr.markAsAccessed();
210 private static void removeHierarchyEntryFromCache(HierarchyCacheEntry entry) {
211 synchronized (fgHierarchyCache) {
212 removeMethodOverrideTester(entry.getTypeHierarchy());
214 fgHierarchyCache.remove(entry);
220 * Gets the number of times the hierarchy could be taken from the hierarchy.
221 * @return Returns a int
223 public static int getCacheHits() {
228 * Gets the number of times the hierarchy was build. Used for testing.
229 * @return Returns a int
231 public static int getCacheMisses() {
232 return fgCacheMisses;