summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2017-06-12 17:26:18 -0400
committerAlan Viverette <alanv@google.com>2017-06-13 11:01:45 -0400
commitbb3c3f1d4ca6dfc90ac68019b71d732a8455912d (patch)
tree6d009b5abb7a2cbe17244382ca79f1282c3db0bf
parentf82b9336fba4305e8f85ad6d201a05b4da165d31 (diff)
downloadplatform_external_doclava-bb3c3f1d4ca6dfc90ac68019b71d732a8455912d.tar.gz
platform_external_doclava-bb3c3f1d4ca6dfc90ac68019b71d732a8455912d.tar.bz2
platform_external_doclava-bb3c3f1d4ca6dfc90ac68019b71d732a8455912d.zip
Add Doclava support for federated APIs not referenced from code
Previously, @link and @see could only be used to reference APIs that were also referenced from code. Since the platform cannot reference Support Library APIs from code, this limitation prevented federation. Federated APIs are now registered in the global Converter and TypeInfos may now lazily resolve their classes. Bug: 36959367 Test: make docs Change-Id: Ie0c0672dbd8bfe2837ec60bc7c7e49cf67c43236
-rw-r--r--src/com/google/doclava/Converter.java62
-rw-r--r--src/com/google/doclava/Doclava.java5
-rw-r--r--src/com/google/doclava/FederationTagger.java67
-rw-r--r--src/com/google/doclava/TypeInfo.java17
4 files changed, 128 insertions, 23 deletions
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index ee91960..0d4e455 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -16,7 +16,7 @@
package com.google.doclava;
-
+import com.google.doclava.apicheck.ApiInfo;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.AnnotationTypeElementDoc;
@@ -42,12 +42,15 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
public class Converter {
private static RootDoc root;
+ private static List<ApiInfo> apis;
public static void makeInfo(RootDoc r) {
root = r;
+ apis = new ArrayList<>();
// create the objects
ClassDoc[] classes = getClasses(r);
@@ -78,6 +81,15 @@ public class Converter {
mRootClasses = Converter.convertClasses(classes);
}
+ /**
+ * Adds additional APIs to be available from calls to obtain() methods.
+ *
+ * @param api the APIs to add, must be non-{@code null}
+ */
+ public static void addApiInfo(ApiInfo api) {
+ apis.add(api);
+ }
+
private static ClassDoc[] getClasses(RootDoc r) {
ClassDoc[] classDocs = r.classes();
ArrayList<ClassDoc> filtered = new ArrayList<ClassDoc>(classDocs.length);
@@ -155,12 +167,54 @@ public class Converter {
Converter.convertClasses(c.innerClasses(false)))));
}
+ /**
+ * Obtains a {@link ClassInfo} describing the specified class. If the class
+ * was not specified on the source path, this method will attempt to locate
+ * the class within the list of federated APIs.
+ *
+ * @param className the fully-qualified class name to search for
+ * @return info for the specified class, or {@code null} if not available
+ * @see #addApiInfo(ApiInfo)
+ */
public static ClassInfo obtainClass(String className) {
- return Converter.obtainClass(root.classNamed(className));
+ ClassInfo result = Converter.obtainClass(root.classNamed(className));
+ if (result != null) {
+ return result;
+ }
+
+ for (ApiInfo api : apis) {
+ result = api.findClass(className);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
}
+ /**
+ * Obtains a {@link PackageInfo} describing the specified package. If the
+ * package was not specified on the source path, this method will attempt to
+ * locate the package within the list of federated APIs.
+ *
+ * @param packageName the fully-qualified package name to search for
+ * @return info for the specified package, or {@code null} if not available
+ * @see #addApiInfo(ApiInfo)
+ */
public static PackageInfo obtainPackage(String packageName) {
- return Converter.obtainPackage(root.packageNamed(packageName));
+ PackageInfo result = Converter.obtainPackage(root.packageNamed(packageName));
+ if (result != null) {
+ return result;
+ }
+
+ for (ApiInfo api : apis) {
+ result = api.getPackages().get(packageName);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
}
private static TagInfo convertTag(Tag tag) {
@@ -456,7 +510,7 @@ public class Converter {
}
// End of workaround.
MethodInfo result =
- new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
+ new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
name, m.signature(), Converter.obtainClass(m.containingClass()), Converter
.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 29ef509..9d3f1b6 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -415,6 +415,11 @@ public class Doclava {
// don't do ref doc tasks in devsite static-only builds
if (!DEVSITE_STATIC_ONLY) {
+ // Load additional data structures from federated sites.
+ for(FederatedSite site : federationTagger.getSites()) {
+ Converter.addApiInfo(site.apiInfo());
+ }
+
// Apply @since tags from the XML file
sinceTagger.tagAll(Converter.rootClasses());
diff --git a/src/com/google/doclava/FederationTagger.java b/src/com/google/doclava/FederationTagger.java
index f3603a5..a83bb20 100644
--- a/src/com/google/doclava/FederationTagger.java
+++ b/src/com/google/doclava/FederationTagger.java
@@ -29,22 +29,46 @@ import java.util.Map;
* against when overlapping content is discovered.
*/
public final class FederationTagger {
- private final Map<String, URL> federatedUrls = new HashMap<String, URL>();
- private final Map<String, String> federatedXmls = new HashMap<String, String>();
- private final List<FederatedSite> federatedSites = new ArrayList<FederatedSite>();
+ private final Map<String, URL> federatedUrls = new HashMap<>();
+ private final Map<String, String> federatedXmls = new HashMap<>();
+ private final List<FederatedSite> federatedSites = new ArrayList<>();
+
private boolean initialized = false;
+
/**
* Adds a Doclava documentation site for federation. Accepts the base URL of
* the remote API.
+ * <p>
+ * If {@link #addSiteApi(String, String)} is not called, this will default to
+ * reading the API from "/xml/current.xml" within the site's base URL.
+ * <p>
+ * <strong>Note:</strong> Must be called before calling tag() or get() methods.
+ *
+ * @param name internally-used name for federation site
*/
public void addSiteUrl(String name, URL site) {
+ if (initialized) {
+ throw new IllegalStateException("Cannot add sites after calling tag() or get() methods.");
+ }
federatedUrls.put(name, site);
}
-
+
+ /**
+ * Adds an explicit Doclava-generated API file for the specified site.
+ * <p>
+ * <strong>Note:</strong> Must be called before calling tag() or get() methods.
+ *
+ * @param name internally-used name for federation site (must match name used
+ * for {@link #addSiteUrl(String, URL)})
+ * @param file path to a Doclava-generated API file
+ */
public void addSiteApi(String name, String file) {
+ if (initialized) {
+ throw new IllegalStateException("Cannot add sites after calling tag() or get() methods.");
+ }
federatedXmls.put(name, file);
}
-
+
public void tag(ClassInfo classDoc) {
initialize();
for (FederatedSite site : federatedSites) {
@@ -58,19 +82,28 @@ public final class FederationTagger {
applyFederation(site, classDocs);
}
}
-
+
+ /**
+ * Returns a non-{@code null} list of {@link FederatedSite} objects, one for
+ * each unique {@code name} added using {@link #addSiteUrl(String, URL)}.
+ */
+ public List<FederatedSite> getSites() {
+ initialize();
+ return federatedSites;
+ }
+
private void initialize() {
if (initialized) {
return;
}
-
+
for (String name : federatedXmls.keySet()) {
if (!federatedUrls.containsKey(name)) {
Errors.error(Errors.NO_FEDERATION_DATA, (SourcePositionInfo) null,
"Unknown documentation site for " + name);
}
}
-
+
for (String name : federatedUrls.keySet()) {
try {
if (federatedXmls.containsKey(name)) {
@@ -87,10 +120,10 @@ public final class FederationTagger {
Errors.error(Errors.NO_FEDERATION_DATA, (SourcePositionInfo) null, error);
}
}
-
+
initialized = true;
}
-
+
private void applyFederation(FederatedSite federationSource, ClassInfo[] classDocs) {
for (ClassInfo classDoc : classDocs) {
PackageInfo packageSpec
@@ -101,11 +134,11 @@ public final class FederationTagger {
}
ClassInfo classSpec = packageSpec.allClasses().get(classDoc.name());
-
+
if (classSpec == null) {
continue;
}
-
+
federateMethods(federationSource, classSpec, classDoc);
federateConstructors(federationSource, classSpec, classDoc);
federateFields(federationSource, classSpec, classDoc);
@@ -124,7 +157,7 @@ public final class FederationTagger {
}
}
}
-
+
private void federateConstructors(FederatedSite site, ClassInfo federatedClass,
ClassInfo localClass) {
for (MethodInfo constructor : localClass.constructors()) {
@@ -133,7 +166,7 @@ public final class FederationTagger {
}
}
}
-
+
private void federateFields(FederatedSite site, ClassInfo federatedClass, ClassInfo localClass) {
for (FieldInfo field : localClass.fields()) {
if (federatedClass.allFields().containsKey(field.name())) {
@@ -141,12 +174,12 @@ public final class FederationTagger {
}
}
}
-
+
private void federateClass(FederatedSite source, ClassInfo doc) {
doc.addFederatedReference(source);
}
-
+
private void federatePackage(FederatedSite source, PackageInfo pkg) {
pkg.addFederatedReference(source);
}
-} \ No newline at end of file
+}
diff --git a/src/com/google/doclava/TypeInfo.java b/src/com/google/doclava/TypeInfo.java
index 7bba560..7e971c4 100644
--- a/src/com/google/doclava/TypeInfo.java
+++ b/src/com/google/doclava/TypeInfo.java
@@ -24,7 +24,7 @@ public class TypeInfo implements Resolvable {
public static final Set<String> PRIMITIVE_TYPES = Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("boolean", "byte", "char", "double", "float", "int",
"long", "short", "void")));
-
+
public TypeInfo(boolean isPrimitive, String dimension, String simpleTypeName,
String qualifiedTypeName, ClassInfo cl) {
mIsPrimitive = isPrimitive;
@@ -107,7 +107,7 @@ public class TypeInfo implements Resolvable {
typeString = typeString.substring(0, extendsPos);
}
- int pos = typeString.indexOf('[');
+ int pos = typeString.indexOf('[');
if (pos > -1) {
mDimension = typeString.substring(pos);
typeString = typeString.substring(0, pos);
@@ -153,7 +153,17 @@ public class TypeInfo implements Resolvable {
mFullName = other.fullName();
}
+ /**
+ * Returns this type as a {@link ClassInfo} if it represents a class or
+ * interface.
+ */
public ClassInfo asClassInfo() {
+ if (!mResolvedClass) {
+ mResolvedClass = true;
+ if (mClass == null && !mIsPrimitive && !mIsTypeVariable && !mIsWildcard) {
+ mClass = Converter.obtainClass(qualifiedTypeName());
+ }
+ }
return mClass;
}
@@ -544,6 +554,9 @@ public class TypeInfo implements Resolvable {
private ArrayList<Resolution> mResolutions;
+ /** Whether the value of {@code mClass} has been resolved. */
+ private boolean mResolvedClass;
+
private boolean mIsPrimitive;
private boolean mIsTypeVariable;
private boolean mIsWildcard;