| /******************************************************************************* |
| * Copyright (c) 2005 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.util; |
| |
| import java.util.ArrayList; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.Compiler; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Wildcard; |
| import org.eclipse.jdt.internal.compiler.lookup.BaseTypes; |
| import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; |
| |
| public class BindingKeyResolver extends BindingKeyParser { |
| Compiler compiler; |
| Binding compilerBinding; |
| |
| char[][] compoundName; |
| int dimension; |
| LookupEnvironment environment; |
| ReferenceBinding genericType; |
| MethodBinding methodBinding; |
| |
| CompilationUnitDeclaration parsedUnit; |
| BlockScope scope; |
| TypeBinding typeBinding; |
| TypeDeclaration typeDeclaration; |
| ArrayList types = new ArrayList(); |
| |
| boolean isCapture; |
| int wildcardKind = -1; |
| |
| private BindingKeyResolver(BindingKeyParser parser, Compiler compiler, LookupEnvironment environment) { |
| super(parser); |
| this.compiler = compiler; |
| this.environment = environment; |
| } |
| |
| public BindingKeyResolver(String key) { |
| this(key, null, null); |
| } |
| |
| public BindingKeyResolver(String key, Compiler compiler, LookupEnvironment environment) { |
| super(key); |
| this.compiler = compiler; |
| this.environment = environment; |
| } |
| |
| /* |
| * If not already cached, computes and cache the compound name (pkg name + top level name) of this key. |
| * Returns the package name if key is a pkg key. |
| * Returns an empty array if malformed. |
| * This key's scanner should be positioned on the package or type token. |
| */ |
| public char[][] compoundName() { |
| return this.compoundName; |
| } |
| |
| public void consumeArrayDimension(char[] brakets) { |
| this.dimension = brakets.length; |
| } |
| |
| public void consumeCapture() { |
| this.isCapture = true; |
| } |
| |
| public void consumeField(char[] fieldName) { |
| FieldBinding[] fields = ((ReferenceBinding) this.typeBinding).fields(); |
| for (int i = 0, length = fields.length; i < length; i++) { |
| FieldBinding field = fields[i]; |
| if (CharOperation.equals(fieldName, field.name)) { |
| this.compilerBinding = field; |
| return; |
| } |
| } |
| } |
| |
| public void consumeParameterizedMethod() { |
| TypeBinding[] arguments = getTypeBindingArguments(); |
| if (arguments.length != this.methodBinding.typeVariables().length) return; |
| this.methodBinding = new ParameterizedGenericMethodBinding(this.methodBinding, arguments, this.environment); |
| this.compilerBinding = this.methodBinding; |
| } |
| |
| public void consumeLocalType(char[] uniqueKey) { |
| LocalTypeBinding[] localTypeBindings = this.parsedUnit.localTypes; |
| for (int i = 0; i < this.parsedUnit.localTypeCount; i++) |
| if (CharOperation.equals(uniqueKey, localTypeBindings[i].computeUniqueKey(false/*without access flags*/))) { |
| this.typeBinding = localTypeBindings[i]; |
| this.compilerBinding = this.typeBinding; |
| return; |
| } |
| } |
| |
| public void consumeLocalVar(char[] varName) { |
| if (this.scope == null) { |
| this.scope = this.methodBinding.sourceMethod().scope; |
| } |
| for (int i = 0; i < this.scope.localIndex; i++) { |
| LocalVariableBinding local = this.scope.locals[i]; |
| if (CharOperation.equals(varName, local.name)) { |
| this.compilerBinding = local; |
| return; |
| } |
| } |
| } |
| |
| public void consumeMethod(char[] selector, char[] signature) { |
| MethodBinding[] methods = ((ReferenceBinding) this.typeBinding).methods(); |
| for (int i = 0, methodLength = methods.length; i < methodLength; i++) { |
| MethodBinding method = methods[i]; |
| if (CharOperation.equals(selector, method.selector) || (selector.length == 0 && method.isConstructor())) { |
| char[] methodSignature = method.original().genericSignature(); |
| if (methodSignature == null) |
| methodSignature = method.signature(); |
| if (CharOperation.equals(signature, methodSignature)) { |
| this.methodBinding = method; |
| this.compilerBinding = this.methodBinding; |
| return; |
| } |
| } |
| } |
| } |
| |
| public void consumeMemberType(char[] simpleTypeName) { |
| this.typeBinding = getTypeBinding(simpleTypeName); |
| this.compilerBinding = this.typeBinding; |
| } |
| |
| public void consumePackage(char[] pkgName) { |
| this.compoundName = CharOperation.splitOn('/', pkgName); |
| this.compilerBinding = new PackageBinding(this.compoundName, null, this.environment); |
| } |
| |
| public void consumeParameterizedType(char[] simpleTypeName, boolean isRaw) { |
| TypeBinding[] arguments = getTypeBindingArguments(); |
| if (simpleTypeName != null) { |
| // parameterized member type |
| this.genericType = this.genericType.getMemberType(simpleTypeName); |
| if (!isRaw) |
| this.typeBinding = this.environment.createParameterizedType(this.genericType, arguments, (ReferenceBinding) this.typeBinding); |
| else |
| // raw type |
| this.typeBinding = this.environment.createRawType(this.genericType, (ReferenceBinding) this.typeBinding); |
| } else { |
| // parameterized top level type |
| this.genericType = (ReferenceBinding) this.typeBinding; |
| this.typeBinding = this.environment.createParameterizedType(this.genericType, arguments, null); |
| } |
| this.compilerBinding = this.typeBinding; |
| } |
| |
| |
| public void consumeParser(BindingKeyParser parser) { |
| this.types.add(parser); |
| } |
| |
| public void consumeScope(int scopeNumber) { |
| if (this.scope == null) { |
| this.scope = this.methodBinding.sourceMethod().scope; |
| } |
| if (scopeNumber >= this.scope.subscopeCount) |
| return; // malformed key |
| this.scope = (BlockScope) this.scope.subscopes[scopeNumber]; |
| } |
| |
| public void consumeRawType() { |
| if (this.typeBinding == null) return; |
| this.typeBinding = this.environment.createRawType((ReferenceBinding) this.typeBinding, null/*no enclosing type*/); |
| } |
| public void consumeSecondaryType(char[] simpleTypeName) { |
| if (this.parsedUnit == null) return; |
| this.typeDeclaration = null; // start from the parsed unit |
| this.typeBinding = getTypeBinding(simpleTypeName); |
| this.compilerBinding = this.typeBinding; |
| } |
| |
| public void consumeFullyQualifiedName(char[] fullyQualifiedName) { |
| this.compoundName = CharOperation.splitOn('/', fullyQualifiedName); |
| } |
| |
| public void consumeTopLevelType() { |
| if (this.compoundName.length == 1 && this.compoundName[0].length == 1) { |
| // case of base type |
| TypeBinding baseTypeBinding = getBaseTypeBinding(this.compoundName[0]); |
| if (baseTypeBinding != null) { |
| this.typeBinding = baseTypeBinding; |
| this.compilerBinding = this.typeBinding; |
| return; |
| } |
| } |
| this.parsedUnit = getCompilationUnitDeclaration(); |
| if (this.parsedUnit != null && this.compiler != null) { |
| this.compiler.process(this.parsedUnit, this.compiler.totalUnits+1); // noop if unit has already been resolved |
| } |
| if (this.parsedUnit == null) { |
| this.typeBinding = getBinaryBinding(); |
| this.compilerBinding = this.typeBinding; |
| } else { |
| char[] typeName = this.compoundName[this.compoundName.length-1]; |
| this.typeBinding = getTypeBinding(typeName); |
| this.compilerBinding = this.typeBinding; |
| } |
| } |
| |
| public void consumeType() { |
| this.typeBinding = getArrayBinding(this.dimension, this.typeBinding); |
| this.compilerBinding = this.typeBinding; |
| } |
| |
| public void consumeTypeVariable(char[] typeVariableName) { |
| TypeVariableBinding[] typeVariableBindings = this.methodBinding != null ? this.methodBinding.typeVariables() : this.typeBinding.typeVariables(); |
| for (int i = 0, length = typeVariableBindings.length; i < length; i++) { |
| TypeVariableBinding typeVariableBinding = typeVariableBindings[i]; |
| if (CharOperation.equals(typeVariableName, typeVariableBinding.sourceName())) { |
| this.compilerBinding = typeVariableBinding; |
| return; |
| } |
| } |
| } |
| |
| public void consumeWildCard(int kind) { |
| this.wildcardKind = kind; |
| } |
| |
| /* |
| * If the given dimension is greater than 0 returns an array binding for the given type binding. |
| * Otherwise return the given type binding. |
| * Returns null if the given type binding is null. |
| */ |
| private TypeBinding getArrayBinding(int dim, TypeBinding binding) { |
| if (binding == null) return null; |
| if (dim == 0) return binding; |
| return this.environment.createArrayType(binding, dim); |
| } |
| |
| private TypeBinding getBaseTypeBinding(char[] signature) { |
| switch (signature[0]) { |
| case 'I' : |
| return BaseTypes.IntBinding; |
| case 'Z' : |
| return BaseTypes.BooleanBinding; |
| case 'V' : |
| return BaseTypes.VoidBinding; |
| case 'C' : |
| return BaseTypes.CharBinding; |
| case 'D' : |
| return BaseTypes.DoubleBinding; |
| case 'B' : |
| return BaseTypes.ByteBinding; |
| case 'F' : |
| return BaseTypes.FloatBinding; |
| case 'J' : |
| return BaseTypes.LongBinding; |
| case 'S' : |
| return BaseTypes.ShortBinding; |
| case 'N': |
| return BaseTypes.NullBinding; |
| default : |
| return null; |
| } |
| } |
| |
| /* |
| * Returns a binary binding corresonding to this key's compound name. |
| * Returns null if not found. |
| */ |
| private TypeBinding getBinaryBinding() { |
| return this.environment.getType(this.compoundName); |
| } |
| |
| /* |
| * Finds the compilation unit declaration corresponding to the key in the given lookup environment. |
| * Returns null if no compilation unit declaration could be found. |
| * This key's scanner should be positioned on the package token. |
| */ |
| public CompilationUnitDeclaration getCompilationUnitDeclaration() { |
| char[][] name = compoundName(); |
| if (name.length == 0) return null; |
| if (this.environment == null) return null; |
| ReferenceBinding binding = this.environment.getType(name); |
| if (!(binding instanceof SourceTypeBinding)) return null; |
| SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) binding; |
| if (sourceTypeBinding.scope == null) |
| return null; |
| return sourceTypeBinding.scope.compilationUnitScope().referenceContext; |
| } |
| |
| /* |
| * Returns the compiler binding corresponding to this key. |
| * Returns null is malformed. |
| * This key's scanner should be positioned on the package token. |
| */ |
| public Binding getCompilerBinding() { |
| parse(); |
| return this.compilerBinding; |
| } |
| |
| private TypeBinding getTypeBinding(char[] simpleTypeName) { |
| if (this.typeBinding instanceof BinaryTypeBinding) { |
| return ((BinaryTypeBinding) this.typeBinding).getMemberType(simpleTypeName); |
| } else { |
| TypeDeclaration[] typeDeclarations = |
| this.typeDeclaration == null ? |
| (this.parsedUnit == null ? null : this.parsedUnit.types) : |
| this.typeDeclaration.memberTypes; |
| if (typeDeclarations == null) return null; |
| for (int i = 0, length = typeDeclarations.length; i < length; i++) { |
| TypeDeclaration declaration = typeDeclarations[i]; |
| if (CharOperation.equals(simpleTypeName, declaration.name)) { |
| this.typeDeclaration = declaration; |
| return declaration.binding; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private TypeBinding[] getTypeBindingArguments() { |
| int size = this.types.size(); |
| TypeBinding[] arguments = new TypeBinding[size]; |
| int rank = 0; |
| for (int i = 0; i < size; i++) { |
| BindingKeyResolver resolver = (BindingKeyResolver) this.types.get(i); |
| TypeBinding binding; |
| int kind = resolver.wildcardKind; |
| switch (kind) { |
| case Wildcard.EXTENDS: |
| case Wildcard.SUPER: |
| binding = this.environment.createWildcard((ReferenceBinding) this.typeBinding, rank++, (TypeBinding) resolver.compilerBinding, null /*no extra bound*/, kind); |
| break; |
| case Wildcard.UNBOUND: |
| binding = this.environment.createWildcard((ReferenceBinding) this.typeBinding, rank++, null/*no bound*/, null /*no extra bound*/, kind); |
| break; |
| default: |
| binding = (TypeBinding) resolver.compilerBinding; |
| } |
| if (resolver.isCapture) |
| binding = new CaptureBinding((WildcardBinding) binding); |
| arguments[i] = binding; |
| } |
| this.types = new ArrayList(); |
| return arguments; |
| } |
| |
| public void malformedKey() { |
| this.compoundName = CharOperation.NO_CHAR_CHAR; |
| } |
| |
| public BindingKeyParser newParser() { |
| return new BindingKeyResolver(this, this.compiler, this.environment); |
| } |
| |
| public String toString() { |
| return getKey(); |
| } |
| |
| } |