/******************************************************************************* * Copyright (c) 2000, 2011 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 * Erling Ellingsen - patch for bug 125570 *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup; import org.eclipse.che.ide.ext.java.jdt.core.compiler.CharOperation; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ClassFileConstants; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ASTNode; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ImportReference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.CompoundNameVector; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.HashtableOfObject; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.HashtableOfType; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.ObjectVector; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.SimpleNameVector; public class CompilationUnitScope extends Scope { public LookupEnvironment environment; public CompilationUnitDeclaration referenceContext; public char[][] currentPackageName; public PackageBinding fPackage; public ImportBinding[] imports; public HashtableOfObject typeOrPackageCache; // used in Scope.getTypeOrPackage() public SourceTypeBinding[] topLevelTypes; private CompoundNameVector qualifiedReferences; private SimpleNameVector simpleNameReferences; private SimpleNameVector rootReferences; private ObjectVector referencedTypes; private ObjectVector referencedSuperTypes; HashtableOfType constantPoolNameUsage; private int captureID = 1; public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) { super(COMPILATION_UNIT_SCOPE, null); this.environment = environment; this.referenceContext = unit; unit.scope = this; this.currentPackageName = unit.currentPackage == null ? CharOperation.NO_CHAR_CHAR : unit.currentPackage.tokens; if (compilerOptions().produceReferenceInfo) { this.qualifiedReferences = new CompoundNameVector(); this.simpleNameReferences = new SimpleNameVector(); this.rootReferences = new SimpleNameVector(); this.referencedTypes = new ObjectVector(); this.referencedSuperTypes = new ObjectVector(); } else { this.qualifiedReferences = null; // used to test if dependencies should be recorded this.simpleNameReferences = null; this.rootReferences = null; this.referencedTypes = null; this.referencedSuperTypes = null; } } void buildFieldsAndMethods() { for (int i = 0, length = this.topLevelTypes.length; i < length; i++) this.topLevelTypes[i].scope.buildFieldsAndMethods(); } void buildTypeBindings(AccessRestriction accessRestriction) { this.topLevelTypes = new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved boolean firstIsSynthetic = false; if (this.referenceContext.compilationResult.compilationUnit != null) { char[][] expectedPackageName = this.referenceContext.compilationResult.compilationUnit.getPackageName(); if (expectedPackageName != null && !CharOperation.equals(this.currentPackageName, expectedPackageName)) { // only report if the unit isn't structurally empty if (this.referenceContext.currentPackage != null || this.referenceContext.types != null || this.referenceContext.imports != null) { problemReporter().packageIsNotExpectedPackage(this.referenceContext); } this.currentPackageName = expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName; } } if (this.currentPackageName == CharOperation.NO_CHAR_CHAR) { // environment default package is never null this.fPackage = this.environment.defaultPackage; } else { if ((this.fPackage = this.environment.createPackage(this.currentPackageName)) == null) { if (this.referenceContext.currentPackage != null) { problemReporter().packageCollidesWithType(this.referenceContext); // only report when the unit has a package // statement } // ensure fPackage is not null this.fPackage = this.environment.defaultPackage; return; } else if (this.referenceContext.isPackageInfo()) { // resolve package annotations now if this is "package-info.java". if (this.referenceContext.types == null || this.referenceContext.types.length == 0) { this.referenceContext.types = new TypeDeclaration[1]; this.referenceContext.createPackageInfoType(); firstIsSynthetic = true; } // ensure the package annotations are copied over before resolution if (this.referenceContext.currentPackage != null) this.referenceContext.types[0].annotations = this.referenceContext.currentPackage.annotations; } recordQualifiedReference(this.currentPackageName); // always dependent on your own package } // Skip typeDeclarations which know of previously reported errors TypeDeclaration[] types = this.referenceContext.types; int typeLength = (types == null) ? 0 : types.length; this.topLevelTypes = new SourceTypeBinding[typeLength]; int count = 0; nextType: for (int i = 0; i < typeLength; i++) { TypeDeclaration typeDecl = types[i]; if (this.environment.isProcessingAnnotations && this.environment.isMissingType(typeDecl.name)) throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the type ReferenceBinding typeBinding = this.fPackage.getType0(typeDecl.name); recordSimpleReference(typeDecl.name); // needed to detect collision cases if (typeBinding != null && typeBinding.isValidBinding() && !(typeBinding instanceof UnresolvedReferenceBinding)) { // if its an unresolved binding - its fixed up whenever its needed, see UnresolvedReferenceBinding.resolve() if (this.environment.isProcessingAnnotations) throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the type // if a type exists, check that its a valid type // it can be a NotFound problem type if its a secondary type referenced before its primary type found in additional // units // and it can be an unresolved type which is now being defined problemReporter().duplicateTypes(this.referenceContext, typeDecl); continue nextType; } if (this.fPackage != this.environment.defaultPackage && this.fPackage.getPackage(typeDecl.name) != null) { // if a package exists, it must be a valid package - cannot be a NotFound problem package // this is now a warning since a package does not really 'exist' until it contains a type, see JLS v2, 7.4.3 problemReporter().typeCollidesWithPackage(this.referenceContext, typeDecl); } if ((typeDecl.modifiers & ClassFileConstants.AccPublic) != 0) { char[] mainTypeName; if ((mainTypeName = this.referenceContext.getMainTypeName()) != null // mainTypeName == null means that implementor of // ICompilationUnit decided to return null && !CharOperation.equals(mainTypeName, typeDecl.name)) { problemReporter().publicClassMustMatchFileName(this.referenceContext, typeDecl); // tolerate faulty main type name (91091), allow to proceed into type construction } } ClassScope child = new ClassScope(this, typeDecl); SourceTypeBinding type = child.buildType(null, this.fPackage, accessRestriction); if (firstIsSynthetic && i == 0) type.modifiers |= ClassFileConstants.AccSynthetic; if (type != null) this.topLevelTypes[count++] = type; } // shrink topLevelTypes... only happens if an error was reported if (count != this.topLevelTypes.length) System.arraycopy(this.topLevelTypes, 0, this.topLevelTypes = new SourceTypeBinding[count], 0, count); } void checkAndSetImports() { if (this.referenceContext.imports == null) { this.imports = getDefaultImports(); return; } // allocate the import array, add java.lang.* by default int numberOfStatements = this.referenceContext.imports.length; int numberOfImports = numberOfStatements + 1; for (int i = 0; i < numberOfStatements; i++) { ImportReference importReference = this.referenceContext.imports[i]; if (((importReference.bits & ASTNode.OnDemand) != 0) && CharOperation.equals(TypeConstants.JAVA_LANG, importReference.tokens) && !importReference.isStatic()) { numberOfImports--; break; } } ImportBinding[] resolvedImports = new ImportBinding[numberOfImports]; resolvedImports[0] = getDefaultImports()[0]; int index = 1; nextImport: for (int i = 0; i < numberOfStatements; i++) { ImportReference importReference = this.referenceContext.imports[i]; char[][] compoundName = importReference.tokens; // skip duplicates or imports of the current package for (int j = 0; j < index; j++) { ImportBinding resolved = resolvedImports[j]; if (resolved.onDemand == ((importReference.bits & ASTNode.OnDemand) != 0) && resolved.isStatic() == importReference.isStatic()) if (CharOperation.equals(compoundName, resolvedImports[j].compoundName)) continue nextImport; } if ((importReference.bits & ASTNode.OnDemand) != 0) { if (CharOperation.equals(compoundName, this.currentPackageName)) continue nextImport; Binding importBinding = findImport(compoundName, compoundName.length); if (!importBinding.isValidBinding() || (importReference.isStatic() && importBinding instanceof PackageBinding)) continue nextImport; // we report all problems in faultInImports() resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference); } else { // resolve single imports only when the last name matches resolvedImports[index++] = new ImportBinding(compoundName, false, null, importReference); } } // shrink resolvedImports... only happens if an error was reported if (resolvedImports.length > index) System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index); this.imports = resolvedImports; } /** Perform deferred check specific to parameterized types: bound checks, supertype collisions */ void checkParameterizedTypes() { if (compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) return; for (int i = 0, length = this.topLevelTypes.length; i < length; i++) { ClassScope scope = this.topLevelTypes[i].scope; scope.checkParameterizedTypeBounds(); scope.checkParameterizedSuperTypeCollisions(); } } /* * INTERNAL USE-ONLY Innerclasses get their name computed as they are generated, since some may not be actually outputed if * sitting inside unreachable code. */ public char[] computeConstantPoolName(LocalTypeBinding localType) { if (localType.constantPoolName != null) { return localType.constantPoolName; } // delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes. if (this.constantPoolNameUsage == null) this.constantPoolNameUsage = new HashtableOfType(); ReferenceBinding outerMostEnclosingType = localType.scope.outerMostClassScope().enclosingSourceType(); // ensure there is not already such a local type name defined by the user int index = 0; char[] candidateName; boolean isCompliant15 = compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5; while (true) { if (localType.isMemberType()) { if (index == 0) { candidateName = CharOperation.concat(localType.enclosingType().constantPoolName(), localType.sourceName, '$'); } else { // in case of collision, then member name gets extra $1 inserted // e.g. class X { { class L{} new X(){ class L{} } } } candidateName = CharOperation.concat(localType.enclosingType().constantPoolName(), '$', String.valueOf(index) .toCharArray(), '$', localType.sourceName); } } else if (localType.isAnonymousType()) { if (isCompliant15) { // from 1.5 on, use immediately enclosing type name candidateName = CharOperation.concat(localType.enclosingType.constantPoolName(), String.valueOf(index + 1) .toCharArray(), '$'); } else { candidateName = CharOperation.concat(outerMostEnclosingType.constantPoolName(), String.valueOf(index + 1) .toCharArray(), '$'); } } else { // local type if (isCompliant15) { candidateName = CharOperation.concat(CharOperation.concat(localType.enclosingType().constantPoolName(), String .valueOf(index + 1).toCharArray(), '$'), localType.sourceName); } else { candidateName = CharOperation.concat(outerMostEnclosingType.constantPoolName(), '$', String.valueOf(index + 1) .toCharArray(), '$', localType.sourceName); } } if (this.constantPoolNameUsage.get(candidateName) != null) { index++; } else { this.constantPoolNameUsage.put(candidateName, localType); break; } } return candidateName; } void connectTypeHierarchy() { for (int i = 0, length = this.topLevelTypes.length; i < length; i++) this.topLevelTypes[i].scope.connectTypeHierarchy(); } void faultInImports() { if (this.typeOrPackageCache != null) return; // can be called when a field constant is resolved before static imports if (this.referenceContext.imports == null) { this.typeOrPackageCache = new HashtableOfObject(1); return; } // collect the top level type names if a single type import exists int numberOfStatements = this.referenceContext.imports.length; HashtableOfType typesBySimpleNames = null; for (int i = 0; i < numberOfStatements; i++) { if ((this.referenceContext.imports[i].bits & ASTNode.OnDemand) == 0) { typesBySimpleNames = new HashtableOfType(this.topLevelTypes.length + numberOfStatements); for (int j = 0, length = this.topLevelTypes.length; j < length; j++) typesBySimpleNames.put(this.topLevelTypes[j].sourceName, this.topLevelTypes[j]); break; } } // allocate the import array, add java.lang.* by default int numberOfImports = numberOfStatements + 1; for (int i = 0; i < numberOfStatements; i++) { ImportReference importReference = this.referenceContext.imports[i]; if (((importReference.bits & ASTNode.OnDemand) != 0) && CharOperation.equals(TypeConstants.JAVA_LANG, importReference.tokens) && !importReference.isStatic()) { numberOfImports--; break; } } ImportBinding[] resolvedImports = new ImportBinding[numberOfImports]; resolvedImports[0] = getDefaultImports()[0]; int index = 1; // keep static imports with normal imports until there is a reason to split them up // on demand imports continue to be packages & types. need to check on demand type imports for fields/methods // single imports change from being just types to types or fields nextImport: for (int i = 0; i < numberOfStatements; i++) { ImportReference importReference = this.referenceContext.imports[i]; char[][] compoundName = importReference.tokens; // skip duplicates or imports of the current package for (int j = 0; j < index; j++) { ImportBinding resolved = resolvedImports[j]; if (resolved.onDemand == ((importReference.bits & ASTNode.OnDemand) != 0) && resolved.isStatic() == importReference.isStatic()) { if (CharOperation.equals(compoundName, resolved.compoundName)) { problemReporter().unusedImport(importReference); // since skipped, must be reported now continue nextImport; } } } if ((importReference.bits & ASTNode.OnDemand) != 0) { if (CharOperation.equals(compoundName, this.currentPackageName)) { problemReporter().unusedImport(importReference); // since skipped, must be reported now continue nextImport; } Binding importBinding = findImport(compoundName, compoundName.length); if (!importBinding.isValidBinding()) { problemReporter().importProblem(importReference, importBinding); continue nextImport; } if (importReference.isStatic() && importBinding instanceof PackageBinding) { problemReporter().cannotImportPackage(importReference); continue nextImport; } resolvedImports[index++] = new ImportBinding(compoundName, true, importBinding, importReference); } else { Binding importBinding = findSingleImport(compoundName, Binding.TYPE | Binding.FIELD | Binding.METHOD, importReference.isStatic()); if (!importBinding.isValidBinding()) { if (importBinding.problemId() == ProblemReasons.Ambiguous) { // keep it unless a duplicate can be found below } else { problemReporter().importProblem(importReference, importBinding); continue nextImport; } } if (importBinding instanceof PackageBinding) { problemReporter().cannotImportPackage(importReference); continue nextImport; } ReferenceBinding conflictingType = null; if (importBinding instanceof MethodBinding) { conflictingType = (ReferenceBinding)getType(compoundName, compoundName.length); if (!conflictingType.isValidBinding() || (importReference.isStatic() && !conflictingType.isStatic())) conflictingType = null; } // collisions between an imported static field & a type should be checked according to spec... but currently not by // javac if (importBinding instanceof ReferenceBinding || conflictingType != null) { ReferenceBinding referenceBinding = conflictingType == null ? (ReferenceBinding)importBinding : conflictingType; ReferenceBinding typeToCheck = referenceBinding.problemId() == ProblemReasons.Ambiguous ? ((ProblemReferenceBinding)referenceBinding).closestMatch : referenceBinding; if (importReference.isTypeUseDeprecated(typeToCheck, this)) problemReporter().deprecatedType(typeToCheck, importReference); ReferenceBinding existingType = typesBySimpleNames.get(compoundName[compoundName.length - 1]); if (existingType != null) { // duplicate test above should have caught this case, but make sure if (existingType == referenceBinding) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=302865 // Check all resolved imports to see if this import qualifies as a duplicate for (int j = 0; j < index; j++) { ImportBinding resolved = resolvedImports[j]; if (resolved instanceof ImportConflictBinding) { ImportConflictBinding importConflictBinding = (ImportConflictBinding)resolved; if (importConflictBinding.conflictingTypeBinding == referenceBinding) { if (!importReference.isStatic()) { // resolved is implicitly static problemReporter().duplicateImport(importReference); resolvedImports[index++] = new ImportBinding(compoundName, false, importBinding, importReference); } } } else if (resolved.resolvedImport == referenceBinding) { if (importReference.isStatic() != resolved.isStatic()) { problemReporter().duplicateImport(importReference); resolvedImports[index++] = new ImportBinding(compoundName, false, importBinding, importReference); } } } continue nextImport; } // either the type collides with a top level type or another imported type for (int j = 0, length = this.topLevelTypes.length; j < length; j++) { if (CharOperation.equals(this.topLevelTypes[j].sourceName, existingType.sourceName)) { problemReporter().conflictingImport(importReference); continue nextImport; } } problemReporter().duplicateImport(importReference); continue nextImport; } typesBySimpleNames.put(compoundName[compoundName.length - 1], referenceBinding); } else if (importBinding instanceof FieldBinding) { for (int j = 0; j < index; j++) { ImportBinding resolved = resolvedImports[j]; // find other static fields with the same name if (resolved.isStatic() && resolved.resolvedImport instanceof FieldBinding && importBinding != resolved.resolvedImport) { if (CharOperation.equals(compoundName[compoundName.length - 1], resolved.compoundName[resolved.compoundName.length - 1])) { problemReporter().duplicateImport(importReference); continue nextImport; } } } } resolvedImports[index++] = conflictingType == null ? new ImportBinding(compoundName, false, importBinding, importReference) : new ImportConflictBinding(compoundName, importBinding, conflictingType, importReference); } } // shrink resolvedImports... only happens if an error was reported if (resolvedImports.length > index) System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[index], 0, index); this.imports = resolvedImports; int length = this.imports.length; this.typeOrPackageCache = new HashtableOfObject(length); for (int i = 0; i < length; i++) { ImportBinding binding = this.imports[i]; if (!binding.onDemand && binding.resolvedImport instanceof ReferenceBinding || binding instanceof ImportConflictBinding) this.typeOrPackageCache.put(binding.compoundName[binding.compoundName.length - 1], binding); } } public void faultInTypes() { faultInImports(); for (int i = 0, length = this.topLevelTypes.length; i < length; i++) this.topLevelTypes[i].faultInTypesForFieldsAndMethods(); } // this API is for code assist purpose public Binding findImport(char[][] compoundName, boolean findStaticImports, boolean onDemand) { if (onDemand) { return findImport(compoundName, compoundName.length); } else { return findSingleImport(compoundName, Binding.TYPE | Binding.FIELD | Binding.METHOD, findStaticImports); } } private Binding findImport(char[][] compoundName, int length) { recordQualifiedReference(compoundName); Binding binding = this.environment.getTopLevelPackage(compoundName[0]); int i = 1; foundNothingOrType: if (binding != null) { PackageBinding packageBinding = (PackageBinding)binding; while (i < length) { binding = packageBinding.getTypeOrPackage(compoundName[i++]); if (binding == null || !binding.isValidBinding()) { binding = null; break foundNothingOrType; } if (!(binding instanceof PackageBinding)) break foundNothingOrType; packageBinding = (PackageBinding)binding; } return packageBinding; } ReferenceBinding type; if (binding == null) { if (compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound); type = findType(compoundName[0], this.environment.defaultPackage, this.environment.defaultPackage); if (type == null || !type.isValidBinding()) return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound); i = 1; // reset to look for member types inside the default package type } else { type = (ReferenceBinding)binding; } while (i < length) { type = (ReferenceBinding)this.environment.convertToRawType(type, false /* * do not force conversion of enclosing types */); // type imports are necessarily // raw for all except last if (!type.canBeSeenBy(this.fPackage)) return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), type, ProblemReasons.NotVisible); char[] name = compoundName[i++]; // does not look for inherited member types on purpose, only immediate members type = type.getMemberType(name); if (type == null) return new ProblemReferenceBinding(CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound); } if (!type.canBeSeenBy(this.fPackage)) return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible); return type; } private Binding findSingleImport(char[][] compoundName, int mask, boolean findStaticImports) { if (compoundName.length == 1) { // findType records the reference // the name cannot be a package if (compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound); ReferenceBinding typeBinding = findType(compoundName[0], this.environment.defaultPackage, this.fPackage); if (typeBinding == null) return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound); return typeBinding; } if (findStaticImports) return findSingleStaticImport(compoundName, mask); return findImport(compoundName, compoundName.length); } private Binding findSingleStaticImport(char[][] compoundName, int mask) { Binding binding = findImport(compoundName, compoundName.length - 1); if (!binding.isValidBinding()) return binding; char[] name = compoundName[compoundName.length - 1]; if (binding instanceof PackageBinding) { Binding temp = ((PackageBinding)binding).getTypeOrPackage(name); if (temp != null && temp instanceof ReferenceBinding) // must resolve to a member type or field, not a top level type return new ProblemReferenceBinding(compoundName, (ReferenceBinding)temp, ProblemReasons.InvalidTypeForStaticImport); return binding; // cannot be a package, error is caught in sender } // look to see if its a static field first ReferenceBinding type = (ReferenceBinding)binding; FieldBinding field = (mask & Binding.FIELD) != 0 ? findField(type, name, null, true) : null; if (field != null) { if (field.problemId() == ProblemReasons.Ambiguous && ((ProblemFieldBinding)field).closestMatch.isStatic()) return field; // keep the ambiguous field instead of a possible method match if (field.isValidBinding() && field.isStatic() && field.canBeSeenBy(type, null, this)) return field; } // look to see if there is a static method with the same selector MethodBinding method = (mask & Binding.METHOD) != 0 ? findStaticMethod(type, name) : null; if (method != null) return method; type = findMemberType(name, type); if (type == null || !type.isStatic()) { if (field != null && !field.isValidBinding() && field.problemId() != ProblemReasons.NotFound) return field; return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotFound); } if (type.isValidBinding() && !type.canBeSeenBy(this.fPackage)) return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible); if (type.problemId() == ProblemReasons.NotVisible) // ensure compoundName is correct return new ProblemReferenceBinding(compoundName, ((ProblemReferenceBinding)type).closestMatch, ProblemReasons.NotVisible); return type; } // helper method for findSingleStaticImport() private MethodBinding findStaticMethod(ReferenceBinding currentType, char[] selector) { if (!currentType.canBeSeenBy(this)) return null; do { currentType.initializeForStaticImports(); MethodBinding[] methods = currentType.getMethods(selector); if (methods != Binding.NO_METHODS) { for (int i = methods.length; --i >= 0; ) { MethodBinding method = methods[i]; if (method.isStatic() && method.canBeSeenBy(this.fPackage)) return method; } } } while ((currentType = currentType.superclass()) != null); return null; } ImportBinding[] getDefaultImports() { // initialize the default imports if necessary... share the default java.lang.* import if (this.environment.defaultImports != null) return this.environment.defaultImports; Binding importBinding = this.environment.getTopLevelPackage(TypeConstants.JAVA); if (importBinding != null) importBinding = ((PackageBinding)importBinding).getTypeOrPackage(TypeConstants.JAVA_LANG[1]); if (importBinding == null || !importBinding.isValidBinding()) { // create a proxy for the missing BinaryType problemReporter().isClassPathCorrect(TypeConstants.JAVA_LANG_OBJECT, this.referenceContext, this.environment.missingClassFileLocation); BinaryTypeBinding missingObject = this.environment.createMissingType(null, TypeConstants.JAVA_LANG_OBJECT); importBinding = missingObject.fPackage; } return this.environment.defaultImports = new ImportBinding[]{new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null)}; } // NOT Public API public final Binding getImport(char[][] compoundName, boolean onDemand, boolean isStaticImport) { if (onDemand) return findImport(compoundName, compoundName.length); return findSingleImport(compoundName, Binding.TYPE | Binding.FIELD | Binding.METHOD, isStaticImport); } public int nextCaptureID() { return this.captureID++; } /* * Answer the problem reporter to use for raising new problems. Note that as a side-effect, this updates the current reference * context (unit, type or method) in case the problem handler decides it is necessary to abort. */ public ProblemReporter problemReporter() { ProblemReporter problemReporter = this.referenceContext.problemReporter; problemReporter.referenceContext = this.referenceContext; return problemReporter; } /* * What do we hold onto: 1. when we resolve 'a.b.c', say we keep only 'a.b.c' & when we fail to resolve 'c' in 'a.b', lets keep * 'a.b.c' THEN when we come across a new/changed/removed item named 'a.b.c', we would find all references to 'a.b.c' -> This * approach fails because every type is resolved in every onDemand import to detect collision cases... so the references could * be 10 times bigger than necessary. 2. when we resolve 'a.b.c', lets keep 'a.b' & 'c' & when we fail to resolve 'c' in 'a.b', * lets keep 'a.b' & 'c' THEN when we come across a new/changed/removed item named 'a.b.c', we would find all references to * 'a.b' & 'c' -> This approach does not have a space problem but fails to handle collision cases. What happens if a type is * added named 'a.b'? We would search for 'a' & 'b' but would not find a match. 3. when we resolve 'a.b.c', lets keep 'a', * 'a.b' & 'a', 'b', 'c' & when we fail to resolve 'c' in 'a.b', lets keep 'a', 'a.b' & 'a', 'b', 'c' THEN when we come across * a new/changed/removed item named 'a.b.c', we would find all references to 'a.b' & 'c' OR 'a.b' -> 'a' & 'b' OR 'a' -> '' & * 'a' -> As long as each single char[] is interned, we should not have a space problem and can handle collision cases. 4. when * we resolve 'a.b.c', lets keep 'a.b' & 'a', 'b', 'c' & when we fail to resolve 'c' in 'a.b', lets keep 'a.b' & 'a', 'b', 'c' * THEN when we come across a new/changed/removed item named 'a.b.c', we would find all references to 'a.b' & 'c' OR 'a.b' -> * 'a' & 'b' in the simple name collection OR 'a' -> 'a' in the simple name collection -> As long as each single char[] is * interned, we should not have a space problem and can handle collision cases. */ void recordQualifiedReference(char[][] qualifiedName) { if (this.qualifiedReferences == null) return; // not recording dependencies int length = qualifiedName.length; if (length > 1) { recordRootReference(qualifiedName[0]); while (!this.qualifiedReferences.contains(qualifiedName)) { this.qualifiedReferences.add(qualifiedName); if (length == 2) { recordSimpleReference(qualifiedName[0]); recordSimpleReference(qualifiedName[1]); return; } length--; recordSimpleReference(qualifiedName[length]); System.arraycopy(qualifiedName, 0, qualifiedName = new char[length][], 0, length); } } else if (length == 1) { recordRootReference(qualifiedName[0]); recordSimpleReference(qualifiedName[0]); } } void recordReference(char[][] qualifiedEnclosingName, char[] simpleName) { recordQualifiedReference(qualifiedEnclosingName); if (qualifiedEnclosingName.length == 0) recordRootReference(simpleName); recordSimpleReference(simpleName); } void recordReference(ReferenceBinding type, char[] simpleName) { ReferenceBinding actualType = typeToRecord(type); if (actualType != null) recordReference(actualType.compoundName, simpleName); } void recordRootReference(char[] simpleName) { if (this.rootReferences == null) return; // not recording dependencies if (!this.rootReferences.contains(simpleName)) this.rootReferences.add(simpleName); } void recordSimpleReference(char[] simpleName) { if (this.simpleNameReferences == null) return; // not recording dependencies if (!this.simpleNameReferences.contains(simpleName)) this.simpleNameReferences.add(simpleName); } void recordSuperTypeReference(TypeBinding type) { if (this.referencedSuperTypes == null) return; // not recording dependencies ReferenceBinding actualType = typeToRecord(type); if (actualType != null && !this.referencedSuperTypes.containsIdentical(actualType)) this.referencedSuperTypes.add(actualType); } public void recordTypeConversion(TypeBinding superType, TypeBinding subType) { recordSuperTypeReference(subType); // must record the hierarchy of the subType that is converted to the superType } void recordTypeReference(TypeBinding type) { if (this.referencedTypes == null) return; // not recording dependencies ReferenceBinding actualType = typeToRecord(type); if (actualType != null && !this.referencedTypes.containsIdentical(actualType)) this.referencedTypes.add(actualType); } void recordTypeReferences(TypeBinding[] types) { if (this.referencedTypes == null) return; // not recording dependencies if (types == null || types.length == 0) return; for (int i = 0, max = types.length; i < max; i++) { // No need to record supertypes of method arguments & thrown exceptions, just the compoundName // If a field/method is retrieved from such a type then a separate call does the job ReferenceBinding actualType = typeToRecord(types[i]); if (actualType != null && !this.referencedTypes.containsIdentical(actualType)) this.referencedTypes.add(actualType); } } Binding resolveSingleImport(ImportBinding importBinding, int mask) { if (importBinding.resolvedImport == null) { importBinding.resolvedImport = findSingleImport(importBinding.compoundName, mask, importBinding.isStatic()); if (!importBinding.resolvedImport.isValidBinding() || importBinding.resolvedImport instanceof PackageBinding) { if (importBinding.resolvedImport.problemId() == ProblemReasons.Ambiguous) return importBinding.resolvedImport; if (this.imports != null) { ImportBinding[] newImports = new ImportBinding[this.imports.length - 1]; for (int i = 0, n = 0, max = this.imports.length; i < max; i++) if (this.imports[i] != importBinding) newImports[n++] = this.imports[i]; this.imports = newImports; } return null; } } return importBinding.resolvedImport; } public void storeDependencyInfo() { // add the type hierarchy of each referenced supertype // cannot do early since the hierarchy may not be fully resolved for (int i = 0; i < this.referencedSuperTypes.size(); i++) { // grows as more types are added ReferenceBinding type = (ReferenceBinding)this.referencedSuperTypes.elementAt(i); if (!this.referencedTypes.containsIdentical(type)) this.referencedTypes.add(type); if (!type.isLocalType()) { ReferenceBinding enclosing = type.enclosingType(); if (enclosing != null) recordSuperTypeReference(enclosing); } ReferenceBinding superclass = type.superclass(); if (superclass != null) recordSuperTypeReference(superclass); ReferenceBinding[] interfaces = type.superInterfaces(); if (interfaces != null) for (int j = 0, length = interfaces.length; j < length; j++) recordSuperTypeReference(interfaces[j]); } for (int i = 0, l = this.referencedTypes.size(); i < l; i++) { ReferenceBinding type = (ReferenceBinding)this.referencedTypes.elementAt(i); if (!type.isLocalType()) recordQualifiedReference(type.isMemberType() ? CharOperation.splitOn('.', type.readableName()) : type.compoundName); } int size = this.qualifiedReferences.size; char[][][] qualifiedRefs = new char[size][][]; for (int i = 0; i < size; i++) qualifiedRefs[i] = this.qualifiedReferences.elementAt(i); this.referenceContext.compilationResult.qualifiedReferences = qualifiedRefs; size = this.simpleNameReferences.size; char[][] simpleRefs = new char[size][]; for (int i = 0; i < size; i++) simpleRefs[i] = this.simpleNameReferences.elementAt(i); this.referenceContext.compilationResult.simpleNameReferences = simpleRefs; size = this.rootReferences.size; char[][] rootRefs = new char[size][]; for (int i = 0; i < size; i++) rootRefs[i] = this.rootReferences.elementAt(i); this.referenceContext.compilationResult.rootReferences = rootRefs; } public String toString() { return "--- CompilationUnit Scope : " + new String(this.referenceContext.getFileName()); //$NON-NLS-1$ } private ReferenceBinding typeToRecord(TypeBinding type) { if (type.isArrayType()) type = ((ArrayBinding)type).leafComponentType; switch (type.kind()) { case Binding.BASE_TYPE: case Binding.TYPE_PARAMETER: case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: return null; case Binding.PARAMETERIZED_TYPE: case Binding.RAW_TYPE: type = type.erasure(); } ReferenceBinding refType = (ReferenceBinding)type; if (refType.isLocalType()) return null; return refType; } public void verifyMethods(MethodVerifier verifier) { for (int i = 0, length = this.topLevelTypes.length; i < length; i++) this.topLevelTypes[i].verifyMethods(verifier); } }