blob: 02d448a02e209698e7edfd36bb5c3215e8cbbeb1 [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.io.Serializable;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
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.IType;
import org.eclipse.jdt.core.Signature;
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.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 static final TypeCache INSTANCE = new TypeCache();
private static final Comparator<IType> COMPARATOR = new P_TypeMatchComparator();
private static final String PDE_BUNDLE_POOL_IDENTIFYER = ".bundle_pool";
private final Map<String, TreeSet<IType>> m_cache;
private final P_ResourceListener m_resourceChangeListener;
public static TypeCache getInstance() {
return INSTANCE;
}
private TypeCache() {
m_cache = new HashMap<String, TreeSet<IType>>();
m_resourceChangeListener = new P_ResourceListener();
ResourcesPlugin.getWorkspace().addResourceChangeListener(m_resourceChangeListener);
}
@Override
public void dispose() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(m_resourceChangeListener);
clearCache();
}
public synchronized void clearCache() {
m_cache.clear();
}
public synchronized Set<IType> getAllCachedTypes() {
Set<IType> types = new HashSet<IType>();
for (Set<IType> lists : m_cache.values()) {
types.addAll(lists);
}
return types;
}
@Override
public IType getType(String typeName) {
TreeSet<IType> types = getTypesInternal(typeName);
if (types != null && types.size() > 0) {
return types.first();
}
return null;
}
@Override
public Set<IType> getTypes(String typeName) {
TreeSet<IType> types = getTypesInternal(typeName);
if (types == null) {
return new TreeSet<IType>();
}
return new TreeSet<IType>(types);
}
private TreeSet<IType> getTypesInternal(String typeName) {
if (StringUtility.isNullOrEmpty(typeName)) {
return null;
}
typeName = typeName.replace('$', '.');
TreeSet<IType> types = null;
synchronized (this) {
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);
synchronized (this) {
if (types != null && types.size() > 0) {
m_cache.put(typeName, types);
}
}
}
catch (CoreException e) {
SdkUtilActivator.logError("error resolving type '" + typeName + "'.", e);
}
}
return types;
}
private TreeSet<IType> resolveType(final String fqn) throws CoreException {
//speed tuning, only search for last component of pattern, remaining checks are done in accept
String fastPat = Signature.getSimpleName(fqn);
final TreeSet<IType> matchList = new TreeSet<IType>(COMPARATOR);
new SearchEngine().search(
SearchPattern.createPattern(fastPat, IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH),
new SearchParticipant[]{SearchEngine.getDefaultSearchParticipant()},
SearchEngine.createWorkspaceScope(),
new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object element = match.getElement();
if (element instanceof IType) {
IType t = (IType) element;
if (t.getFullyQualifiedName('.').indexOf(fqn) >= 0) {
matchList.add(t);
}
}
}
},
null
);
return matchList;
}
private static final class P_TypeMatchComparator implements Comparator<IType>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public int compare(IType o1, IType o2) {
if (o1 == o2) {
return 0;
}
// favor types in the workspace
boolean b1 = o1.isBinary();
boolean b2 = o2.isBinary();
if (b1 != b2) {
if (b1) {
return 1;
}
else {
return -1;
}
}
String path1 = buildPath(o1);
String path2 = buildPath(o2);
// favor types in the bundle_pool
boolean p1 = path1.contains(PDE_BUNDLE_POOL_IDENTIFYER);
boolean p2 = path2.contains(PDE_BUNDLE_POOL_IDENTIFYER);
if (p1 != p2) {
if (p1) {
return -1;
}
else {
return 1;
}
}
// descending (newest first)
return path2.compareTo(path1);
}
private String buildPath(IType t) {
String fqn = t.getFullyQualifiedName();
String portableString = t.getPath().toPortableString();
StringBuilder sb = new StringBuilder(fqn.length() + portableString.length());
sb.append(fqn);
sb.append(portableString);
return sb.toString();
}
}
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 resource.getType() > IResource.PROJECT;// stop visiting at project level
}
});
}
else if (event.getType() == IResourceChangeEvent.PRE_DELETE && event.getResource().getType() == IResource.PROJECT) {
clearCache();
}
}
catch (CoreException e) {
SdkUtilActivator.logWarning(e);
}
}
}
}