blob: eda36456a391913a0fbb93ecbb0072f6ee0648be [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.sdk.util.internal.typecache;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeMap;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.core.search.TypeDeclarationMatch;
import org.eclipse.scout.commons.CompositeLong;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.sdk.util.internal.SdkUtilActivator;
import org.eclipse.scout.sdk.util.type.TypeUtility;
import org.eclipse.scout.sdk.util.typecache.ITypeCache;
public final class TypeCache implements ITypeCache {
private final static TypeCache INSTANCE = new TypeCache();
private final Object m_cacheLock;
private HashMap<String, ArrayList<IType>> m_cache;
private P_ResourceListener m_resourceChangeListener;
public static TypeCache getInstance() {
return INSTANCE;
}
private TypeCache() {
m_cache = new HashMap<String, ArrayList<IType>>();
m_cacheLock = new Object();
m_resourceChangeListener = new P_ResourceListener();
ResourcesPlugin.getWorkspace().addResourceChangeListener(m_resourceChangeListener);
}
@Override
public void dispose() {
if (m_cache != null) {
clearCache();
}
ResourcesPlugin.getWorkspace().removeResourceChangeListener(m_resourceChangeListener);
m_resourceChangeListener = null;
m_cache = null;
}
private void clearCache() {
synchronized (m_cacheLock) {
m_cache.clear();
}
}
public IType[] getAllCachedTypes() {
ArrayList<IType> types = new ArrayList<IType>();
synchronized (m_cacheLock) {
for (ArrayList<IType> lists : m_cache.values()) {
types.addAll(lists);
}
}
return types.toArray(new IType[types.size()]);
}
@Override
public IType getType(String typeName) {
ArrayList<IType> types = getTypesInternal(typeName);
if (types != null && types.size() > 0) {
return types.get(0);
}
return null;
}
@Override
public IType getType(String typeName, IJavaProject classpath) {
ArrayList<IType> types = getTypesInternal(typeName);
if (types != null) {
for (IType t : types) {
if (classpath.isOnClasspath(t)) {
return t;
}
}
}
return null;
}
@Override
public IType[] getTypes(String typeName) {
ArrayList<IType> types = getTypesInternal(typeName);
if (types == null) {
return new IType[]{};
}
return types.toArray(new IType[types.size()]);
}
@Override
public IType[] getTypes(String typeName, IJavaProject classpath) {
ArrayList<IType> types = getTypesInternal(typeName);
ArrayList<IType> result = new ArrayList<IType>(types.size());
if (types != null) {
for (IType t : types) {
if (classpath.isOnClasspath(t)) {
result.add(t);
}
}
}
return result.toArray(new IType[result.size()]);
}
private ArrayList<IType> getTypesInternal(String typeName) {
if (StringUtility.isNullOrEmpty(typeName)) {
return null;
}
typeName = typeName.replace('$', '.');
ArrayList<IType> types = null;
synchronized (m_cacheLock) {
types = m_cache.get(typeName);
if (types != null) {
if (types.size() > 0) {
// keep cache clean
Iterator<IType> it = types.iterator();
while (it.hasNext()) {
IType type = it.next();
if (!TypeUtility.exists(type)) {
it.remove();
}
}
}
if (types.size() == 0) {
m_cache.remove(typeName);
types = null;
}
}
}
if (types == null) {
// search the type
try {
types = resolveType(typeName);
}
catch (CoreException e) {
SdkUtilActivator.logError("error resolving type '" + typeName + "'.", e);
}
synchronized (m_cacheLock) {
if (types != null && types.size() > 0) {
m_cache.put(typeName, types);
}
}
}
return types;
}
private ArrayList<IType> resolveType(final String fqn) throws CoreException {
final TreeMap<CompositeLong, IType> matchList = new TreeMap<CompositeLong, IType>();
//speed tuning, only search for last component of pattern, remaining checks are done in accept
String fastPat = fqn;
int i = fastPat.lastIndexOf('.');
if (i >= 0) {
fastPat = fastPat.substring(i + 1);
}
if (!StringUtility.hasText(fastPat)) {
return null;
}
new SearchEngine().search(
SearchPattern.createPattern(fastPat, IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH),
new SearchParticipant[]{SearchEngine.getDefaultSearchParticipant()},
SearchEngine.createWorkspaceScope(),
new SearchRequestor() {
@Override
public final void acceptSearchMatch(SearchMatch match) throws CoreException {
if (match instanceof TypeDeclarationMatch) {
TypeDeclarationMatch typeMatch = (TypeDeclarationMatch) match;
IType t = (IType) typeMatch.getElement();
if (TypeUtility.exists(t) && t.getFullyQualifiedName('.').indexOf(fqn) >= 0) {
matchList.put(new CompositeLong(t.isBinary() ? 1 : 0, matchList.size()), t);
}
}
}
},
null
);
return new ArrayList<IType>(matchList.values());
}
private final class P_ResourceListener implements IResourceChangeListener {
@Override
public void resourceChanged(final IResourceChangeEvent event) {
IResourceDelta delta = event.getDelta();
try {
if (delta != null) {
delta.accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(IResourceDelta visitDelta) {
IResource resource = visitDelta.getResource();
if (resource.getType() == IResource.PROJECT && ((visitDelta.getFlags() & (IResourceDelta.OPEN | IResourceDelta.REMOVED)) != 0)) {
clearCache();
return false;
}
return true;
}
});
}
else if (event.getType() == IResourceChangeEvent.PRE_DELETE && event.getResource().getType() == IResource.PROJECT) {
clearCache();
}
}
catch (CoreException e) {
SdkUtilActivator.logWarning(e);
}
}
}
}