/* * Copyright (C) 2015 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.langmodel; import static com.google.auto.common.MoreElements.getPackage; import static com.google.common.base.Preconditions.checkArgument; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import com.google.auto.common.MoreElements; import java.util.Optional; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.NoType; import javax.lang.model.type.NullType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.TypeVisitor; import javax.lang.model.type.WildcardType; import javax.lang.model.util.SimpleElementVisitor6; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.SimpleTypeVisitor8; /** * Utility methods for determining whether a {@linkplain TypeMirror type} or an {@linkplain Element * element} is accessible given the rules outlined in section 6.6 of the * Java Language Specification. * *

This class only provides an approximation for accessibility. It does not always yield the same * result as the compiler, but will always err on the side of declaring something inaccessible. This * ensures that using this class will never result in generating code that will not compile. * *

Whenever compiler independence is not a requirement, the compiler-specific implementation of * this functionality should be preferred. For example, {@link * com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be * preferable for {@code javac}. */ public final class Accessibility { /** Returns true if the given type can be referenced from any package. */ public static boolean isTypePubliclyAccessible(TypeMirror type) { return type.accept(new TypeAccessibilityVisitor(), null); } /** Returns true if the given type can be referenced from code in the given package. */ public static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) { return type.accept(new TypeAccessibilityVisitor(packageName), null); } private static boolean isTypeAccessibleFrom(TypeMirror type, Optional packageName) { return type.accept(new TypeAccessibilityVisitor(packageName), null); } private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor6 { final Optional packageName; TypeAccessibilityVisitor() { this(Optional.empty()); } TypeAccessibilityVisitor(String packageName) { this(Optional.of(packageName)); } TypeAccessibilityVisitor(Optional packageName) { this.packageName = packageName; } boolean isAccessible(TypeMirror type) { return type.accept(this, null); } @Override public Boolean visitNoType(NoType type, Void p) { return true; } @Override public Boolean visitDeclared(DeclaredType type, Void p) { if (!isAccessible(type.getEnclosingType())) { // TODO(gak): investigate this check. see comment in Binding return false; } if (!isElementAccessibleFrom(type.asElement(), packageName)) { return false; } for (TypeMirror typeArgument : type.getTypeArguments()) { if (!isAccessible(typeArgument)) { return false; } } return true; } @Override public Boolean visitArray(ArrayType type, Void p) { return type.getComponentType().accept(this, null); } @Override public Boolean visitPrimitive(PrimitiveType type, Void p) { return true; } @Override public Boolean visitNull(NullType type, Void p) { return true; } @Override public Boolean visitTypeVariable(TypeVariable type, Void p) { // a _reference_ to a type variable is always accessible return true; } @Override public Boolean visitWildcard(WildcardType type, Void p) { if (type.getExtendsBound() != null && !isAccessible(type.getExtendsBound())) { return false; } if (type.getSuperBound() != null && !isAccessible(type.getSuperBound())) { return false; } return true; } @Override protected Boolean defaultAction(TypeMirror type, Void p) { throw new IllegalArgumentException( String.format( "%s of kind %s should not be checked for accessibility", type, type.getKind())); } } /** Returns true if the given element can be referenced from any package. */ public static boolean isElementPubliclyAccessible(Element element) { return element.accept(new ElementAccessibilityVisitor(), null); } /** Returns true if the given element can be referenced from code in the given package. */ // TODO(gak): account for protected public static boolean isElementAccessibleFrom(Element element, String packageName) { return element.accept(new ElementAccessibilityVisitor(packageName), null); } private static boolean isElementAccessibleFrom(Element element, Optional packageName) { return element.accept(new ElementAccessibilityVisitor(packageName), null); } /** Returns true if the given element can be referenced from other code in its own package. */ public static boolean isElementAccessibleFromOwnPackage(Element element) { return isElementAccessibleFrom( element, MoreElements.getPackage(element).getQualifiedName().toString()); } private static final class ElementAccessibilityVisitor extends SimpleElementVisitor6 { final Optional packageName; ElementAccessibilityVisitor() { this(Optional.empty()); } ElementAccessibilityVisitor(String packageName) { this(Optional.of(packageName)); } ElementAccessibilityVisitor(Optional packageName) { this.packageName = packageName; } @Override public Boolean visitPackage(PackageElement element, Void p) { return true; } @Override public Boolean visitType(TypeElement element, Void p) { switch (element.getNestingKind()) { case MEMBER: return accessibleMember(element); case TOP_LEVEL: return accessibleModifiers(element); case ANONYMOUS: case LOCAL: return false; } throw new AssertionError(); } boolean accessibleMember(Element element) { if (!element.getEnclosingElement().accept(this, null)) { return false; } return accessibleModifiers(element); } boolean accessibleModifiers(Element element) { if (element.getModifiers().contains(PUBLIC)) { return true; } else if (element.getModifiers().contains(PRIVATE)) { return false; } else if (packageName.isPresent() && getPackage(element).getQualifiedName().contentEquals(packageName.get())) { return true; } else { return false; } } @Override public Boolean visitTypeParameter(TypeParameterElement element, Void p) { throw new IllegalArgumentException( "It does not make sense to check the accessibility of a type parameter"); } @Override public Boolean visitExecutable(ExecutableElement element, Void p) { return accessibleMember(element); } @Override public Boolean visitVariable(VariableElement element, Void p) { ElementKind kind = element.getKind(); checkArgument(kind.isField(), "checking a variable that isn't a field: %s", kind); return accessibleMember(element); } } private static final TypeVisitor> RAW_TYPE_ACCESSIBILITY_VISITOR = new SimpleTypeVisitor8>() { @Override protected Boolean defaultAction(TypeMirror e, Optional requestingPackage) { return isTypeAccessibleFrom(e, requestingPackage); } @Override public Boolean visitDeclared(DeclaredType t, Optional requestingPackage) { return isElementAccessibleFrom(t.asElement(), requestingPackage); } }; /** Returns true if the raw type of {@code type} is accessible from the given package. */ public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) { return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.of(requestingPackage)); } /** Returns true if the raw type of {@code type} is accessible from any package. */ public static boolean isRawTypePubliclyAccessible(TypeMirror type) { return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.empty()); } private Accessibility() {} }