aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skylarkinterface
diff options
context:
space:
mode:
authorGravatar cparsons <cparsons@google.com>2018-05-24 13:48:22 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-05-24 13:51:33 -0700
commit4dc97ffb3c7afb463678997d5549d9b7f959e394 (patch)
tree860f6cec29ac91ae42a53eccfaad0a39fd61b360 /src/main/java/com/google/devtools/build/lib/skylarkinterface
parentee5ea6bde9c774623c8083914b84f6a53dc82288 (diff)
Make @SkylarkModule detection (through superclasses/superinterfaces) well-defined.
Moving forward, if a class not already annotated with @SkylarkModule has SkylarkModule supertypes A and B, then it must be the case that A is a type of B, or B is a type of A. The skylark type of a given class is dictated by the *most specific* superclass annotated with @SkylarkModule. RELNOTES: None. PiperOrigin-RevId: 197946898
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skylarkinterface')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java52
1 files changed, 45 insertions, 7 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java
index 81cd6aec06..3f25c32598 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkInterfaceUtils.java
@@ -33,6 +33,46 @@ public class SkylarkInterfaceUtils {
}
}
+ /**
+ * Returns the more specific class of two classes. Class x is more specific than class y
+ * if x is assignable to y. For example, of Integer.class and Object.class, Integer.class is more
+ * specific.
+ *
+ * <p>If either class is null, returns the other class.</p>
+ *
+ * <p>If the classes are identical, returns the class.</p>
+ *
+ * @throws IllegalArgumentException if neither class is assignable to the other
+ */
+ private static <T extends Annotation> ClassWithAnnotation<T> moreSpecificClass(
+ ClassWithAnnotation<T> x, ClassWithAnnotation<T> y) {
+ if (x == null) {
+ return y;
+ } else if (y == null) {
+ return x;
+ }
+ Class<?> xClass = x.klass;
+ Class<?> yClass = y.klass;
+ if (xClass.isAssignableFrom(yClass)) {
+ return y;
+ } else if (yClass.isAssignableFrom(xClass)) {
+ return x;
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "Expected one of %s and %s to be assignable to each other",
+ xClass, yClass));
+ }
+ }
+
+ /**
+ * Searches a class or interface's class hierarchy for the given class annotation.
+ *
+ * <p>If the given class annotation appears multiple times within the class hierachy, this
+ * chooses the annotation on the most-specified class in the hierarchy.</p>
+ *
+ * @return a {@link ClassWithAnnotation} containing the best-fit annotation and the class
+ * it was declared on
+ */
@Nullable
private static <T extends Annotation> ClassWithAnnotation<T> searchForClassAnnotation(
Class<?> classObj,
@@ -40,20 +80,18 @@ public class SkylarkInterfaceUtils {
if (classObj.isAnnotationPresent(annotationClass)) {
return new ClassWithAnnotation<T>(classObj, classObj.getAnnotation(annotationClass));
}
+ ClassWithAnnotation<T> bestCandidate = null;
+
Class<?> superclass = classObj.getSuperclass();
if (superclass != null) {
ClassWithAnnotation<T> result = searchForClassAnnotation(superclass, annotationClass);
- if (result != null) {
- return result;
- }
+ bestCandidate = moreSpecificClass(result, bestCandidate);
}
for (Class<?> interfaceObj : classObj.getInterfaces()) {
ClassWithAnnotation<T> result = searchForClassAnnotation(interfaceObj, annotationClass);
- if (result != null) {
- return result;
- }
+ bestCandidate = moreSpecificClass(result, bestCandidate);
}
- return null;
+ return bestCandidate;
}
/**