/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* 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:
* * Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.library.lang.java.m3.internal;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MemberRef;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.MethodRef;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
public class BindingsResolver {
private TypeStore store;
private final IValueFactory values = ValueFactoryFactory.getValueFactory();
private final TypeFactory tf = TypeFactory.getInstance();
private final boolean collectBindings;
private final Map<String, Integer> anonymousClassCounter = new HashMap<String, Integer>();
private final Map<String, String> resolvedAnonymousClasses = new HashMap<String, String>();
private final Map<ISourceLocation, Integer> initializerCounter = new HashMap<ISourceLocation, Integer>();
private final Map<Initializer, ISourceLocation> initializerLookUp = new HashMap<>();
private org.rascalmpl.value.type.Type typeSymbol;
private final Map<String, ISourceLocation> locationCache;
private final boolean debug = false;
BindingsResolver(final TypeStore store, Map<String, ISourceLocation> cache, boolean collectBindings) {
this.collectBindings = collectBindings;
this.store = store;
this.locationCache = cache;
}
public ISourceLocation resolveBinding(ASTNode node, boolean tryHard) {
if (collectBindings) {
if (node instanceof TypeDeclaration) {
return resolveBinding(((TypeDeclaration) node).resolveBinding());
} else if (node instanceof EnumDeclaration) {
return resolveBinding(((EnumDeclaration) node).resolveBinding());
} else if (node instanceof AnnotationTypeDeclaration) {
return resolveBinding(((AnnotationTypeDeclaration) node).resolveBinding());
} else if (node instanceof AnnotationTypeMemberDeclaration) {
return resolveBinding(((AnnotationTypeMemberDeclaration) node).resolveBinding());
} else if (node instanceof AnonymousClassDeclaration) {
return resolveBinding(((AnonymousClassDeclaration) node).resolveBinding());
} else if (node instanceof EnumConstantDeclaration) {
return resolveBinding(((EnumConstantDeclaration) node).resolveVariable());
} else if (node instanceof ClassInstanceCreation) {
return resolveBinding(((ClassInstanceCreation) node).resolveConstructorBinding());
} else if (node instanceof FieldAccess) {
return resolveFieldAccess((FieldAccess) node);
} else if (node instanceof MethodInvocation) {
return resolveBinding(((MethodInvocation) node).resolveMethodBinding());
} else if (node instanceof QualifiedName) {
return resolveQualifiedName((QualifiedName) node);
} else if (node instanceof SimpleName) {
return resolveSimpleName(node, tryHard);
} else if (node instanceof SuperFieldAccess) {
return resolveBinding(((SuperFieldAccess) node).resolveFieldBinding());
} else if (node instanceof SuperMethodInvocation) {
return resolveBinding(((SuperMethodInvocation) node).resolveMethodBinding());
} else if (node instanceof MemberRef) {
return resolveBinding(((MemberRef) node).resolveBinding());
} else if (node instanceof MethodDeclaration) {
return resolveBinding(((MethodDeclaration) node).resolveBinding());
} else if (node instanceof MethodRef) {
return resolveBinding(((MethodRef) node).resolveBinding());
} else if (node instanceof PackageDeclaration) {
return resolveBinding(((PackageDeclaration) node).resolveBinding());
} else if (node instanceof Type) {
return resolveBinding(((Type) node).resolveBinding());
} else if (node instanceof TypeParameter) {
return resolveBinding(((TypeParameter) node).resolveBinding());
} else if (node instanceof VariableDeclaration) {
return resolveVariable(node);
} else if (node instanceof ConstructorInvocation) {
return resolveBinding(((ConstructorInvocation) node).resolveConstructorBinding());
} else if (node instanceof SuperConstructorInvocation) {
return resolveBinding(((SuperConstructorInvocation) node).resolveConstructorBinding());
} else if (node instanceof TypeDeclarationStatement) {
return resolveBinding(((TypeDeclarationStatement) node).resolveBinding());
} else if (node instanceof Initializer) {
return resolveInitializer((Initializer) node);
}
}
return makeBinding("unknown", null, null);
}
private ISourceLocation resolveVariable(ASTNode node) {
VariableDeclaration n = (VariableDeclaration) node;
IVariableBinding bin = n.resolveBinding();
ISourceLocation result = resolveBinding(n.resolveBinding());
// Have to move towards parent to make the binding unique
if (result.getScheme() == "unresolved") {
result = resolveBinding(n.getParent(), bin, n.getName());
}
return result;
}
private ISourceLocation resolveSimpleName(ASTNode node, boolean tryHard) {
SimpleName n = (SimpleName) node;
IBinding resolveBinding = n.resolveBinding();
ISourceLocation result = resolveBinding(resolveBinding);
// Have to move towards parent to make the binding unique
if (result.getScheme() == "unresolved" && tryHard) {
result = resolveBinding(n.getParent(), resolveBinding, n);
}
return result;
}
private ISourceLocation resolveFieldAccess(FieldAccess node) {
ITypeBinding tb = node.getExpression().resolveTypeBinding();
if (tb != null && tb.isArray() && "length".equals(node.getName().getIdentifier())) {
ISourceLocation arrayType = resolveBinding(tb);
return makeBinding("java+arrayLength", arrayType.getAuthority(), arrayType.getPath());
}
return resolveBinding(node.resolveFieldBinding());
}
private ISourceLocation resolveBinding(ASTNode parentNode, IBinding resolvedBinding, SimpleName nodeName) {
ISourceLocation parentBinding = resolveBinding(parentNode, false);
// Something has to declare it!!!
while(parentBinding.getScheme().equals("unknown") || parentBinding.getScheme().equals("unresolved")) {
if (parentNode == null) {
//truely unresolved/unknown
return makeBinding("unresolved", null, null);
}
parentNode = parentNode.getParent();
parentBinding = resolveBinding(parentNode, false);
}
// TODO: @ashimshahi please check this additional null check and the way we return an unresolved binding here
if (resolvedBinding == null) {
return makeBinding("unresolved", null, null);
}
String key = resolvedBinding.getKey();
// Binding keys for initializers are not unique so we always force them to be recomputed
if (!(parentNode instanceof Initializer)) {
if (locationCache.containsKey(key)) {
return locationCache.get(key);
}
}
String qualifiedName = parentBinding.getPath();
String[] bindingKeys = key.split("#");
if (bindingKeys.length > 2) {
// scoped variable that may have the same name as some other scoped variable
for (int i = 1; i < bindingKeys.length - 1; i++) {
if (!qualifiedName.endsWith("/"))
qualifiedName += "/";
qualifiedName += "scope("+bindingKeys[i]+")/";
}
}
// FIXME: May not be variable only!!!
ISourceLocation childBinding = makeBinding("java+variable", null, qualifiedName.concat(nodeName.getIdentifier()));
locationCache.put(key, childBinding);
return childBinding;
}
private ISourceLocation resolveQualifiedName(QualifiedName node) {
ITypeBinding tb = node.getQualifier().resolveTypeBinding();
if (tb != null && tb.isArray() && "length".equals(node.getName().getIdentifier())) {
ISourceLocation arrayType = resolveBinding(tb);
return makeBinding("java+arrayLength", arrayType.getAuthority(), arrayType.getPath());
}
return resolveBinding(node.getName(), false);
}
private ISourceLocation resolveInitializer(Initializer node) {
if (initializerLookUp.containsKey(node)) {
return initializerLookUp.get(node);
}
int initCounter = 1;
ISourceLocation parent = resolveBinding(node.getParent(), true);
if (initializerCounter.containsKey(parent)) {
initCounter = initializerCounter.get(parent) + 1;
}
initializerCounter.put(parent, initCounter);
String key = "";
for (String storedKey: locationCache.keySet()) {
if (locationCache.get(storedKey).equals(parent)) {
key = storedKey;
break;
}
}
key += "$initializer" + initCounter;
ISourceLocation result = makeBinding("java+initializer", null, parent.getPath() + "$initializer" + initCounter);
locationCache.put(key, result);
initializerLookUp.put(node, result);
return result;
}
public ISourceLocation resolveBinding(IBinding binding) {
if (binding == null) {
return makeBinding("unresolved", null, null);
}
if (binding instanceof ITypeBinding) {
return resolveBinding((ITypeBinding) binding);
} else if (binding instanceof IMethodBinding) {
return resolveBinding((IMethodBinding) binding);
} else if (binding instanceof IPackageBinding) {
return resolveBinding((IPackageBinding) binding);
} else if (binding instanceof IVariableBinding) {
return resolveBinding((IVariableBinding) binding);
}
return makeBinding("unknown", null, null);
}
public IConstructor resolveType(IBinding binding, boolean isDeclaration) {
if (debug) {
System.err.println("resolving " + binding);
}
IConstructor result = unresolvedSym();
if (binding != null) {
ISourceLocation uri = resolveBinding(binding);
if (binding instanceof ITypeBinding) {
return computeTypeSymbol(uri, (ITypeBinding) binding, isDeclaration);
} else if (binding instanceof IMethodBinding) {
return computeMethodTypeSymbol(uri, (IMethodBinding) binding, isDeclaration);
} else if (binding instanceof IVariableBinding) {
return resolveType(((IVariableBinding) binding).getType(), isDeclaration);
}
}
return result;
}
private IConstructor computeMethodTypeSymbol(ISourceLocation decl, IMethodBinding binding, boolean isDeclaration) {
IList parameters = computeTypes(isDeclaration ? binding.getParameterTypes() : binding.getTypeArguments(), false);
if (binding.isConstructor()) {
return constructorSymbol(decl, parameters);
} else {
IList typeParameters = computeTypes(isDeclaration ? binding.getTypeParameters() : binding.getTypeArguments(), isDeclaration);
IConstructor retSymbol = resolveType(binding.getReturnType(), false);
return methodSymbol(decl, typeParameters, retSymbol, parameters);
}
}
private IList computeTypes(ITypeBinding[] bindings, boolean isDeclaration) {
IListWriter parameters = values.listWriter();
for (ITypeBinding parameterType: bindings) {
IConstructor arg = resolveType(parameterType, isDeclaration);
parameters.append(arg);
}
return parameters.done();
}
private org.rascalmpl.value.type.Type getTypeSymbol() {
if (typeSymbol == null) {
typeSymbol = store.lookupAbstractDataType("TypeSymbol");
}
return typeSymbol;
}
private IConstructor methodSymbol(ISourceLocation decl, IList typeParameters, IConstructor retSymbol, IList parameters) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "method", tf.tupleType(decl.getType(), typeParameters.getType(), retSymbol.getType(), parameters.getType()));
return values.constructor(cons, decl, typeParameters, retSymbol, parameters);
}
private IConstructor constructorSymbol(ISourceLocation decl, IList parameters) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "constructor", tf.tupleType(decl.getType(), parameters.getType()));
return values.constructor(cons, decl, parameters);
}
private IConstructor parameterNode(ISourceLocation decl, ITypeBinding[] bound, boolean isDeclaration, boolean isUpperbound) {
IConstructor boundSym = unboundedSym();
if (isDeclaration) {
if (bound.length > 0) {
boundSym = boundSymbol(bound, isDeclaration, isUpperbound);
}
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "typeParameter", tf.tupleType(decl.getType(), boundSym.getType()));
return values.constructor(cons, decl, boundSym);
}
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "typeArgument", tf.tupleType(decl.getType()));
return values.constructor(cons, decl);
}
private IConstructor unboundedSym() {
org.rascalmpl.value.type.Type boundType = store.lookupAbstractDataType("Bound");
org.rascalmpl.value.type.Type cons = store.lookupConstructor(boundType, "unbounded", tf.voidType());
return values.constructor(cons);
}
private IConstructor unresolvedSym() {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "unresolved", tf.voidType());
return values.constructor(cons);
}
private Set<ITypeBinding> wildCardBoundsVisited = new HashSet<>();
private IConstructor computeTypeSymbol(ISourceLocation decl, ITypeBinding binding, boolean isDeclaration) {
if (binding.isPrimitive()) {
return primitiveSymbol(binding.getName());
}
else if (binding.isArray()) {
return arraySymbol(resolveType(binding.getComponentType(), isDeclaration), binding.getDimensions());
}
else if (binding.isNullType()) {
return nullSymbol();
}
else if (binding.isEnum()) {
return enumSymbol(decl);
}
else if (binding.isTypeVariable()) {
return parameterNode(decl, binding.getTypeBounds(), isDeclaration, true);
}
else if (binding.isWildcardType()) {
/*
* For wildcards the isUpperbound() information is only correct in the binding here.
* The previous implementation tried to look for that information in the type binding of the bounds,
* which wasn't correct.
*/
ITypeBinding bound = binding.getBound();
if (bound == null) {
return wildcardSymbol(unboundedSym());
}
else {
return wildcardSymbol(boundSymbol(new ITypeBinding[] { bound }, isDeclaration, binding.isUpperbound()));
}
}
else if (binding.isClass()) {
return classSymbol(decl, computeTypes(isDeclaration ? binding.getTypeParameters() : binding.getTypeArguments(), isDeclaration));
}
else if (binding.isCapture()) {
ITypeBinding[] typeBounds = binding.getTypeBounds();
ITypeBinding wildcard = binding.getWildcard();
if (typeBounds.length == 0) {
return captureSymbol(unboundedSym(), resolveType(wildcard, isDeclaration));
}
else {
// the type graph has cycles in case of abstract classes which extend some wildcard like class X extends ? extends Enum<?>.
// we fix this by recording the bounds on the stack and if we end up in a binding which we are already resolving we bail out.
for (ITypeBinding b : typeBounds) {
if (wildCardBoundsVisited.contains(b)) {
return captureSymbol(unboundedSym(), resolveType(wildcard, isDeclaration));
}
wildCardBoundsVisited.add(b);
}
IConstructor res = captureSymbol(boundSymbol(typeBounds, isDeclaration, true), resolveType(wildcard, isDeclaration));
for (ITypeBinding b : typeBounds) {
wildCardBoundsVisited.remove(b);
}
return res;
}
}
else if (binding.isInterface()) {
return interfaceSymbol(decl, computeTypes(isDeclaration ? binding.getTypeParameters() : binding.getTypeArguments(), isDeclaration));
}
return null;
}
private IConstructor captureSymbol(IConstructor bound, IConstructor wildcard) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "capture", tf.tupleType(bound.getType(), wildcard.getType()));
return values.constructor(cons, bound, wildcard);
}
private IConstructor wildcardSymbol(IConstructor boundSymbol) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "wildcard", tf.tupleType(boundSymbol.getType()));
return values.constructor(cons, boundSymbol);
}
static int stackdepth =0;
private IConstructor boundSymbol(ITypeBinding[] bound, boolean isDeclaration, boolean isUpperbound) {
// Assumption: Anything appearing in a bound symbol is not a declaration
stackdepth++;
IList boundSym = computeTypes(bound, false);
if (stackdepth > 5) {
System.err.println("100!");
}
stackdepth--;
org.rascalmpl.value.type.Type boundType = store.lookupAbstractDataType("Bound");
if (!isUpperbound) {
org.rascalmpl.value.type.Type sup = store.lookupConstructor(boundType, "super", tf.tupleType(boundSym.getType()));
return values.constructor(sup, boundSym);
}
else {
org.rascalmpl.value.type.Type ext = store.lookupConstructor(boundType, "extends", tf.tupleType(boundSym.getType()));
return values.constructor(ext, boundSym);
}
}
private IConstructor enumSymbol(ISourceLocation decl) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "enum", tf.tupleType(decl.getType()));
return values.constructor(cons, decl);
}
private IConstructor interfaceSymbol(ISourceLocation decl, IList typeParameters) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "interface", tf.tupleType(decl.getType(), typeParameters.getType()));
return values.constructor(cons, decl, typeParameters);
}
private IConstructor classSymbol(ISourceLocation decl, IList typeParameters) {
if (decl.getPath().equals("/java/lang/Object")) {
org.rascalmpl.value.type.Type obj = store.lookupConstructor(getTypeSymbol(), "object", tf.voidType());
return values.constructor(obj);
}
else {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "class", tf.tupleType(decl.getType(), typeParameters.getType()));
return values.constructor(cons, decl, typeParameters);
}
}
private IConstructor nullSymbol() {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "null", tf.voidType());
return values.constructor(cons);
}
private IConstructor primitiveSymbol(String name) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), name, tf.voidType());
return values.constructor(cons);
}
private IConstructor arraySymbol(IConstructor elem, int dimensions) {
org.rascalmpl.value.type.Type cons = store.lookupConstructor(getTypeSymbol(), "array", tf.tupleType(elem.getType(), tf.integerType()));
return values.constructor(cons, elem, values.integer(dimensions));
}
private ISourceLocation resolveBinding(IMethodBinding binding) {
if (binding == null) {
return makeBinding("unresolved", null, null);
}
if (locationCache.containsKey(binding.getKey()))
return locationCache.get(binding.getKey());
String signature = resolveBinding(binding.getDeclaringClass()).getPath();
if (!signature.isEmpty()) {
signature = signature.concat("/");
}
String params = "";
for (ITypeBinding parameterType: binding.getMethodDeclaration().getParameterTypes()) {
if (!params.isEmpty()) {
params = params.concat(",");
}
if (parameterType.isTypeVariable()) {
params = params.concat(parameterType.getName());
}
else {
params = params.concat(getPath(resolveBinding(parameterType)).replaceAll("/", "."));
}
}
signature = signature.concat(binding.getMethodDeclaration().getName() + "(" + params + ")");
String scheme = "unknown";
if (binding.isConstructor()) {
scheme = "java+constructor";
} else {
scheme = "java+method";
}
ISourceLocation result = makeBinding(scheme, null, signature);
locationCache.put(binding.getKey(), result);
return result;
}
private ISourceLocation resolveBinding(IPackageBinding binding) {
if (binding == null) {
return makeBinding("unresolved", null, null);
}
if (locationCache.containsKey(binding.getKey()))
return locationCache.get(binding.getKey());
ISourceLocation result = makeBinding("java+package", null, binding.getName().replaceAll("\\.", "/"));
locationCache.put(binding.getKey(), result);
return result;
}
private ISourceLocation resolveBinding(ITypeBinding binding) {
try {
if (binding == null) {
return makeBinding("unresolved", null, null);
}
if (locationCache.containsKey(binding.getKey())) {
return locationCache.get(binding.getKey());
}
String scheme = binding.isInterface() ? "java+interface" : "java+class";
String qualifiedName = binding.getTypeDeclaration().getQualifiedName();
if (qualifiedName.isEmpty()) {
if (binding.getDeclaringMethod() != null) {
qualifiedName = resolveBinding(binding.getDeclaringMethod()).getPath();
}
else if (binding.getDeclaringClass() != null) {
qualifiedName = resolveBinding(binding.getDeclaringClass()).getPath();
}
else {
if (debug) {
System.err.println("No defining class or method for " + binding.getClass().getCanonicalName());
System.err.println("Most probably anonymous class in initializer");
}
}
}
if (binding.isEnum()) {
scheme = "java+enum";
}
if (binding.isArray()) {
scheme = "java+array";
}
if (binding.isTypeVariable()) {
scheme = "java+typeVariable";
if (binding.getDeclaringMethod() != null) {
qualifiedName = resolveBinding(binding.getDeclaringMethod()).getPath() + "/" + qualifiedName;
} else if (binding.getDeclaringClass() != null) {
qualifiedName = resolveBinding(binding.getDeclaringClass()).getPath() + "/" + qualifiedName;
}
}
if (binding.isPrimitive()) {
scheme = "java+primitiveType";
}
if (binding.isWildcardType()) {
return makeBinding("unknown", null, null);
}
if (binding.isLocal()) {
qualifiedName = qualifiedName.concat("/").concat(binding.getName());
}
if (binding.isAnonymous()) {
String key = binding.getKey();
if (resolvedAnonymousClasses.containsKey(key)) {
qualifiedName = resolvedAnonymousClasses.get(key);
}
else {
int anonCounter = 1;
if (anonymousClassCounter.containsKey(qualifiedName)) {
anonCounter = anonymousClassCounter.get(qualifiedName) + 1;
}
else {
anonCounter = 1;
}
anonymousClassCounter.put(qualifiedName, anonCounter);
qualifiedName += "$anonymous" + anonCounter;
resolvedAnonymousClasses.put(key, qualifiedName);
}
scheme = "java+anonymousClass";
}
ISourceLocation result = makeBinding(scheme, null, qualifiedName.replaceAll("\\.", "/"));
locationCache.put(binding.getKey(), result);
return result;
}
catch (@SuppressWarnings("restriction") org.eclipse.jdt.internal.compiler.problem.AbortCompilation e) {
// work around internal error of JDT compiler which can throw this exception
// after calling getKey()
return makeBinding("unknown", null, null);
}
}
private ISourceLocation resolveBinding(IVariableBinding binding) {
if (binding == null) {
return makeBinding("unresolved", null, null);
}
String qualifiedName = "";
ITypeBinding declaringClass = binding.getDeclaringClass();
if (declaringClass != null) {
qualifiedName = getPath(resolveBinding(declaringClass));
} else {
IMethodBinding declaringMethod = binding.getDeclaringMethod();
if (declaringMethod != null) {
qualifiedName = getPath(resolveBinding(declaringMethod));
}
else {
//binding.getDeI
}
}
if (!qualifiedName.isEmpty()) {
qualifiedName = qualifiedName.concat("/");
} else {
return makeBinding("unresolved", null, null);
}
if (locationCache.containsKey(binding.getKey()))
return locationCache.get(binding.getKey());
String bindingKey = binding.getKey();
String[] bindingKeys = bindingKey.split("#");
if (bindingKeys.length > 2) {
// scoped variable that may have the same name as some other scoped variable
for (int i = 1; i < bindingKeys.length - 1; i++) {
if (!qualifiedName.endsWith("/"))
qualifiedName += "/";
qualifiedName += "scope("+bindingKeys[i]+")/";
}
}
String scheme = "java+variable";
if (binding.isEnumConstant()) {
scheme = "java+enumConstant";
} else if (binding.isParameter()) {
scheme = "java+parameter";
} else if (binding.isField()) {
scheme = "java+field";
}
ISourceLocation result = makeBinding(scheme, null, qualifiedName.concat(binding.getName()));
locationCache.put(binding.getKey(), result);
return result;
}
protected ISourceLocation makeBinding(String scheme, String authority, String path) {
try {
return values.sourceLocation(scheme, authority, path);
} catch (URISyntaxException | UnsupportedOperationException e) {
throw new RuntimeException("Should not happen", e);
}
}
private String getPath(ISourceLocation iSourceLocation) {
String path = iSourceLocation.getPath();
return path.substring(1, path.length());
}
}