• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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