1 /* 2 * Copyright (C) 2015 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.langmodel; 18 19 import static androidx.room.compiler.processing.XElementKt.isField; 20 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 21 import static androidx.room.compiler.processing.XTypeKt.isArray; 22 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 23 import static dagger.internal.codegen.xprocessing.XElements.isExecutable; 24 import static dagger.internal.codegen.xprocessing.XElements.isPackage; 25 import static dagger.internal.codegen.xprocessing.XElements.isPrivate; 26 import static dagger.internal.codegen.xprocessing.XElements.isPublic; 27 import static dagger.internal.codegen.xprocessing.XTypeElements.isNested; 28 import static dagger.internal.codegen.xprocessing.XTypes.asArray; 29 import static dagger.internal.codegen.xprocessing.XTypes.getEnclosingType; 30 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 31 import static dagger.internal.codegen.xprocessing.XTypes.isNoType; 32 import static dagger.internal.codegen.xprocessing.XTypes.isNullType; 33 import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive; 34 import static dagger.internal.codegen.xprocessing.XTypes.isTypeVariable; 35 import static dagger.internal.codegen.xprocessing.XTypes.isWildcard; 36 37 import androidx.room.compiler.processing.XElement; 38 import androidx.room.compiler.processing.XProcessingEnv; 39 import androidx.room.compiler.processing.XType; 40 import com.squareup.javapoet.ClassName; 41 import com.squareup.javapoet.TypeName; 42 import java.util.Optional; 43 44 /** 45 * Utility methods for determining whether a {@link XType} or an {@link XElement} is accessible 46 * given the rules outlined in <a 47 * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the 48 * Java Language Specification</a>. 49 * 50 * <p>This class only provides an approximation for accessibility. It does not always yield the same 51 * result as the compiler, but will always err on the side of declaring something inaccessible. This 52 * ensures that using this class will never result in generating code that will not compile. 53 */ 54 public final class Accessibility { 55 /** Returns true if the given type can be referenced from any package. */ isTypePubliclyAccessible(XType type)56 public static boolean isTypePubliclyAccessible(XType type) { 57 return isTypeAccessibleFrom(type, Optional.empty()); 58 } 59 60 /** Returns true if the given type can be referenced from code in the given package. */ isTypeAccessibleFrom(XType type, String packageName)61 public static boolean isTypeAccessibleFrom(XType type, String packageName) { 62 return isTypeAccessibleFrom(type, Optional.of(packageName)); 63 } 64 isTypeAccessibleFrom(XType type, Optional<String> packageName)65 private static boolean isTypeAccessibleFrom(XType type, Optional<String> packageName) { 66 if (isNoType(type) || isPrimitive(type) || isNullType(type) || isTypeVariable(type)) { 67 return true; 68 } else if (isArray(type)) { 69 return isTypeAccessibleFrom(asArray(type).getComponentType(), packageName); 70 } else if (isDeclared(type)) { 71 XType enclosingType = getEnclosingType(type); 72 if (enclosingType != null && !isTypeAccessibleFrom(enclosingType, packageName)) { 73 return false; 74 } 75 if (!isElementAccessibleFrom(type.getTypeElement(), packageName)) { 76 return false; 77 } 78 return type.getTypeArguments().stream() 79 .allMatch(typeArgument -> isTypeAccessibleFrom(typeArgument, packageName)); 80 } else if (isWildcard(type)) { 81 return type.extendsBound() == null || isTypeAccessibleFrom(type.extendsBound(), packageName); 82 } 83 throw new AssertionError(String.format("%s should not be checked for accessibility", type)); 84 } 85 86 /** Returns true if the given element can be referenced from any package. */ isElementPubliclyAccessible(XElement element)87 public static boolean isElementPubliclyAccessible(XElement element) { 88 return isElementAccessibleFrom(element, Optional.empty()); 89 } 90 91 /** Returns true if the given element can be referenced from other code in its own package. */ isElementAccessibleFromOwnPackage(XElement element)92 public static boolean isElementAccessibleFromOwnPackage(XElement element) { 93 return isElementAccessibleFrom( 94 element, Optional.of(element.getClosestMemberContainer().getClassName().packageName())); 95 } 96 97 /** Returns true if the given element can be referenced from code in the given package. */ 98 // TODO(gak): account for protected 99 // TODO(bcorso): account for kotlin srcs (package-private doesn't exist, internal does exist). isElementAccessibleFrom(XElement element, String packageName)100 public static boolean isElementAccessibleFrom(XElement element, String packageName) { 101 return isElementAccessibleFrom(element, Optional.of(packageName)); 102 } 103 isElementAccessibleFrom(XElement element, Optional<String> packageName)104 private static boolean isElementAccessibleFrom(XElement element, Optional<String> packageName) { 105 if (isPackage(element)) { 106 return true; 107 } else if (isTypeElement(element)) { 108 return isNested(asTypeElement(element)) 109 ? accessibleMember(element, packageName) 110 : accessibleModifiers(element, packageName); 111 } else if (isExecutable(element) || isField(element)) { 112 return accessibleMember(element, packageName); 113 } 114 throw new AssertionError(); 115 } 116 accessibleMember(XElement element, Optional<String> packageName)117 private static boolean accessibleMember(XElement element, Optional<String> packageName) { 118 return isElementAccessibleFrom(element.getEnclosingElement(), packageName) 119 && accessibleModifiers(element, packageName); 120 } 121 accessibleModifiers(XElement element, Optional<String> packageName)122 private static boolean accessibleModifiers(XElement element, Optional<String> packageName) { 123 if (isPublic(element)) { 124 return true; 125 } else if (isPrivate(element)) { 126 return false; 127 } 128 return packageName.isPresent() 129 && element 130 .getClosestMemberContainer() 131 .getClassName() 132 .packageName() 133 .contentEquals(packageName.get()); 134 } 135 136 /** Returns true if the raw type of {@code type} is accessible from the given package. */ isRawTypeAccessible(XType type, String requestingPackage)137 public static boolean isRawTypeAccessible(XType type, String requestingPackage) { 138 return isDeclared(type) 139 ? isElementAccessibleFrom(type.getTypeElement(), requestingPackage) 140 : isTypeAccessibleFrom(type, requestingPackage); 141 } 142 143 /** Returns true if the raw type of {@code type} is accessible from any package. */ isRawTypePubliclyAccessible(XType type)144 public static boolean isRawTypePubliclyAccessible(XType type) { 145 return isDeclared(type) 146 ? isElementPubliclyAccessible(type.getTypeElement()) 147 : isTypePubliclyAccessible(type); 148 } 149 150 /** 151 * Returns an accessible type in {@code requestingClass}'s package based on {@code type}: 152 * 153 * <ul> 154 * <li>If {@code type} is accessible from the package, returns it. 155 * <li>If not, but {@code type}'s raw type is accessible from the package, returns the raw type. 156 * <li>Otherwise returns {@link Object}. 157 * </ul> 158 */ accessibleTypeName( XType type, ClassName requestingClass, XProcessingEnv processingEnv)159 public static TypeName accessibleTypeName( 160 XType type, ClassName requestingClass, XProcessingEnv processingEnv) { 161 if (isTypeAccessibleFrom(type, requestingClass.packageName())) { 162 return type.getTypeName(); 163 } else if (isDeclared(type) && isRawTypeAccessible(type, requestingClass.packageName())) { 164 return type.getRawType().getTypeName(); 165 } else { 166 return TypeName.OBJECT; 167 } 168 } 169 Accessibility()170 private Accessibility() {} 171 } 172