001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 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.corext.util;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.Map;
015:
016: import org.eclipse.core.runtime.IProgressMonitor;
017:
018: import org.eclipse.jdt.core.IType;
019: import org.eclipse.jdt.core.ITypeHierarchy;
020: import org.eclipse.jdt.core.ITypeHierarchyChangedListener;
021: import org.eclipse.jdt.core.JavaModelException;
022:
023: public class SuperTypeHierarchyCache {
024:
025: private static class HierarchyCacheEntry implements
026: ITypeHierarchyChangedListener {
027:
028: private ITypeHierarchy fTypeHierarchy;
029: private long fLastAccess;
030:
031: public HierarchyCacheEntry(ITypeHierarchy hierarchy) {
032: fTypeHierarchy = hierarchy;
033: fTypeHierarchy.addTypeHierarchyChangedListener(this );
034: markAsAccessed();
035: }
036:
037: public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) {
038: removeHierarchyEntryFromCache(this );
039: }
040:
041: public ITypeHierarchy getTypeHierarchy() {
042: return fTypeHierarchy;
043: }
044:
045: public void markAsAccessed() {
046: fLastAccess = System.currentTimeMillis();
047: }
048:
049: public long getLastAccess() {
050: return fLastAccess;
051: }
052:
053: public void dispose() {
054: fTypeHierarchy.removeTypeHierarchyChangedListener(this );
055: fTypeHierarchy = null;
056: }
057:
058: /* (non-Javadoc)
059: * @see java.lang.Object#toString()
060: */
061: public String toString() {
062: return "Super hierarchy of: " + fTypeHierarchy.getType().getElementName(); //$NON-NLS-1$
063: }
064:
065: }
066:
067: private static final int CACHE_SIZE = 8;
068:
069: private static ArrayList fgHierarchyCache = new ArrayList(
070: CACHE_SIZE);
071: private static Map fgMethodOverrideTesterCache = new LRUMap(
072: CACHE_SIZE);
073:
074: private static int fgCacheHits = 0;
075: private static int fgCacheMisses = 0;
076:
077: /**
078: * Get a hierarchy for the given type
079: */
080: public static ITypeHierarchy getTypeHierarchy(IType type)
081: throws JavaModelException {
082: return getTypeHierarchy(type, null);
083: }
084:
085: public static MethodOverrideTester getMethodOverrideTester(
086: IType type) throws JavaModelException {
087: MethodOverrideTester test = null;
088: synchronized (fgMethodOverrideTesterCache) {
089: test = (MethodOverrideTester) fgMethodOverrideTesterCache
090: .get(type);
091: }
092: if (test == null) {
093: ITypeHierarchy hierarchy = getTypeHierarchy(type); // don't nest the locks
094: synchronized (fgMethodOverrideTesterCache) {
095: test = (MethodOverrideTester) fgMethodOverrideTesterCache
096: .get(type); // test again after waiting a long time for 'getTypeHierarchy'
097: if (test == null) {
098: test = new MethodOverrideTester(type, hierarchy);
099: fgMethodOverrideTesterCache.put(type, test);
100: }
101: }
102: }
103: return test;
104: }
105:
106: private static void removeMethodOverrideTester(
107: ITypeHierarchy hierarchy) {
108: synchronized (fgMethodOverrideTesterCache) {
109: for (Iterator iter = fgMethodOverrideTesterCache.values()
110: .iterator(); iter.hasNext();) {
111: MethodOverrideTester curr = (MethodOverrideTester) iter
112: .next();
113: if (curr.getTypeHierarchy().equals(hierarchy)) {
114: iter.remove();
115: }
116: }
117: }
118: }
119:
120: /**
121: * Get a hierarchy for the given type
122: */
123: public static ITypeHierarchy getTypeHierarchy(IType type,
124: IProgressMonitor progressMonitor) throws JavaModelException {
125: ITypeHierarchy hierarchy = findTypeHierarchyInCache(type);
126: if (hierarchy == null) {
127: fgCacheMisses++;
128: hierarchy = type.newSupertypeHierarchy(progressMonitor);
129: addTypeHierarchyToCache(hierarchy);
130: } else {
131: fgCacheHits++;
132: }
133: return hierarchy;
134: }
135:
136: private static void addTypeHierarchyToCache(ITypeHierarchy hierarchy) {
137: synchronized (fgHierarchyCache) {
138: int nEntries = fgHierarchyCache.size();
139: if (nEntries >= CACHE_SIZE) {
140: // find obsolete entries or remove entry that was least recently accessed
141: HierarchyCacheEntry oldest = null;
142: ArrayList obsoleteHierarchies = new ArrayList(
143: CACHE_SIZE);
144: for (int i = 0; i < nEntries; i++) {
145: HierarchyCacheEntry entry = (HierarchyCacheEntry) fgHierarchyCache
146: .get(i);
147: ITypeHierarchy curr = entry.getTypeHierarchy();
148: if (!curr.exists()
149: || hierarchy.contains(curr.getType())) {
150: obsoleteHierarchies.add(entry);
151: } else {
152: if (oldest == null
153: || entry.getLastAccess() < oldest
154: .getLastAccess()) {
155: oldest = entry;
156: }
157: }
158: }
159: if (!obsoleteHierarchies.isEmpty()) {
160: for (int i = 0; i < obsoleteHierarchies.size(); i++) {
161: removeHierarchyEntryFromCache((HierarchyCacheEntry) obsoleteHierarchies
162: .get(i));
163: }
164: } else if (oldest != null) {
165: removeHierarchyEntryFromCache(oldest);
166: }
167: }
168: HierarchyCacheEntry newEntry = new HierarchyCacheEntry(
169: hierarchy);
170: fgHierarchyCache.add(newEntry);
171: }
172: }
173:
174: /**
175: * Check if the given type is in the hierarchy
176: * @param type
177: * @return Return <code>true</code> if a hierarchy for the given type is cached.
178: */
179: public static boolean hasInCache(IType type) {
180: return findTypeHierarchyInCache(type) != null;
181: }
182:
183: private static ITypeHierarchy findTypeHierarchyInCache(IType type) {
184: synchronized (fgHierarchyCache) {
185: for (int i = fgHierarchyCache.size() - 1; i >= 0; i--) {
186: HierarchyCacheEntry curr = (HierarchyCacheEntry) fgHierarchyCache
187: .get(i);
188: ITypeHierarchy hierarchy = curr.getTypeHierarchy();
189: if (!hierarchy.exists()) {
190: removeHierarchyEntryFromCache(curr);
191: } else {
192: if (hierarchy.contains(type)) {
193: curr.markAsAccessed();
194: return hierarchy;
195: }
196: }
197: }
198: }
199: return null;
200: }
201:
202: private static void removeHierarchyEntryFromCache(
203: HierarchyCacheEntry entry) {
204: synchronized (fgHierarchyCache) {
205: removeMethodOverrideTester(entry.getTypeHierarchy());
206: entry.dispose();
207: fgHierarchyCache.remove(entry);
208: }
209: }
210:
211: /**
212: * Gets the number of times the hierarchy could be taken from the hierarchy.
213: * @return Returns a int
214: */
215: public static int getCacheHits() {
216: return fgCacheHits;
217: }
218:
219: /**
220: * Gets the number of times the hierarchy was build. Used for testing.
221: * @return Returns a int
222: */
223: public static int getCacheMisses() {
224: return fgCacheMisses;
225: }
226: }
|