blob: 0d720cc2894b4145ebdf70ace6167578008a4264 [file] [log] [blame]
package org.eclipse.osbp.xtext.oxtype.ui.imports;
import static com.google.common.collect.Lists.newArrayList;
import static org.eclipse.xtext.util.Strings.notNull;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor;
import org.eclipse.osbp.xtext.oxtype.imports.EObjectJvmLinkUsage;
import org.eclipse.osbp.xtext.oxtype.imports.EObjectUsage;
import org.eclipse.osbp.xtext.oxtype.imports.EObjectUsages;
import org.eclipse.osbp.xtext.oxtype.imports.IUnresolvedEObjectResolver;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.access.jdt.IJavaProjectProvider;
import org.eclipse.xtext.common.types.util.TypeReferences;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.util.IAcceptor;
import org.eclipse.xtext.xbase.conversion.XbaseQualifiedNameValueConverter;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
@SuppressWarnings("restriction")
public class InteractiveUnresolvedEClassResolver implements IUnresolvedEObjectResolver {
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(InteractiveUnresolvedEClassResolver.class);
@Inject
private EObjectChooser typeChooser;
@Inject
private IJavaProjectProvider projectProvider;
@Inject
private IScopeProvider scopeProvider;
@Inject
private IQualifiedNameProvider nameProvider;
@Inject
private TypeReferences typeRefs;
@Inject
XbaseQualifiedNameValueConverter valueConverter;
// @Inject
// private ISimilarityMatcher similarityMatcher;
public void resolve(EObjectUsages typeUsages, XtextResource resource) {
if (resource == null)
return;
// in the first step, handle all unresolved EObject imports "import
// ns"
Multimap<String, EObjectUsage> name2EObjectUsage = LinkedHashMultimap.create();
for (EObjectUsage unresolved : typeUsages.getUnresolvedEObjects()) {
name2EObjectUsage.put(unresolved.getUniqueKeyUnresolved(), unresolved);
}
for (String key : name2EObjectUsage.keySet()) {
Iterable<EObjectUsage> usages = name2EObjectUsage.get(key);
EObject resolvedType = resolve(usages, resource);
if (resolvedType != null && !resolvedType.eIsProxy()) {
for (EObjectUsage usage : usages) {
usage.resolve(resolvedType, nameProvider.getFullyQualifiedName(resolvedType), typeUsages);
}
}
}
// in the second step, handle all the JvmHelperLinks for
// "import org.my.foo.Bar"
Multimap<String, EObjectJvmLinkUsage> name2typeUsage = LinkedHashMultimap.create();
for (EObjectJvmLinkUsage unresolved : typeUsages.getUnresolvedJvmTypes()) {
QualifiedName text = unresolved.getTypeQualifiedName();
if (text != null) {
name2typeUsage.put(text.toString(), unresolved);
}
}
for (String name : name2typeUsage.keySet()) {
Iterable<EObjectJvmLinkUsage> usages = name2typeUsage.get(name);
JvmType resolvedType = resolveJvmLinks(name, usages, resource);
if (resolvedType != null) {
EObjectJvmLinkUsage first = usages.iterator().next();
for (EObjectJvmLinkUsage usage : usages)
typeUsages.addResolvedJvmLink(first.getParent(), resolvedType, usage.getTextRegion(),
usage.getContext(), usage.getCrossReferenceString());
}
}
}
protected EObject resolve(Iterable<EObjectUsage> usages, XtextResource resource) {
EObjectUsage first = usages.iterator().next();
if (first.getReference().getEReferenceType().getEPackage() == TypesPackage.eINSTANCE) {
return resolveOrphanedJvmTypes(usages, resource, first);
} else {
return resolveEObject(usages, resource, first);
}
}
private EObject resolveEObject(Iterable<EObjectUsage> usages, XtextResource resource, EObjectUsage first) {
IScope scope = scopeProvider.getScope(first.getContext(), first.getReference());
String name = valueConverter.toValue(first.getCrossReferenceString(), null);
List<EObject> candidateTypes = new ArrayList<EObject>();
for (IEObjectDescription desc : scope.getAllElements()) {
// for later use maybe
// if (similarityMatcher.isSimilar(name, desc.getQualifiedName()
// .getLastSegment())) {
if (name.equals(desc.getQualifiedName().getLastSegment())) {
EObject result = desc.getEObjectOrProxy();
if (result.eIsProxy()) {
result = EcoreUtil.resolve(desc.getEObjectOrProxy(), resource);
}
candidateTypes.add(result);
}
}
if (candidateTypes.isEmpty()) {
return null;
} else if (candidateTypes.size() == 1) {
return candidateTypes.get(0);
} else {
return typeChooser.choose(candidateTypes, usages, resource);
}
}
protected JvmType resolveJvmLinks(String name, Iterable<EObjectJvmLinkUsage> usages, XtextResource resource) {
EObjectJvmLinkUsage first = usages.iterator().next();
IScope scope = scopeProvider.getScope(first.getContext(), first.getReference());
EObjectUsage resolvedParent = first.getParent();
if (!resolvedParent.isResolved()) {
throw new IllegalStateException("Parent EObjectUsage must be resolved!");
}
// construct the qualified name of the JvmType
QualifiedName typeFQN = resolvedParent.getTypeQualifiedName().skipLast(1)
.append(first.getCrossReferenceString());
IEObjectDescription desc = scope.getSingleElement(typeFQN);
if (desc == null) {
return null;
}
EObject result = desc.getEObjectOrProxy();
if (result.eIsProxy()) {
result = EcoreUtil.resolve(desc.getEObjectOrProxy(), resource);
}
return (JvmType) result;
}
protected EObject resolveOrphanedJvmTypes(Iterable<EObjectUsage> usages, XtextResource resource,
EObjectUsage first) {
try {
IJavaSearchScope javaSearchScope = getJavaSearchScope(resource);
final List<EObject> candidateTypes = newArrayList();
EObject context = first.getContext();
findJvmCandidateTypes(context, first.getCrossReferenceString(), javaSearchScope,
new IAcceptor<JvmDeclaredType>() {
public void accept(JvmDeclaredType t) {
candidateTypes.add(t);
}
});
if (candidateTypes.isEmpty())
return null;
else if (candidateTypes.size() == 1)
return candidateTypes.get(0);
else
return typeChooser.choose(candidateTypes, usages, resource);
} catch (JavaModelException e) {
LOG.error("Error searching for type named '" + notNull(first.getCrossReferenceString()) + "'", e);
}
return null;
}
protected IJavaSearchScope getJavaSearchScope(XtextResource resource) {
IJavaProject javaProject = projectProvider.getJavaProject(resource.getResourceSet());
IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaProject });
return searchScope;
}
protected String getQualifiedTypeName(char[] packageName, char[][] enclosingTypeNames, char[] simpleTypeName) {
StringBuilder fqName = new StringBuilder(packageName.length + simpleTypeName.length + 1);
if (packageName.length != 0) {
fqName.append(packageName);
fqName.append('.');
}
for (char[] enclosingType : enclosingTypeNames) {
fqName.append(enclosingType);
fqName.append('.');
}
fqName.append(simpleTypeName);
String fqNameAsString = fqName.toString();
return fqNameAsString;
}
protected void findJvmCandidateTypes(final EObject context, final String typeSimpleName,
IJavaSearchScope searchScope, final IAcceptor<JvmDeclaredType> acceptor) throws JavaModelException {
BasicSearchEngine searchEngine = new BasicSearchEngine();
searchEngine.searchAllTypeNames(null, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
typeSimpleName.toCharArray(), SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
IJavaSearchConstants.TYPE, searchScope, new IRestrictedAccessTypeRequestor() {
public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName,
char[][] enclosingTypeNames, String path, AccessRestriction access) {
final String qualifiedTypeName = getQualifiedTypeName(packageName, enclosingTypeNames,
simpleTypeName);
if (access == null
|| (access.getProblemId() != IProblem.ForbiddenReference && !access.ignoreIfBetter())) {
JvmType importType = typeRefs.findDeclaredType(qualifiedTypeName, context.eResource());
if (importType instanceof JvmDeclaredType) {
acceptor.accept((JvmDeclaredType) importType);
}
}
}
}, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, new NullProgressMonitor());
}
}