/*******************************************************************************
* Copyright (c) 2007, 2009 Spring IDE Developers
* 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:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.core.java;
import java.util.ArrayList;
import java.util.List;
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;
/**
* Cache for {@link ITypeHierarchy} instances.
* <p>
* The implementation has been taken from {@link org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache}.
* @author Christian Dupuis
* @since 2.0.1
*/
public class SuperTypeHierarchyCache {
/**
* Internal cache entry
*/
private static class HierarchyCacheEntry implements ITypeHierarchyChangedListener {
private long lastAccess;
private ITypeHierarchy typeHierarchy;
public HierarchyCacheEntry(ITypeHierarchy hierarchy) {
typeHierarchy = hierarchy;
typeHierarchy.addTypeHierarchyChangedListener(this);
markAsAccessed();
}
public void dispose() {
typeHierarchy.removeTypeHierarchyChangedListener(this);
typeHierarchy = null;
}
public long getLastAccess() {
return lastAccess;
}
public ITypeHierarchy getTypeHierarchy() {
return typeHierarchy;
}
public void markAsAccessed() {
lastAccess = System.currentTimeMillis();
}
public void typeHierarchyChanged(ITypeHierarchy typeHierarchy) {
removeHierarchyEntryFromCache(this);
}
}
private static final int CACHE_SIZE = 50;
private static List<HierarchyCacheEntry> HIERACHY_CACHE = new ArrayList<HierarchyCacheEntry>(CACHE_SIZE);
private static void addTypeHierarchyToCache(ITypeHierarchy hierarchy) {
synchronized (HIERACHY_CACHE) {
int nEntries = HIERACHY_CACHE.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 = (HierarchyCacheEntry) HIERACHY_CACHE.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((HierarchyCacheEntry) obsoleteHierarchies.get(i));
}
}
else if (oldest != null) {
removeHierarchyEntryFromCache(oldest);
}
}
HierarchyCacheEntry newEntry = new HierarchyCacheEntry(hierarchy);
HIERACHY_CACHE.add(newEntry);
}
}
private static ITypeHierarchy findTypeHierarchyInCache(IType type) {
synchronized (HIERACHY_CACHE) {
for (int i = HIERACHY_CACHE.size() - 1; i >= 0; i--) {
HierarchyCacheEntry curr = (HierarchyCacheEntry) HIERACHY_CACHE.get(i);
ITypeHierarchy hierarchy = curr.getTypeHierarchy();
if (!hierarchy.exists()) {
removeHierarchyEntryFromCache(curr);
}
else {
if (hierarchy.contains(type)) {
curr.markAsAccessed();
return hierarchy;
}
}
}
}
return null;
}
/**
* Get the {@link ITypeHierarchy} for the given {@link IType}.
* <p>
* If no hierarchy can be found in the cache a new one will be created.
* <p>
* Calling this method is equivalent to calling {@link #getTypeHierarchy(IType, null)}.
* @param type the {@link IType} to get the super type hierarchy for
* @return the {@link ITypeHierarchy} for the given <code>type</code>
*/
public static ITypeHierarchy getTypeHierarchy(IType type) throws JavaModelException {
return getTypeHierarchy(type, null);
}
/**
* Get the {@link ITypeHierarchy} for the given {@link IType}.
* <p>
* If no hierarchy can be found in the cache a new one will be created.
* @param type the {@link IType} to get the super type hierarchy for
* @param progressMonitor a {@link IProgressMonitor} instance to report progress
* @return the {@link ITypeHierarchy} for the given <code>type</code>
*/
public static ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor progressMonitor)
throws JavaModelException {
ITypeHierarchy hierarchy = findTypeHierarchyInCache(type);
if (hierarchy == null) {
hierarchy = type.newTypeHierarchy(progressMonitor);
addTypeHierarchyToCache(hierarchy);
}
return hierarchy;
}
/**
* Checks if a {@link ITypeHierarchy} for a given {@link IType} is in the internal cache.
* @param type the {@link IType} to check if a {@link ITypeHierarchy} is cached
* @return true if a {@link ITypeHierarchy} is cached
*/
public static boolean hasInCache(IType type) {
return findTypeHierarchyInCache(type) != null;
}
private static void removeHierarchyEntryFromCache(HierarchyCacheEntry entry) {
synchronized (HIERACHY_CACHE) {
entry.dispose();
HIERACHY_CACHE.remove(entry);
}
}
}