blob: 961e6e102e675917c36211a70e00c4bb6774e9bd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.util;
import java.util.ArrayList;
import org.eclipse.core.runtime.IProgressMonitor;
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 SuperTypeHierarchyCache {
private static class HierarchyCacheEntry implements ITypeHierarchyChangedListener {
private ITypeHierarchy fTypeHierarchy;
private long fLastAccess;
public HierarchyCacheEntry(ITypeHierarchy hierarchy) {
fTypeHierarchy= hierarchy;
fTypeHierarchy.addTypeHierarchyChangedListener(this);
markAsAccessed();
}
public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) {
synchronized (fgHierarchyCache) {
freeHierarchy();
}
}
public ITypeHierarchy getTypeHierarchy() {
return fTypeHierarchy;
}
public void markAsAccessed() {
fLastAccess= System.currentTimeMillis();
}
public long getLastAccess() {
return fLastAccess;
}
public void freeHierarchy() {
fTypeHierarchy.removeTypeHierarchyChangedListener(this);
fgHierarchyCache.remove(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return "Superhierarchy of: " + fTypeHierarchy.getType().getElementName(); //$NON-NLS-1$
}
}
private static final int CACHE_SIZE= 8;
private static ArrayList fgHierarchyCache= new ArrayList(CACHE_SIZE);
private static int fgCacheHits= 0;
private static int fgCacheMisses= 0;
/**
* Get a hierarchy for the given type
*/
public static ITypeHierarchy getTypeHierarchy(IType type) throws JavaModelException {
return getTypeHierarchy(type, null);
}
/**
* Get a hierarchy for the given type
*/
public static ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor progressMonitor) throws JavaModelException {
ITypeHierarchy hierarchy= findTypeHierarchyInCache(type);
if (hierarchy == null) {
fgCacheMisses++;
hierarchy= type.newSupertypeHierarchy(progressMonitor);
addTypeHierarchyToCache(hierarchy);
} else {
fgCacheHits++;
}
return hierarchy;
}
private static void addTypeHierarchyToCache(ITypeHierarchy hierarchy) {
synchronized (fgHierarchyCache) {
int nEntries= fgHierarchyCache.size();
if (nEntries >= CACHE_SIZE) {
// find obsolete entries or remove entry that was least recently accessed
HierarchyCacheEntry oldest= null;
ArrayList obsoleteHierarchies= new ArrayList(CACHE_SIZE);
for (int i= 0; i < nEntries; i++) {
HierarchyCacheEntry entry= (HierarchyCacheEntry) 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++) {
((HierarchyCacheEntry) obsoleteHierarchies.get(i)).freeHierarchy();
}
} else if (oldest != null) {
oldest.freeHierarchy();
}
}
HierarchyCacheEntry newEntry= new HierarchyCacheEntry(hierarchy);
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 hasInCache(IType type) {
return findTypeHierarchyInCache(type) != null;
}
private static ITypeHierarchy findTypeHierarchyInCache(IType type) {
synchronized (fgHierarchyCache) {
for (int i= fgHierarchyCache.size() - 1; i>= 0; i--) {
HierarchyCacheEntry curr= (HierarchyCacheEntry) fgHierarchyCache.get(i);
ITypeHierarchy hierarchy= curr.getTypeHierarchy();
if (!hierarchy.exists()) {
curr.freeHierarchy();
} else {
if (hierarchy.contains(type)) {
curr.markAsAccessed();
return hierarchy;
}
}
}
}
return null;
}
/**
* 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;
}
}