Merge "Make JAVA_TOOLS_JAR error a little more descriptive." am: 7b46b5a466 am: 01990004ec am: ed699579df

Original change: https://android-review.googlesource.com/c/platform/external/doclava/+/1705585

Change-Id: I1e9f0af8a47f28b23843d536a2d7b5cc7461eb68
diff --git a/Android.bp b/Android.bp
index a3e2317..4c81ca5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,6 +54,7 @@
 
 java_library_host {
     name: "doclava",
+    java_version: "1.8",
     srcs: [
         "src/**/*.java",
     ],
@@ -69,6 +70,7 @@
 
 java_library_host {
     name: "doclava-no-guava",
+    java_version: "1.8",
     srcs: [
         "src/**/*.java",
     ],
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index cf14237..2143875 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -23,6 +23,7 @@
 import com.sun.javadoc.AnnotationValue;
 import com.sun.javadoc.ClassDoc;
 import com.sun.javadoc.ConstructorDoc;
+import com.sun.javadoc.Doc;
 import com.sun.javadoc.ExecutableMemberDoc;
 import com.sun.javadoc.FieldDoc;
 import com.sun.javadoc.MemberDoc;
@@ -43,7 +44,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 
 public class Converter {
   private static RootDoc root;
@@ -288,7 +288,7 @@
   }
 
   private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) {
-    SourcePosition pos = m.position();
+    SourcePosition pos = getPositionSafe(m);
     int len = p.length;
     ParameterInfo[] q = new ParameterInfo[len];
     for (int i = 0; i < len; i++) {
@@ -447,6 +447,27 @@
     return (MethodInfo) mMethods.obtain(o);
   }
 
+  /**
+   * Returns the result of a call to {@code f.position()} but avoids a {@link NullPointerException}
+   * if {@code f.position()} throws a {@link NullPointerException} due to the {@link Doc} element
+   * being related to an annotation.
+   *
+   * <p>This is consistent with the Java runtime where references to annotations that do not exist
+   * on the classpath are allowed. This method is used, for example, to allow Doclava to process
+   * JARs in the classpath that were generated by the Kotlin compiler, without requiring the Kotlin
+   * stdlib to exist on the classpath when Doclava is executed.
+   */
+  private static SourcePosition getPositionSafe(Doc f) {
+    try {
+      return f.position();
+    } catch (NullPointerException e) {
+      if (f.isAnnotationTypeElement() || mFieldDocElementsOnAnnotationClasses.contains(f)) {
+        return null;
+      }
+      throw e;
+    }
+  }
+
   private static Cache mMethods = new Cache() {
     @Override
     protected Object make(Object o) {
@@ -466,7 +487,7 @@
                     new ArrayList<ParameterInfo>(Arrays.asList(
                             Converter.convertParameters(m.parameters(), m))),
                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
-                            m.thrownExceptions()))), Converter.convertSourcePosition(m.position()),
+                            m.thrownExceptions()))), Converter.convertSourcePosition(getPositionSafe(m)),
                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
                             Converter.convertAnnotationInstances(m.annotations()))));
         result.setVarargs(m.isVarArgs());
@@ -547,7 +568,7 @@
           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
-          f.constantValue(), Converter.convertSourcePosition(f.position()),
+          f.constantValue(), Converter.convertSourcePosition(getPositionSafe(f)),
           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
               .convertAnnotationInstances(f.annotations()))));
     }
@@ -760,6 +781,12 @@
       new HashMap<AnnotationValue, AnnotationValueInfo>();
   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
       new HashSet<AnnotationValue>();
+  /**
+   * Stores the {@link FieldDoc} instances the are contained in annotation classes. This is used by
+   * {@link #getPositionSafe(Doc)} to allow {@link NullPointerException} for annotation classes to
+   * be ignored.
+   */
+  private static HashSet<FieldDoc> mFieldDocElementsOnAnnotationClasses = new HashSet<FieldDoc>();
 
   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
     if (o == null) {
@@ -769,6 +796,9 @@
     if (v != null) return v;
     v = new AnnotationValueInfo(element);
     mAnnotationValues.put(o, v);
+    if (o.value() instanceof FieldDoc) {
+      mFieldDocElementsOnAnnotationClasses.add((FieldDoc) o.value());
+    }
     if (mAnnotationValuesNeedingInit != null) {
       mAnnotationValuesNeedingInit.add(o);
     } else {