diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/google/doclava/ArtifactTagger.java | 163 | ||||
-rw-r--r-- | src/com/google/doclava/ClassInfo.java | 5 | ||||
-rw-r--r-- | src/com/google/doclava/DocInfo.java | 21 | ||||
-rw-r--r-- | src/com/google/doclava/Doclava.java | 10 | ||||
-rw-r--r-- | src/com/google/doclava/Errors.java | 2 | ||||
-rw-r--r-- | src/com/google/doclava/MemberInfo.java | 8 | ||||
-rw-r--r-- | src/com/google/doclava/NavTree.java | 10 | ||||
-rw-r--r-- | src/com/google/doclava/Stubs.java | 12 |
8 files changed, 215 insertions, 16 deletions
diff --git a/src/com/google/doclava/ArtifactTagger.java b/src/com/google/doclava/ArtifactTagger.java new file mode 100644 index 0000000..3c5ee06 --- /dev/null +++ b/src/com/google/doclava/ArtifactTagger.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.doclava; + +import com.google.clearsilver.jsilver.data.Data; +import com.google.doclava.apicheck.ApiCheck; +import com.google.doclava.apicheck.ApiInfo; +import com.google.doclava.apicheck.ApiParseException; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Applies version information to the Doclava class model from apicheck XML files. + * <p> + * Sample usage: + * <pre> + * ClassInfo[] classInfos = ... + * + * ArtifactTagger artifactTagger = new ArtifactTagger() + * artifactTagger.addArtifact("frameworks/support/core-ui/api/current.xml", + * "com.android.support:support-core-ui:26.0.0") + * artifactTagger.addArtifact("frameworks/support/design/api/current.xml", + * "com.android.support:support-design:26.0.0") + * artifactTagger.tagAll(...); + * </pre> + */ +public class ArtifactTagger { + + private final Map<String, String> xmlToArtifact = new LinkedHashMap<>(); + + /** + * Specifies the apicheck XML file associated with an artifact. + * <p> + * This method should only be called once per artifact. + * + * @param file an apicheck XML file + * @param mavenSpec the Maven spec for the artifact to which the XML file belongs + */ + public void addArtifact(String file, String mavenSpec) { + xmlToArtifact.put(file, mavenSpec); + } + + /** + * Tags the specified docs with artifact information. + * + * @param classDocs the docs to tag + */ + public void tagAll(ClassInfo[] classDocs) { + // Read through the XML files in order, applying their artifact information + // to the Javadoc models. + for (Map.Entry<String, String> artifactSpec : xmlToArtifact.entrySet()) { + String xmlFile = artifactSpec.getKey(); + String artifactName = artifactSpec.getValue(); + + ApiInfo specApi; + try { + specApi = new ApiCheck().parseApi(xmlFile); + } catch (ApiParseException e) { + StringWriter stackTraceWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stackTraceWriter)); + Errors.error(Errors.BROKEN_ARTIFACT_FILE, (SourcePositionInfo) null, + "Failed to parse " + xmlFile + " for " + artifactName + " artifact data.\n" + + stackTraceWriter.toString()); + continue; + } + + applyArtifactsFromSpec(artifactName, specApi, classDocs); + } + + if (!xmlToArtifact.isEmpty()) { + warnForMissingArtifacts(classDocs); + } + } + + /** + * Returns {@code true} if any artifact mappings are specified. + */ + public boolean hasArtifacts() { + return !xmlToArtifact.isEmpty(); + } + + /** + * Writes an index of the artifact names to {@code data}. + */ + public void writeArtifactNames(Data data) { + int index = 1; + for (String artifact : xmlToArtifact.values()) { + data.setValue("artifact." + index + ".name", artifact); + index++; + } + } + + /** + * Applies artifact information to {@code classDocs} where not already present. + * + * @param mavenSpec the Maven spec for the artifact + * @param specApi the spec for this artifact + * @param classDocs the docs to update + */ + private void applyArtifactsFromSpec(String mavenSpec, ApiInfo specApi, ClassInfo[] classDocs) { + for (ClassInfo classDoc : classDocs) { + PackageInfo packageSpec = specApi.getPackages().get(classDoc.containingPackage().name()); + if (packageSpec != null) { + ClassInfo classSpec = packageSpec.allClasses().get(classDoc.name()); + if (classSpec != null) { + if (classDoc.getArtifact() == null) { + classDoc.setArtifact(mavenSpec); + } else { + Errors.error(Errors.BROKEN_ARTIFACT_FILE, (SourcePositionInfo) null, "Class " + + classDoc.name() + " belongs to multiple artifacts: " + classDoc.getArtifact() + + " and " + mavenSpec); + } + } + } + } + } + + /** + * Warns if any symbols are missing artifact information. When configured properly, this will + * yield zero warnings because {@code apicheck} guarantees that all symbols are present in the + * most recent API. + * + * @param classDocs the docs to verify + */ + private void warnForMissingArtifacts(ClassInfo[] classDocs) { + for (ClassInfo claz : classDocs) { + if (checkLevelRecursive(claz) && claz.getArtifact() == null) { + Errors.error(Errors.NO_ARTIFACT_DATA, claz.position(), "XML missing class " + + claz.qualifiedName()); + } + } + } + + /** + * Returns true if {@code claz} and all containing classes are documented. The result may be used + * to filter out members that exist in the API data structure but aren't a part of the API. + */ + private boolean checkLevelRecursive(ClassInfo claz) { + for (ClassInfo c = claz; c != null; c = c.containingClass()) { + if (!c.checkLevel()) { + return false; + } + } + return true; + } +} diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java index b152508..3a4090c 100644 --- a/src/com/google/doclava/ClassInfo.java +++ b/src/com/google/doclava/ClassInfo.java @@ -1123,6 +1123,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco if (isDeprecated()) { data.setValue(base + ".deprecatedsince", getDeprecatedSince()); } + data.setValue(base + ".artifact", getArtifact()); ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters(); AnnotationInstanceInfo.makeLinkListHDF( @@ -1193,6 +1194,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco if (isDeprecated()) { data.setValue("class.deprecatedsince", getDeprecatedSince()); } + data.setValue("class.artifact", getArtifact()); setFederatedReferences(data, "class"); // the containing package -- note that this can be passed to type_link, @@ -1510,9 +1512,6 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco public boolean isRemovedImpl() { ClassInfo cl = this; while (cl != null) { - if (cl.hasShowAnnotation()) { - return false; - } PackageInfo pkg = cl.containingPackage(); if (pkg != null && pkg.hasRemovedComment()) { return true; diff --git a/src/com/google/doclava/DocInfo.java b/src/com/google/doclava/DocInfo.java index d8a1961..650ce0d 100644 --- a/src/com/google/doclava/DocInfo.java +++ b/src/com/google/doclava/DocInfo.java @@ -104,6 +104,26 @@ public abstract class DocInfo { return mSince; } + /** + * Sets the artifact in which the class resides. + * <p> + * This property should be specified as a full Maven dependency spec. For example, a Support + * Library core utility class may use "com.android.support:support-core-utils:26.0.1". + * + * @param artifact the artifact in which the class resides + * @return + */ + public void setArtifact(String artifact) { + mArtifact = artifact; + } + + /** + * Returns the artifact in which the class resides. + */ + public String getArtifact() { + return mArtifact; + } + public void setDeprecatedSince(String since) { mDeprecatedSince = since; } @@ -137,6 +157,7 @@ public abstract class DocInfo { Comment mComment; SourcePositionInfo mPosition; private String mSince; + private String mArtifact; private String mDeprecatedSince; private Set<FederatedSite> mFederatedReferences = new LinkedHashSet<FederatedSite>(); } diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java index 0327114..e1c92f1 100644 --- a/src/com/google/doclava/Doclava.java +++ b/src/com/google/doclava/Doclava.java @@ -101,6 +101,7 @@ public class Doclava { public static Map<Character, String> escapeChars = new HashMap<Character, String>(); public static String title = ""; public static SinceTagger sinceTagger = new SinceTagger(); + public static ArtifactTagger artifactTagger = new ArtifactTagger(); public static HashSet<String> knownTags = new HashSet<String>(); public static FederationTagger federationTagger = new FederationTagger(); public static Set<String> showAnnotations = new HashSet<String>(); @@ -309,6 +310,8 @@ public class Doclava { parseComments = true; } else if (a[0].equals("-since")) { sinceTagger.addVersion(a[1], a[2]); + } else if (a[0].equals("-artifact")) { + artifactTagger.addArtifact(a[1], a[2]); } else if (a[0].equals("-offlinemode")) { offlineMode = true; } else if (a[0].equals("-metadataDebug")) { @@ -433,6 +436,9 @@ public class Doclava { // Apply @since tags from the XML file sinceTagger.tagAll(Converter.rootClasses()); + // Apply @artifact tags from the XML file + artifactTagger.tagAll(Converter.rootClasses()); + // Apply details of federated documentation federationTagger.tagAll(Converter.rootClasses()); @@ -833,6 +839,9 @@ public class Doclava { if (option.equals("-since")) { return 3; } + if (option.equals("-artifact")) { + return 3; + } if (option.equals("-offlinemode")) { return 1; } @@ -979,6 +988,7 @@ public class Doclava { } data.setValue("reference", "1"); data.setValue("reference.apilevels", sinceTagger.hasVersions() ? "1" : "0"); + data.setValue("reference.artifacts", artifactTagger.hasArtifacts() ? "1" : "0"); data.setValue("docs.packages." + i + ".name", s); data.setValue("docs.packages." + i + ".link", pkg.htmlPage()); data.setValue("docs.packages." + i + ".since", pkg.getSince()); diff --git a/src/com/google/doclava/Errors.java b/src/com/google/doclava/Errors.java index 48202a3..9edd239 100644 --- a/src/com/google/doclava/Errors.java +++ b/src/com/google/doclava/Errors.java @@ -311,6 +311,8 @@ public class Errors { public static final Error BROADCAST_BEHAVIOR = new Error(126, LINT); public static final Error SDK_CONSTANT = new Error(127, LINT); public static final Error TODO = new Error(128, LINT); + public static final Error NO_ARTIFACT_DATA = new Error(129, HIDDEN); + public static final Error BROKEN_ARTIFACT_FILE = new Error(130, ERROR); public static boolean setErrorLevel(int code, int level) { for (Error e : sErrors) { diff --git a/src/com/google/doclava/MemberInfo.java b/src/com/google/doclava/MemberInfo.java index 8c88648..8e7863e 100644 --- a/src/com/google/doclava/MemberInfo.java +++ b/src/com/google/doclava/MemberInfo.java @@ -53,14 +53,6 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped { } @Override - public boolean isRemoved() { - if (mShowAnnotations.size() > 0) { - return false; - } - return super.isRemoved(); - } - - @Override public boolean isHiddenOrRemoved() { return isHidden() || isRemoved(); } diff --git a/src/com/google/doclava/NavTree.java b/src/com/google/doclava/NavTree.java index 03926b2..5d055db 100644 --- a/src/com/google/doclava/NavTree.java +++ b/src/com/google/doclava/NavTree.java @@ -165,7 +165,7 @@ public class NavTree { for (ClassInfo cl : classes) { if (cl.checkLevel()) { - children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince())); + children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince(), cl.getArtifact())); } } @@ -179,12 +179,18 @@ public class NavTree { private String mLink; List<Node> mChildren; private String mSince; + private String mArtifact; Node(String label, String link, List<Node> children, String since) { + this(label, link, children, since, null); + } + + Node(String label, String link, List<Node> children, String since, String artifact) { mLabel = label; mLink = link; mChildren = children; mSince = since; + mArtifact = artifact; } static void renderString(StringBuilder buf, String s) { @@ -243,6 +249,8 @@ public class NavTree { renderChildren(buf); buf.append(", "); renderString(buf, mSince); + buf.append(", "); + renderString(buf, mArtifact); buf.append(" ]"); } } diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java index 479b5ae..4dc859d 100644 --- a/src/com/google/doclava/Stubs.java +++ b/src/com/google/doclava/Stubs.java @@ -1230,14 +1230,16 @@ public class Stubs { ClassInfo clazz = member.containingClass(); boolean visible = member.isPublic() || member.isProtected(); + boolean hidden = member.isHidden(); boolean removed = member.isRemoved(); while (clazz != null) { visible &= clazz.isPublic() || clazz.isProtected(); + hidden |= clazz.isHidden(); removed |= clazz.isRemoved(); clazz = clazz.containingClass(); } - if (visible && removed) { + if (visible && !hidden && removed) { if (member instanceof MethodInfo) { final MethodInfo method = (MethodInfo) member; return (method.findOverriddenMethod(method.name(), method.signature()) == null); @@ -1257,15 +1259,17 @@ public class Stubs { boolean visible = member.isPublic() || member.isProtected(); boolean hasShowAnnotation = member.hasShowAnnotation(); - boolean hiddenOrRemoved = member.isHiddenOrRemoved(); + boolean hidden = member.isHidden(); + boolean removed = member.isRemoved(); while (clazz != null) { visible &= clazz.isPublic() || clazz.isProtected(); hasShowAnnotation |= clazz.hasShowAnnotation(); - hiddenOrRemoved |= clazz.isHiddenOrRemoved(); + hidden |= clazz.isHidden(); + removed |= clazz.isRemoved(); clazz = clazz.containingClass(); } - if (visible && hasShowAnnotation && !hiddenOrRemoved) { + if (visible && hasShowAnnotation && !hidden && !removed) { if (member instanceof MethodInfo) { final MethodInfo method = (MethodInfo) member; return (method.findOverriddenMethod(method.name(), method.signature()) == null); |