| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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 |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contributions for |
| * bug 186342 - [compiler][null] Using annotations for null checking |
| * bug 365519 - editorial cleanup after bug 186342 and bug 365387 |
| * bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfPackage; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfType; |
| |
| public class PackageBinding extends Binding implements TypeConstants { |
| public long tagBits = 0; // See values in the interface TagBits below |
| |
| public char[][] compoundName; |
| PackageBinding parent; |
| public LookupEnvironment environment; |
| HashtableOfType knownTypes; |
| HashtableOfPackage knownPackages; |
| |
| // code representing the default that has been defined for this package (using @NonNullByDefault) |
| // one of Binding.{NO_NULL_DEFAULT,NULL_UNSPECIFIED_BY_DEFAULT,NONNULL_BY_DEFAULT} |
| protected int defaultNullness = NO_NULL_DEFAULT; |
| |
| protected PackageBinding() { |
| // for creating problem package |
| } |
| public PackageBinding(char[] topLevelPackageName, LookupEnvironment environment) { |
| this(new char[][] {topLevelPackageName}, null, environment); |
| } |
| /* Create the default package. |
| */ |
| public PackageBinding(char[][] compoundName, PackageBinding parent, LookupEnvironment environment) { |
| this.compoundName = compoundName; |
| this.parent = parent; |
| this.environment = environment; |
| this.knownTypes = null; // initialized if used... class counts can be very large 300-600 |
| this.knownPackages = new HashtableOfPackage(3); // sub-package counts are typically 0-3 |
| if (compoundName != CharOperation.NO_CHAR_CHAR) |
| checkIfNullAnnotationPackage(); |
| } |
| |
| public PackageBinding(LookupEnvironment environment) { |
| this(CharOperation.NO_CHAR_CHAR, null, environment); |
| } |
| /*private*/ void addNotFoundPackage(char[] simpleName) { |
| this.knownPackages.put(simpleName, LookupEnvironment.TheNotFoundPackage); |
| } |
| private void addNotFoundType(char[] simpleName) { |
| if (this.knownTypes == null) |
| this.knownTypes = new HashtableOfType(25); |
| this.knownTypes.put(simpleName, LookupEnvironment.TheNotFoundType); |
| } |
| void addPackage(PackageBinding element) { |
| if ((element.tagBits & TagBits.HasMissingType) == 0) clearMissingTagBit(); |
| this.knownPackages.put(element.compoundName[element.compoundName.length - 1], element); |
| } |
| void addType(ReferenceBinding element) { |
| if ((element.tagBits & TagBits.HasMissingType) == 0) clearMissingTagBit(); |
| if (this.knownTypes == null) |
| this.knownTypes = new HashtableOfType(25); |
| char [] name = element.compoundName[element.compoundName.length - 1]; |
| ReferenceBinding priorType = this.knownTypes.getput(name, element); |
| if (priorType != null && priorType.isUnresolvedType() && !element.isUnresolvedType()) { |
| ((UnresolvedReferenceBinding) priorType).setResolvedType(element, this.environment); |
| } |
| if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) |
| if (element.isAnnotationType() || element instanceof UnresolvedReferenceBinding) // unresolved types don't yet have the modifiers set |
| checkIfNullAnnotationType(element); |
| } |
| |
| void clearMissingTagBit() { |
| PackageBinding current = this; |
| do { |
| current.tagBits &= ~TagBits.HasMissingType; |
| } while ((current = current.parent) != null); |
| } |
| /* |
| * slash separated name |
| * org.eclipse.jdt.core --> org/eclipse/jdt/core |
| */ |
| public char[] computeUniqueKey(boolean isLeaf) { |
| return CharOperation.concatWith(this.compoundName, '/'); |
| } |
| private PackageBinding findPackage(char[] name, char[] mod) { |
| ModuleBinding module = this.environment.getModule(mod); |
| PackageBinding sub = module == null ? null : module.getPackage(this.compoundName, name); |
| if (sub == null) |
| return null; |
| |
| // char[][] subPkgCompoundName = CharOperation.arrayConcat(this.compoundName, name); |
| // PackageBinding subPackageBinding = new PackageBinding(subPkgCompoundName, this, this.environment); |
| // addPackage(subPackageBinding); |
| // return subPackageBinding; |
| return sub; |
| } |
| /* Answer the subpackage named name; ask the oracle for the package if its not in the cache. |
| * Answer null if it could not be resolved. |
| * |
| * NOTE: This should only be used when we know there is NOT a type with the same name. |
| */ |
| PackageBinding getPackage(char[] name, char[] mod) { |
| PackageBinding binding = getPackage0(name); |
| if (binding != null) { |
| if (binding == LookupEnvironment.TheNotFoundPackage) |
| return null; |
| else |
| return binding; |
| } |
| if ((binding = findPackage(name, mod)) != null) |
| return binding; |
| |
| // not found so remember a problem package binding in the cache for future lookups |
| addNotFoundPackage(name); |
| return null; |
| } |
| /* Answer the subpackage named name if it exists in the cache. |
| * Answer theNotFoundPackage if it could not be resolved the first time |
| * it was looked up, otherwise answer null. |
| * |
| * NOTE: Senders must convert theNotFoundPackage into a real problem |
| * package if its to returned. |
| */ |
| |
| PackageBinding getPackage0(char[] name) { |
| return this.knownPackages.get(name); |
| } |
| /* Answer the type named name; ask the oracle for the type if its not in the cache. |
| * Answer a NotVisible problem type if the type is not visible from the invocationPackage. |
| * Answer null if it could not be resolved. |
| * |
| * NOTE: This should only be used by source types/scopes which know there is NOT a |
| * package with the same name. |
| */ |
| |
| ReferenceBinding getType(char[] name, char[] mod) { |
| ReferenceBinding referenceBinding = getType0(name); |
| if (referenceBinding == null) { |
| if ((referenceBinding = this.environment.askForType(this, name, mod)) == null) { |
| // not found so remember a problem type binding in the cache for future lookups |
| addNotFoundType(name); |
| return null; |
| } |
| } |
| |
| if (referenceBinding == LookupEnvironment.TheNotFoundType) |
| return null; |
| |
| referenceBinding = (ReferenceBinding) BinaryTypeBinding.resolveType(referenceBinding, this.environment, false /* no raw conversion for now */); |
| if (referenceBinding.isNestedType()) |
| return new ProblemReferenceBinding(new char[][]{ name }, referenceBinding, ProblemReasons.InternalNameProvided); |
| return referenceBinding; |
| } |
| /* Answer the type named name if it exists in the cache. |
| * Answer theNotFoundType if it could not be resolved the first time |
| * it was looked up, otherwise answer null. |
| * |
| * NOTE: Senders must convert theNotFoundType into a real problem |
| * reference type if its to returned. |
| */ |
| |
| ReferenceBinding getType0(char[] name) { |
| if (this.knownTypes == null) |
| return null; |
| return this.knownTypes.get(name); |
| } |
| /* Answer the package or type named name; ask the oracle if it is not in the cache. |
| * Answer null if it could not be resolved. |
| * |
| * When collisions exist between a type name & a package name, answer the type. |
| * Treat the package as if it does not exist... a problem was already reported when the type was defined. |
| * |
| * NOTE: no visibility checks are performed. |
| * THIS SHOULD ONLY BE USED BY SOURCE TYPES/SCOPES. |
| */ |
| |
| public Binding getTypeOrPackage(char[] name, char[] mod) { |
| ReferenceBinding referenceBinding = getType0(name); |
| if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { |
| referenceBinding = (ReferenceBinding) BinaryTypeBinding.resolveType(referenceBinding, this.environment, false /* no raw conversion for now */); |
| if (referenceBinding.isNestedType()) { |
| return new ProblemReferenceBinding(new char[][]{name}, referenceBinding, ProblemReasons.InternalNameProvided); |
| } |
| if ((referenceBinding.tagBits & TagBits.HasMissingType) == 0) { |
| return referenceBinding; |
| } |
| // referenceBinding is a MissingType, will return it if no package is found |
| } |
| |
| PackageBinding packageBinding = getPackage0(name); |
| if (packageBinding != null && packageBinding != LookupEnvironment.TheNotFoundPackage) { |
| return (packageBinding.canBeSeenBy(mod)) ? packageBinding : null; |
| //return packageBinding; |
| } |
| if (referenceBinding == null) { // have not looked for it before |
| if ((referenceBinding = this.environment.askForType(this, name, mod)) != null) { |
| if (referenceBinding.isNestedType()) { |
| return new ProblemReferenceBinding(new char[][]{name}, referenceBinding, ProblemReasons.InternalNameProvided); |
| } |
| return referenceBinding; |
| } |
| |
| // Since name could not be found, add a problem binding |
| // to the collections so it will be reported as an error next time. |
| addNotFoundType(name); |
| } |
| |
| if (packageBinding == null) { // have not looked for it before |
| if ((packageBinding = findPackage(name, mod)) != null) { |
| return packageBinding; |
| } |
| if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { |
| return referenceBinding; // found cached missing type - check if package conflict |
| } |
| //addNotFoundPackage(name); Not a package in module mod does not mean not a package at all |
| } |
| |
| return null; |
| } |
| public final boolean isViewedAsDeprecated() { |
| if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { |
| this.tagBits |= TagBits.DeprecatedAnnotationResolved; |
| if (this.compoundName != CharOperation.NO_CHAR_CHAR) { |
| ReferenceBinding packageInfo = this.getType(TypeConstants.PACKAGE_INFO_NAME, null); |
| if (packageInfo != null) { |
| packageInfo.initializeDeprecatedAnnotationTagBits(); |
| this.tagBits |= packageInfo.tagBits & TagBits.AllStandardAnnotationsMask; |
| } |
| } |
| } |
| return (this.tagBits & TagBits.AnnotationDeprecated) != 0; |
| } |
| /* API |
| * Answer the receiver's binding type from Binding.BindingID. |
| */ |
| public final int kind() { |
| return Binding.PACKAGE; |
| } |
| |
| public int problemId() { |
| if ((this.tagBits & TagBits.HasMissingType) != 0) |
| return ProblemReasons.NotFound; |
| return ProblemReasons.NoError; |
| } |
| |
| |
| void checkIfNullAnnotationPackage() { |
| LookupEnvironment env = this.environment; |
| if (env.globalOptions.isAnnotationBasedNullAnalysisEnabled) { |
| if (isPackageOfQualifiedTypeName(this.compoundName, env.getNullableAnnotationName())) |
| env.nullableAnnotationPackage = this; |
| if (isPackageOfQualifiedTypeName(this.compoundName, env.getNonNullAnnotationName())) |
| env.nonnullAnnotationPackage = this; |
| if (isPackageOfQualifiedTypeName(this.compoundName, env.getNonNullByDefaultAnnotationName())) |
| env.nonnullByDefaultAnnotationPackage = this; |
| } |
| } |
| public boolean canBeSeenBy(char[] clientModule) { |
| ModuleBinding client = this.environment.getModule(clientModule); |
| return client.canSee(this); |
| } |
| private boolean isPackageOfQualifiedTypeName(char[][] packageName, char[][] typeName) { |
| int length; |
| if (typeName == null || (length = packageName.length) != typeName.length -1) |
| return false; |
| for (int i=0; i<length; i++) |
| if (!CharOperation.equals(packageName[i], typeName[i])) |
| return false; |
| return true; |
| } |
| |
| void checkIfNullAnnotationType(ReferenceBinding type) { |
| // check if type is one of the configured null annotation types |
| // if so mark as a well known type using the corresponding typeBit: |
| if (this.environment.nullableAnnotationPackage == this |
| && CharOperation.equals(type.compoundName, this.environment.getNullableAnnotationName())) { |
| type.typeBits |= TypeIds.BitNullableAnnotation; |
| if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type |
| this.environment.nullableAnnotationPackage = null; // don't check again |
| } else if (this.environment.nonnullAnnotationPackage == this |
| && CharOperation.equals(type.compoundName, this.environment.getNonNullAnnotationName())) { |
| type.typeBits |= TypeIds.BitNonNullAnnotation; |
| if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type |
| this.environment.nonnullAnnotationPackage = null; // don't check again |
| } else if (this.environment.nonnullByDefaultAnnotationPackage == this |
| && CharOperation.equals(type.compoundName, this.environment.getNonNullByDefaultAnnotationName())) { |
| type.typeBits |= TypeIds.BitNonNullByDefaultAnnotation; |
| if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type |
| this.environment.nonnullByDefaultAnnotationPackage = null; // don't check again |
| } else { |
| type.typeBits |= this.environment.getNullAnnotationBit(type.compoundName); |
| } |
| } |
| |
| public char[] readableName() /*java.lang*/ { |
| return CharOperation.concatWith(this.compoundName, '.'); |
| } |
| public String toString() { |
| String str; |
| if (this.compoundName == CharOperation.NO_CHAR_CHAR) { |
| str = "The Default Package"; //$NON-NLS-1$ |
| } else { |
| str = "package " + ((this.compoundName != null) ? CharOperation.toString(this.compoundName) : "UNNAMED"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if ((this.tagBits & TagBits.HasMissingType) != 0) { |
| str += "[MISSING]"; //$NON-NLS-1$ |
| } |
| return str; |
| } |
| } |