package org.objectstyle.wolips.core.resources.types; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.ITypeHierarchyChangedListener; import org.eclipse.jdt.core.JavaModelException; public class SubTypeHierarchyCache { private static class HierarchyCacheEntry implements ITypeHierarchyChangedListener { private IJavaProject fProject; private ITypeHierarchy fTypeHierarchy; private long fLastAccess; public HierarchyCacheEntry(ITypeHierarchy hierarchy, IJavaProject project) { fTypeHierarchy = hierarchy; fTypeHierarchy.addTypeHierarchyChangedListener(this); fProject = project; markAsAccessed(); } public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) { removeHierarchyEntryFromCache(this); } public ITypeHierarchy getTypeHierarchy() { return fTypeHierarchy; } public void markAsAccessed() { fLastAccess = System.currentTimeMillis(); } public long getLastAccess() { return fLastAccess; } public IJavaProject getProject() { return fProject; } public void dispose() { fTypeHierarchy.removeTypeHierarchyChangedListener(this); fTypeHierarchy = null; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Sub hierarchy of: " + fTypeHierarchy.getType().getElementName(); //$NON-NLS-1$ } } private static final int CACHE_SIZE = 24; private static List<HierarchyCacheEntry> fgHierarchyCache = new ArrayList<HierarchyCacheEntry>(CACHE_SIZE); private static int fgCacheHits = 0; private static int fgCacheMisses = 0; /** * Get a hierarchy for the given type */ public static ITypeHierarchy getTypeHierarchyInProject(IType type, IJavaProject project) throws JavaModelException { return getTypeHierarchyInProject(type, project, null); } /** * Get a hierarchy for the given type */ public static ITypeHierarchy getTypeHierarchyInProject(IType type, IJavaProject project, IProgressMonitor progressMonitor) throws JavaModelException { ITypeHierarchy hierarchy = findTypeHierarchyInProjectInCache(type, project); if (hierarchy == null) { fgCacheMisses++; if (project != null) { hierarchy = type.newTypeHierarchy(project, progressMonitor); } else { hierarchy = type.newTypeHierarchy(progressMonitor); } addTypeHierarchyInProjectToCache(hierarchy, project); } else { fgCacheHits++; } return hierarchy; } private static void addTypeHierarchyInProjectToCache(ITypeHierarchy hierarchy, IJavaProject project) { synchronized (fgHierarchyCache) { int nEntries = fgHierarchyCache.size(); if (nEntries >= CACHE_SIZE) { // find obsolete entries or remove entry that was least recently accessed HierarchyCacheEntry oldest = null; List<HierarchyCacheEntry> obsoleteHierarchies = new ArrayList<HierarchyCacheEntry>(CACHE_SIZE); for (int i = 0; i < nEntries; i++) { HierarchyCacheEntry entry = fgHierarchyCache.get(i); ITypeHierarchy curr = entry.getTypeHierarchy(); if (!curr.exists() || hierarchy.contains(curr.getType())) { obsoleteHierarchies.add(entry); } else { if (oldest == null || entry.getLastAccess() < oldest.getLastAccess()) { oldest = entry; } } } if (!obsoleteHierarchies.isEmpty()) { for (int i = 0; i < obsoleteHierarchies.size(); i++) { removeHierarchyEntryFromCache(obsoleteHierarchies.get(i)); } } else if (oldest != null) { removeHierarchyEntryFromCache(oldest); } } HierarchyCacheEntry newEntry = new HierarchyCacheEntry(hierarchy, project); fgHierarchyCache.add(newEntry); } } /** * Check if the given type is in the hierarchy * @param type * @return Return <code>true</code> if a hierarchy for the given type is cached. */ public static boolean hasInCacheInProject(IType type, IJavaProject project) { return findTypeHierarchyInProjectInCache(type, project) != null; } private static ITypeHierarchy findTypeHierarchyInProjectInCache(IType type, IJavaProject project) { synchronized (fgHierarchyCache) { for (int i = fgHierarchyCache.size() - 1; i >= 0; i--) { HierarchyCacheEntry curr = fgHierarchyCache.get(i); ITypeHierarchy hierarchy = curr.getTypeHierarchy(); if (!hierarchy.exists()) { removeHierarchyEntryFromCache(curr); } else { if (((project == null && curr.getProject() == null) || (project != null && project.equals(curr.getProject()))) && hierarchy.contains(type)) { curr.markAsAccessed(); return hierarchy; } } } } return null; } static void removeHierarchyEntryFromCache(HierarchyCacheEntry entry) { synchronized (fgHierarchyCache) { entry.dispose(); fgHierarchyCache.remove(entry); } } /** * Gets the number of times the hierarchy could be taken from the hierarchy. * @return Returns a int */ public static int getCacheHits() { return fgCacheHits; } /** * Gets the number of times the hierarchy was build. Used for testing. * @return Returns a int */ public static int getCacheMisses() { return fgCacheMisses; } }