summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2017-08-01 15:11:17 -0700
committerXin Li <delphij@google.com>2017-08-01 15:13:14 -0700
commite15c52f95e09391629d4046a1ceb296ae29710cd (patch)
tree9f43c9f35e3bacbad225375ccd341c436cd59c04
parent875a77b6e1c6b9bd6d536972c8db385cb4becfe9 (diff)
parent2f6f6abe4d05282c82dd56fa897949f49ac3cd6c (diff)
downloadplatform_external_doclava-e15c52f95e09391629d4046a1ceb296ae29710cd.tar.gz
platform_external_doclava-e15c52f95e09391629d4046a1ceb296ae29710cd.tar.bz2
platform_external_doclava-e15c52f95e09391629d4046a1ceb296ae29710cd.zip
DO NOT MERGE: Merge commit '2f6f6abe4d05282c82dd56fa897949f49ac3cd6c'android-o-mr1-preview-2android-o-mr1-preview-1
from oc-support-26.0-dev to stage-aosp-master. Bug: 64219148 Test: build Change-Id: I3a43d851a95d17df6b33ee416df178fdf494e811
-rw-r--r--res/assets/templates-sdk/macros_override.cs2
-rw-r--r--src/com/google/doclava/AnnotationInstanceInfo.java36
-rw-r--r--src/com/google/doclava/ClassInfo.java17
-rw-r--r--src/com/google/doclava/Converter.java62
-rw-r--r--src/com/google/doclava/Doclava.java28
-rw-r--r--src/com/google/doclava/FederationTagger.java67
-rw-r--r--src/com/google/doclava/MemberInfo.java8
-rw-r--r--src/com/google/doclava/MethodInfo.java26
-rw-r--r--src/com/google/doclava/Stubs.java145
-rw-r--r--src/com/google/doclava/TypeInfo.java17
-rw-r--r--test/doclava/ApiCheckTest.java92
11 files changed, 385 insertions, 115 deletions
diff --git a/res/assets/templates-sdk/macros_override.cs b/res/assets/templates-sdk/macros_override.cs
index 92be480..453eb59 100644
--- a/res/assets/templates-sdk/macros_override.cs
+++ b/res/assets/templates-sdk/macros_override.cs
@@ -106,7 +106,7 @@ def:dump_permission(tag) ?>Requires the <?cs
if subcount(tag.values) > 1 ?> permissions.<?cs
else ?> permission.<?cs
/if ?><?cs
-/def ?>
+/def ?><?cs
# Print output for @service tags ?><?cs
def:dump_service(tag) ?>Instances of this class must be obtained using <?cs
diff --git a/src/com/google/doclava/AnnotationInstanceInfo.java b/src/com/google/doclava/AnnotationInstanceInfo.java
index d353426..07ffd9d 100644
--- a/src/com/google/doclava/AnnotationInstanceInfo.java
+++ b/src/com/google/doclava/AnnotationInstanceInfo.java
@@ -20,6 +20,7 @@ import com.google.clearsilver.jsilver.data.Data;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
public class AnnotationInstanceInfo implements Resolvable {
private ClassInfo mType;
@@ -146,19 +147,40 @@ public class AnnotationInstanceInfo implements Resolvable {
/**
* Get a new list containing the set of annotations that are shared between
- * the input annotations collection and the names of annotations passed in
- * the showAnnotations parameter
+ * the input annotations collection and the set of allowed annotations.
*/
- public static ArrayList<AnnotationInstanceInfo> getShowAnnotationsIntersection(
- ArrayList<AnnotationInstanceInfo> annotations) {
+ public static ArrayList<AnnotationInstanceInfo> getAnnotationsIntersection(
+ Collection<String> allowedAnnotations,
+ Collection<? extends AnnotationInstanceInfo> allAnnotations) {
ArrayList<AnnotationInstanceInfo> list = new ArrayList<AnnotationInstanceInfo>();
- if (annotations != null) {
- for (AnnotationInstanceInfo info : annotations) {
- if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
+ java.util.Objects.requireNonNull(allowedAnnotations);
+ if (allAnnotations != null) {
+ for (AnnotationInstanceInfo info : allAnnotations) {
+ if (allowedAnnotations.contains(info.type().qualifiedName())) {
list.add(info);
}
}
}
return list;
}
+
+ /**
+ * Get a new list containing the set of annotations that are shared between
+ * the input annotations collection and the names of annotations passed in
+ * the showAnnotations parameter
+ */
+ public static ArrayList<AnnotationInstanceInfo> getShowAnnotationsIntersection(
+ Collection<? extends AnnotationInstanceInfo> annotations) {
+ return getAnnotationsIntersection(Doclava.showAnnotations, annotations);
+ }
+
+ /**
+ * Get a new list containing the set of annotations that are shared between
+ * the input annotations collection and the names of annotations passed in
+ * the hideAnnotations parameter
+ */
+ public static ArrayList<AnnotationInstanceInfo> getHideAnnotationsIntersection(
+ Collection<? extends AnnotationInstanceInfo> annotations) {
+ return getAnnotationsIntersection(Doclava.hideAnnotations, annotations);
+ }
}
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index 20addc6..b152508 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -135,6 +135,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
mIsPrimitive = isPrimitive;
mAnnotations = annotations;
mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
+ mHideAnnotations = AnnotationInstanceInfo.getHideAnnotationsIntersection(annotations);
}
public void init(TypeInfo typeInfo, ArrayList<ClassInfo> interfaces,
@@ -167,6 +168,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
mRealSuperclassType = superclassType;
mAnnotations = annotations;
mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
+ mHideAnnotations = AnnotationInstanceInfo.getHideAnnotationsIntersection(annotations);
// after providing new methods and new superclass info,clear any cached
// lists of self + superclass methods, ctors, etc.
@@ -1471,8 +1473,8 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
}
/**
- * @return true if the containing package has @hide comment, or an ancestor
- * class of this class is hidden, or this class has @hide comment.
+ * @return true if the containing package has @hide comment, a hide annotaion,
+ * or a containing class of this class is hidden.
*/
public boolean isHiddenImpl() {
ClassInfo cl = this;
@@ -1484,7 +1486,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
if (pkg != null && pkg.hasHideComment()) {
return true;
}
- if (cl.comment().isHidden()) {
+ if (cl.comment().isHidden() || cl.hasHideAnnotation()) {
return true;
}
cl = cl.containingClass();
@@ -1536,6 +1538,14 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
return mShowAnnotations;
}
+ public boolean hasHideAnnotation() {
+ return mHideAnnotations != null && mHideAnnotations.size() > 0;
+ }
+
+ public ArrayList<AnnotationInstanceInfo> hideAnnotations() {
+ return mHideAnnotations;
+ }
+
public ArrayList<AnnotationInstanceInfo> getShowAnnotationsIncludeOuters() {
ArrayList<AnnotationInstanceInfo> allAnnotations = new ArrayList<AnnotationInstanceInfo>();
ClassInfo cl = this;
@@ -1823,6 +1833,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco
private ClassInfo mSuperclass;
private ArrayList<AnnotationInstanceInfo> mAnnotations;
private ArrayList<AnnotationInstanceInfo> mShowAnnotations;
+ private ArrayList<AnnotationInstanceInfo> mHideAnnotations;
private boolean mSuperclassInit;
private boolean mDeprecatedKnown;
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 6d26d80..15789e5 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -104,6 +104,7 @@ public class Doclava {
public static HashSet<String> knownTags = new HashSet<String>();
public static FederationTagger federationTagger = new FederationTagger();
public static Set<String> showAnnotations = new HashSet<String>();
+ public static Set<String> hideAnnotations = new HashSet<String>();
public static boolean showAnnotationOverridesVisibility = false;
public static Set<String> hiddenPackages = new HashSet<String>();
public static boolean includeAssets = true;
@@ -182,6 +183,8 @@ public class Doclava {
String exactApiFile = null;
String debugStubsFile = "";
HashSet<String> stubPackages = null;
+ HashSet<String> stubImportPackages = null;
+ boolean stubSourceOnly = false;
ArrayList<String> knownTagsFiles = new ArrayList<String>();
root = r;
@@ -251,6 +254,8 @@ public class Doclava {
keepListFile = a[1];
} else if (a[0].equals("-showAnnotation")) {
showAnnotations.add(a[1]);
+ } else if (a[0].equals("-hideAnnotation")) {
+ hideAnnotations.add(a[1]);
} else if (a[0].equals("-showAnnotationOverridesVisibility")) {
showAnnotationOverridesVisibility = true;
} else if (a[0].equals("-hidePackage")) {
@@ -278,6 +283,14 @@ public class Doclava {
for (String pkg : a[1].split(":")) {
stubPackages.add(pkg);
}
+ } else if (a[0].equals("-stubimportpackages")) {
+ stubImportPackages = new HashSet<String>();
+ for (String pkg : a[1].split(":")) {
+ stubImportPackages.add(pkg);
+ hiddenPackages.add(pkg);
+ }
+ } else if (a[0].equals("-stubsourceonly")) {
+ stubSourceOnly = true;
} else if (a[0].equals("-sdkvalues")) {
sdkValuePath = a[1];
} else if (a[0].equals("-api")) {
@@ -412,6 +425,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());
@@ -509,7 +527,9 @@ public class Doclava {
if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null
|| exactApiFile != null) {
Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, exactApiFile,
- stubPackages);
+ stubPackages,
+ stubImportPackages,
+ stubSourceOnly);
}
Errors.printErrors();
@@ -780,6 +800,12 @@ public class Doclava {
if (option.equals("-stubpackages")) {
return 2;
}
+ if (option.equals("-stubimportpackages")) {
+ return 2;
+ }
+ if (option.equals("-stubsourceonly")) {
+ return 1;
+ }
if (option.equals("-sdkvalues")) {
return 2;
}
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/MemberInfo.java b/src/com/google/doclava/MemberInfo.java
index 6c5aad3..8c88648 100644
--- a/src/com/google/doclava/MemberInfo.java
+++ b/src/com/google/doclava/MemberInfo.java
@@ -39,6 +39,7 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped {
mKind = kind;
mAnnotations = annotations;
mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
+ mHideAnnotations = AnnotationInstanceInfo.getHideAnnotationsIntersection(annotations);
}
public abstract boolean isExecutable();
@@ -48,7 +49,7 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped {
if (mShowAnnotations.size() > 0) {
return false;
}
- return super.isHidden();
+ return super.isHidden() || mHideAnnotations.size() > 0;
}
@Override
@@ -177,6 +178,10 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped {
return mShowAnnotations;
}
+ public ArrayList<AnnotationInstanceInfo> hideAnnotations() {
+ return mHideAnnotations;
+ }
+
ClassInfo mContainingClass;
ClassInfo mRealContainingClass;
String mName;
@@ -191,5 +196,6 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped {
String mKind;
private ArrayList<AnnotationInstanceInfo> mAnnotations;
private ArrayList<AnnotationInstanceInfo> mShowAnnotations;
+ private ArrayList<AnnotationInstanceInfo> mHideAnnotations;
}
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index 47b1978..e5761c1 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -28,6 +28,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.function.Predicate;
public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolvable {
public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
@@ -108,7 +109,7 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
if (containingClass().realSuperclass() != null
&& containingClass().realSuperclass().isAbstract()) {
- queue.add(containingClass());
+ queue.add(containingClass().realSuperclass());
}
addInterfaces(containingClass().realInterfaces(), queue);
for (ClassInfo iface : queue) {
@@ -123,17 +124,13 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
return null;
}
- public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
+ public MethodInfo findPredicateOverriddenMethod(Predicate<MethodInfo> predicate) {
if (mReturnType == null) {
// ctor
return null;
}
if (mOverriddenMethod != null) {
- // Even if we're told outright that this was the overridden method, we want to
- // be conservative and ignore mismatches of parameter types -- they arise from
- // extending generic specializations, and we want to consider the derived-class
- // method to be a non-override.
- if (this.signature().equals(mOverriddenMethod.signature())) {
+ if (predicate.test(mOverriddenMethod)) {
return mOverriddenMethod;
}
}
@@ -141,13 +138,12 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
if (containingClass().realSuperclass() != null
&& containingClass().realSuperclass().isAbstract()) {
- queue.add(containingClass());
+ queue.add(containingClass().realSuperclass());
}
addInterfaces(containingClass().realInterfaces(), queue);
for (ClassInfo iface : queue) {
for (MethodInfo me : iface.methods()) {
- if (me.name().equals(this.name()) && me.signature().equals(this.signature())
- && notStrippable.contains(me.containingClass())) {
+ if (predicate.test(me)) {
return me;
}
}
@@ -167,7 +163,7 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
if (containingClass().realSuperclass() != null
&& containingClass().realSuperclass().isAbstract()) {
- queue.add(containingClass());
+ queue.add(containingClass().realSuperclass());
}
addInterfaces(containingClass().realInterfaces(), queue);
for (ClassInfo iface : queue) {
@@ -893,6 +889,8 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
+ " to " + mInfo.scope());
}
+ // Changing the deprecated annotation is binary- and source-compatible, but
+ // we still need to log the API change.
if (!isDeprecated() == mInfo.isDeprecated()) {
Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method "
+ mInfo.prettyQualifiedSignature() + " has changed deprecation state " + isDeprecated()
@@ -900,16 +898,14 @@ public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolv
consistent = false;
}
- // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
- // "compatibility with existing binaries."
- /*
+ // Changing the synchronized modifier is binary- and source-compatible (see
+ // JLS 3 13.4.20), but we still need to log the API change.
if (mIsSynchronized != mInfo.mIsSynchronized) {
Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
+ " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
+ mInfo.mIsSynchronized);
consistent = false;
}
- */
for (ClassInfo exception : thrownExceptions()) {
if (!mInfo.throwsException(exception)) {
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index fbcff97..59ca505 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -18,6 +18,7 @@ package com.google.doclava;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -27,6 +28,8 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -43,7 +46,9 @@ import java.util.stream.Collectors;
public class Stubs {
public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
- String removedApiFile, String exactApiFile, HashSet<String> stubPackages) {
+ String removedApiFile, String exactApiFile, HashSet<String> stubPackages,
+ HashSet<String> stubImportPackages,
+ boolean stubSourceOnly) {
// figure out which classes we need
final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
ClassInfo[] all = Converter.allClasses();
@@ -94,11 +99,11 @@ public class Stubs {
"Cannot open file for write");
}
}
- // If a class is public or protected, not hidden, and marked as included,
+ // If a class is public or protected, not hidden, not imported and marked as included,
// then we can't strip it
for (ClassInfo cl : all) {
if (cl.checkLevel() && cl.isIncluded()) {
- cantStripThis(cl, notStrippable, "0:0");
+ cantStripThis(cl, notStrippable, "0:0", stubImportPackages);
}
}
@@ -117,7 +122,7 @@ public class Stubs {
+ m.name() + " is deprecated");
}
- ClassInfo hiddenClass = findHiddenClasses(m.returnType());
+ ClassInfo hiddenClass = findHiddenClasses(m.returnType(), stubImportPackages);
if (null != hiddenClass) {
if (hiddenClass.qualifiedName() == m.returnType().asClassInfo().qualifiedName()) {
// Return type is hidden
@@ -134,7 +139,7 @@ public class Stubs {
for (ParameterInfo p : m.parameters()) {
TypeInfo t = p.type();
if (!t.isPrimitive()) {
- hiddenClass = findHiddenClasses(t);
+ hiddenClass = findHiddenClasses(t, stubImportPackages);
if (null != hiddenClass) {
if (hiddenClass.qualifiedName() == t.asClassInfo().qualifiedName()) {
// Parameter type is hidden
@@ -187,6 +192,9 @@ public class Stubs {
final HashSet<Pattern> stubPackageWildcards = extractWildcards(stubPackages);
for (ClassInfo cl : notStrippable) {
if (!cl.isDocOnly()) {
+ if (stubSourceOnly && !Files.exists(Paths.get(cl.position().file))) {
+ continue;
+ }
if (shouldWriteStub(cl.containingPackage().name(), stubPackages, stubPackageWildcards)) {
// write out the stubs
if (stubsDir != null) {
@@ -282,15 +290,34 @@ public class Stubs {
return wildcards;
}
- private static ClassInfo findHiddenClasses(TypeInfo ti) {
+ /**
+ * Find references to hidden classes.
+ *
+ * <p>This finds hidden classes that are used by public parts of the API in order to ensure the
+ * API is self consistent and does not reference classes that are not included in
+ * the stubs. Any such references cause an error to be reported.
+ *
+ * <p>A reference to an imported class is not treated as an error, even though imported classes
+ * are hidden from the stub generation. That is because imported classes are, by definition,
+ * excluded from the set of classes for which stubs are required.
+ *
+ * @param ti the type information to examine for references to hidden classes.
+ * @param stubImportPackages the possibly null set of imported package names.
+ * @return a reference to a hidden class or null if there are none
+ */
+ private static ClassInfo findHiddenClasses(TypeInfo ti, HashSet<String> stubImportPackages) {
ClassInfo ci = ti.asClassInfo();
if (ci == null) return null;
+ if (stubImportPackages != null
+ && stubImportPackages.contains(ci.containingPackage().qualifiedName())) {
+ return null;
+ }
if (ci.isHiddenOrRemoved()) return ci;
if (ti.typeArguments() != null) {
for (TypeInfo tii : ti.typeArguments()) {
// Avoid infinite recursion in the case of Foo<T extends Foo>
if (tii.qualifiedTypeName() != ti.qualifiedTypeName()) {
- ClassInfo hiddenClass = findHiddenClasses(tii);
+ ClassInfo hiddenClass = findHiddenClasses(tii, stubImportPackages);
if (hiddenClass != null) return hiddenClass;
}
}
@@ -298,7 +325,14 @@ public class Stubs {
return null;
}
- public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
+ public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why,
+ HashSet<String> stubImportPackages) {
+
+ if (stubImportPackages != null
+ && stubImportPackages.contains(cl.containingPackage().qualifiedName())) {
+ // if the package is imported then it does not need stubbing.
+ return;
+ }
if (!notStrippable.add(cl)) {
// slight optimization: if it already contains cl, it already contains
@@ -318,12 +352,14 @@ public class Stubs {
for (FieldInfo fInfo : cl.selfFields()) {
if (fInfo.type() != null) {
if (fInfo.type().asClassInfo() != null) {
- cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName());
+ cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName(),
+ stubImportPackages);
}
if (fInfo.type().typeArguments() != null) {
for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) {
if (tTypeInfo.asClassInfo() != null) {
- cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName());
+ cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName(),
+ stubImportPackages);
}
}
}
@@ -335,7 +371,8 @@ public class Stubs {
if (cl.asTypeInfo().typeArguments() != null) {
for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) {
if (tInfo.asClassInfo() != null) {
- cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName());
+ cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName(),
+ stubImportPackages);
}
}
}
@@ -343,11 +380,12 @@ public class Stubs {
// cant strip any of the annotation elements
// cantStripThis(cl.annotationElements(), notStrippable);
// take care of methods
- cantStripThis(cl.allSelfMethods(), notStrippable);
- cantStripThis(cl.allConstructors(), notStrippable);
+ cantStripThis(cl.allSelfMethods(), notStrippable, stubImportPackages);
+ cantStripThis(cl.allConstructors(), notStrippable, stubImportPackages);
// blow the outer class open if this is an inner class
if (cl.containingClass() != null) {
- cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName());
+ cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName(),
+ stubImportPackages);
}
// blow open super class and interfaces
ClassInfo supr = cl.realSuperclass();
@@ -365,7 +403,8 @@ public class Stubs {
Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName()
+ " stripped of unavailable superclass " + supr.qualifiedName());
} else {
- cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName());
+ cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName(),
+ stubImportPackages);
if (supr.isPrivate()) {
Errors.error(Errors.PRIVATE_SUPERCLASS, cl.position(), "Public class "
+ cl.qualifiedName() + " extends private class " + supr.qualifiedName());
@@ -374,7 +413,8 @@ public class Stubs {
}
}
- private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable) {
+ private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable,
+ HashSet<String> stubImportPackages) {
// for each method, blow open the parameters, throws and return types. also blow open their
// generics
if (mInfos != null) {
@@ -383,7 +423,8 @@ public class Stubs {
for (TypeInfo tInfo : mInfo.getTypeParameters()) {
if (tInfo.asClassInfo() != null) {
cantStripThis(tInfo.asClassInfo(), notStrippable, "8:"
- + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
+ + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
+ stubImportPackages);
}
}
}
@@ -391,7 +432,8 @@ public class Stubs {
for (ParameterInfo pInfo : mInfo.parameters()) {
if (pInfo.type() != null && pInfo.type().asClassInfo() != null) {
cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:"
- + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
+ + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
+ stubImportPackages);
if (pInfo.type().typeArguments() != null) {
for (TypeInfo tInfoType : pInfo.type().typeArguments()) {
if (tInfoType.asClassInfo() != null) {
@@ -404,7 +446,8 @@ public class Stubs {
+ "()");
} else {
cantStripThis(tcl, notStrippable, "10:"
- + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
+ + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
+ stubImportPackages);
}
}
}
@@ -414,16 +457,18 @@ public class Stubs {
}
for (ClassInfo thrown : mInfo.thrownExceptions()) {
cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName()
- + ":" + mInfo.name());
+ + ":" + mInfo.name(), stubImportPackages);
}
if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) {
cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:"
- + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
+ + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
+ stubImportPackages);
if (mInfo.returnType().typeArguments() != null) {
for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) {
if (tyInfo.asClassInfo() != null) {
cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:"
- + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
+ + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name(),
+ stubImportPackages);
}
}
}
@@ -811,40 +856,32 @@ public class Stubs {
|| !field.type().dimension().equals("") || field.containingClass().isInterface();
}
- // Returns 'true' if the method is an @Override of a visible parent
- // method implementation, and thus does not affect the API.
+ /**
+ * Test if the given method has a concrete implementation in a superclass or
+ * interface that has no differences in its public API representation.
+ *
+ * @return {@code true} if the tested method can be safely elided from the
+ * public API to conserve space.
+ */
static boolean methodIsOverride(HashSet<ClassInfo> notStrippable, MethodInfo mi) {
// Abstract/static/final methods are always listed in the API description
if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
return false;
}
- // Find any relevant ancestor declaration and inspect it
- MethodInfo om = mi;
- do {
- MethodInfo superMethod = om.findSuperclassImplementation(notStrippable);
- if (om.equals(superMethod)) {
- break;
- }
- om = superMethod;
- } while (om != null && (om.isHiddenOrRemoved() || om.containingClass().isHiddenOrRemoved()));
- if (om != null) {
- // Visibility mismatch is an API change, so check for it
- if (mi.mIsPrivate == om.mIsPrivate && mi.mIsPublic == om.mIsPublic
- && mi.mIsProtected == om.mIsProtected) {
- // Look only for overrides of an ancestor class implementation,
- // not of e.g. an abstract or interface method declaration
- if (!om.isAbstract()) {
- // If the only "override" turns out to be in our own class
- // (which sometimes happens in concrete subclasses of
- // abstract base classes), it's not really an override
- if (!mi.mContainingClass.equals(om.mContainingClass)) {
- return true;
- }
+ final String api = writeMethodApiWithoutDefault(mi);
+ final MethodInfo overridden = mi.findPredicateOverriddenMethod(new Predicate<MethodInfo>() {
+ @Override
+ public boolean test(MethodInfo test) {
+ if (test.isHiddenOrRemoved() || test.containingClass().isHiddenOrRemoved()) {
+ return false;
}
+
+ final String testApi = writeMethodApiWithoutDefault(test);
+ return api.equals(testApi);
}
- }
- return false;
+ });
+ return (overridden != null);
}
static boolean canCallMethod(ClassInfo from, MethodInfo m) {
@@ -1573,10 +1610,20 @@ public class Stubs {
apiWriter.print(";\n");
}
+ static String writeMethodApiWithoutDefault(MethodInfo mi) {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeMethodApi(new PrintStream(out), mi, false);
+ return out.toString();
+ }
+
static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
+ writeMethodApi(apiWriter, mi, true);
+ }
+
+ static void writeMethodApi(PrintStream apiWriter, MethodInfo mi, boolean withDefault) {
apiWriter.print(" method ");
apiWriter.print(mi.scope());
- if (mi.isDefault()) {
+ if (mi.isDefault() && withDefault) {
apiWriter.print(" default");
}
if (mi.isStatic()) {
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;
diff --git a/test/doclava/ApiCheckTest.java b/test/doclava/ApiCheckTest.java
index d9f1a07..ce0464a 100644
--- a/test/doclava/ApiCheckTest.java
+++ b/test/doclava/ApiCheckTest.java
@@ -17,26 +17,31 @@
package doclava;
import com.google.doclava.Errors;
-import com.google.doclava.Errors.Error;
import com.google.doclava.Errors.ErrorMessage;
import com.google.doclava.apicheck.ApiCheck;
import com.google.doclava.apicheck.ApiCheck.Report;
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
import java.util.Iterator;
-public class ApiCheckTest extends TestCase {
- /**
- * Clear all errors and make sure all future errors will be recorded.
- */
+public class ApiCheckTest {
+
+ @Before
public void setUp() {
+ // Clear all errors and make sure all future errors will be recorded.
Errors.clearErrors();
for (Errors.Error error : Errors.sErrors) {
Errors.setErrorLevel(error.code, Errors.ERROR);
}
}
+ @Test
public void testEquivalentApi() {
String[] args = { "test/api/medium.xml", "test/api/medium.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -44,6 +49,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(report.errors().size(), 0);
}
+ @Test
public void testMethodReturnTypeChanged() {
String[] args = { "test/api/return-type-changed-1.xml", "test/api/return-type-changed-2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -52,6 +58,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_TYPE, report.errors().iterator().next().error());
}
+ @Test
public void testMethodParameterChanged() {
String[] args = { "test/api/parameter-changed-1.xml", "test/api/parameter-changed-2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -66,6 +73,7 @@ public class ApiCheckTest extends TestCase {
assertTrue(m2.error().equals(Errors.ADDED_METHOD) || m2.error().equals(Errors.REMOVED_METHOD));
}
+ @Test
public void testConstructorParameterChanged() {
String[] args = { "test/api/parameter-changed-1.xml", "test/api/parameter-changed-3.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -79,6 +87,7 @@ public class ApiCheckTest extends TestCase {
assertTrue(m2.error().equals(Errors.ADDED_METHOD) || m2.error().equals(Errors.REMOVED_METHOD));
}
+ @Test
public void testAddedClass() {
String[] args = { "test/api/simple.xml", "test/api/added-class.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -87,6 +96,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_CLASS, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedClass() {
String[] args = { "test/api/added-class.xml", "test/api/simple.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -95,6 +105,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_CLASS, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedDeprecatedClass() {
String[] args = { "test/api/added-deprecated-class.xml", "test/api/simple.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -103,6 +114,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_DEPRECATED_CLASS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedSuper() {
String[] args = { "test/api/simple.xml", "test/api/changed-super.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -111,13 +123,19 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SUPERCLASS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedAssignableReturn() {
- String[] args = { "test/api/changed-assignable-return-1.xml", "test/api/changed-assignable-return-2.xml" };
+ String[] args = {
+ "test/api/changed-assignable-return-1.xml",
+ "test/api/changed-assignable-return-2.xml"
+ };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
- assertEquals(0, report.errors().size());
+ assertEquals(1, report.errors().size());
+ assertEquals(Errors.CHANGED_TYPE, report.errors().iterator().next().error());
}
+ @Test
public void testInsertedSuper() {
String[] args = { "test/api/inserted-super-1.xml", "test/api/inserted-super-2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -125,6 +143,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(0, report.errors().size());
}
+ @Test
public void testAddedInterface() {
String[] args = { "test/api/removed-interface.xml", "test/api/medium.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -133,6 +152,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_INTERFACE, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedInterface() {
String[] args = { "test/api/medium.xml", "test/api/removed-interface.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -141,6 +161,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_INTERFACE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedAbstractClass() {
String[] args = { "test/api/medium.xml", "test/api/changed-abstract.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -149,6 +170,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_ABSTRACT, report.errors().iterator().next().error());
}
+ @Test
public void testChangedAbstractClass2() {
String[] args = { "test/api/changed-abstract.xml", "test/api/medium.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -157,6 +179,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_ABSTRACT, report.errors().iterator().next().error());
}
+ @Test
public void testChangedAbstractMethod() {
String[] args = { "test/api/medium.xml", "test/api/changed-abstract2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -165,6 +188,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_ABSTRACT, report.errors().iterator().next().error());
}
+ @Test
public void testChangedAbstractMethod2() {
String[] args = { "test/api/changed-abstract2.xml", "test/api/medium.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -173,6 +197,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_ABSTRACT, report.errors().iterator().next().error());
}
+ @Test
public void testAddedPackage() {
String[] args = { "test/api/medium.xml", "test/api/added-package.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -181,6 +206,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_PACKAGE, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedPackage() {
String[] args = { "test/api/added-package.xml", "test/api/medium.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -189,6 +215,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_PACKAGE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedValue() {
String[] args = { "test/api/constants.xml", "test/api/changed-value.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -197,6 +224,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_VALUE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedValue2() {
String[] args = { "test/api/constants.xml", "test/api/changed-value2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -205,6 +233,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_VALUE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedType() {
String[] args = { "test/api/constants.xml", "test/api/changed-type.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -213,7 +242,8 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_TYPE, report.errors().iterator().next().error());
}
- public void testChangedFinalField() {
+ @Test
+ public void testAddedFinalField() {
String[] args = { "test/api/constants.xml", "test/api/changed-final.xml" };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
@@ -221,7 +251,8 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_FINAL, report.errors().iterator().next().error());
}
- public void testChangedFinalMethod() {
+ @Test
+ public void testAddedFinalMethod() {
String[] args = { "test/api/constants.xml", "test/api/changed-final2.xml" };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
@@ -229,22 +260,27 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_FINAL, report.errors().iterator().next().error());
}
- public void testChangedFinalClass() {
+ @Test
+ public void testAddedFinalClass() {
String[] args = { "test/api/constants.xml", "test/api/changed-final3.xml" };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
- assertEquals(1, report.errors().size());
+ // One error for the class, one for the constructor, one for the method.
+ assertEquals(3, report.errors().size());
assertEquals(Errors.ADDED_FINAL, report.errors().iterator().next().error());
}
- public void testChangedFinalClass2() {
+ @Test
+ public void testRemovedFinalClass() {
String[] args = { "test/api/changed-final3.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
- assertEquals(1, report.errors().size());
+ // One error for the class, one for the constructor, one for the method.
+ assertEquals(3, report.errors().size());
assertEquals(Errors.REMOVED_FINAL, report.errors().iterator().next().error());
}
+ @Test
public void testAddedField() {
String[] args = { "test/api/constants.xml", "test/api/added-field.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -253,6 +289,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_FIELD, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedField() {
String[] args = { "test/api/added-field.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -261,6 +298,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_FIELD, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedDeprecatedField() {
String[] args = { "test/api/added-deprecated-field.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -269,6 +307,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_DEPRECATED_FIELD, report.errors().iterator().next().error());
}
+ @Test
public void testAddedMethod() {
String[] args = { "test/api/constants.xml", "test/api/added-method.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -277,6 +316,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.ADDED_METHOD, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedMethod() {
String[] args = { "test/api/added-method.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -285,6 +325,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_METHOD, report.errors().iterator().next().error());
}
+ @Test
public void testRemovedDeprecatedMethod() {
String[] args = { "test/api/added-deprecated-method.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -293,6 +334,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.REMOVED_DEPRECATED_METHOD, report.errors().iterator().next().error());
}
+ @Test
public void testChangedStaticMethod() {
String[] args = { "test/api/constants.xml", "test/api/changed-static.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -301,6 +343,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_STATIC, report.errors().iterator().next().error());
}
+ @Test
public void testChangedStaticClass() {
String[] args = { "test/api/constants.xml", "test/api/changed-static2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -309,6 +352,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_STATIC, report.errors().iterator().next().error());
}
+ @Test
public void testChangedStaticField() {
String[] args = { "test/api/constants.xml", "test/api/changed-static3.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -317,6 +361,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_STATIC, report.errors().iterator().next().error());
}
+ @Test
public void testChangedTransient() {
String[] args = { "test/api/constants.xml", "test/api/changed-transient.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -325,13 +370,15 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_TRANSIENT, report.errors().iterator().next().error());
}
+ @Test
public void testChangedSynchronized() {
String[] args = { "test/api/constants.xml", "test/api/changed-synchronized.xml" };
ApiCheck apiCheck = new ApiCheck();
Report report = apiCheck.checkApi(args);
- assertEquals(0, report.errors().size());
+ assertEquals(1, report.errors().size());
}
+ @Test
public void testChangedVolatile() {
String[] args = { "test/api/constants.xml", "test/api/changed-volatile.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -340,6 +387,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_VOLATILE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedNative() {
String[] args = { "test/api/constants.xml", "test/api/changed-native.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -348,6 +396,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_NATIVE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedScopeMethod() {
String[] args = { "test/api/constants.xml", "test/api/changed-scope.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -356,6 +405,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SCOPE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedScopeClass() {
String[] args = { "test/api/changed-scope.xml", "test/api/constants.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -364,6 +414,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SCOPE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedScopeClass2() {
String[] args = { "test/api/constants.xml", "test/api/changed-scope2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -372,6 +423,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SCOPE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedScopeField() {
String[] args = { "test/api/constants.xml", "test/api/changed-scope3.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -380,6 +432,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SCOPE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedConstructorScope() {
String[] args = { "test/api/constants.xml", "test/api/changed-scope4.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -388,6 +441,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_SCOPE, report.errors().iterator().next().error());
}
+ @Test
public void testChangedMethodThrows() {
String[] args = { "test/api/throws.xml", "test/api/removed-exception.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -396,6 +450,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_THROWS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedMethodThrows2() {
String[] args = { "test/api/removed-exception.xml", "test/api/throws.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -404,6 +459,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_THROWS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedConstructorThrows() {
String[] args = { "test/api/throws.xml", "test/api/added-exception.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -412,6 +468,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_THROWS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedConstructorThrows2() {
String[] args = { "test/api/added-exception.xml", "test/api/throws.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -420,6 +477,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_THROWS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedMethodDeprecated() {
String[] args = { "test/api/constants.xml", "test/api/changed-deprecated.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -428,6 +486,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_DEPRECATED, report.errors().iterator().next().error());
}
+ @Test
public void testChangedConstructorDeprecated() {
String[] args = { "test/api/constants.xml", "test/api/changed-deprecated2.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -436,6 +495,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_DEPRECATED, report.errors().iterator().next().error());
}
+ @Test
public void testChangedFieldDeprecated() {
String[] args = { "test/api/constants.xml", "test/api/changed-deprecated3.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -444,6 +504,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_DEPRECATED, report.errors().iterator().next().error());
}
+ @Test
public void testChangedClassToInterface() {
String[] args = { "test/api/changed-class-info2.xml", "test/api/changed-class-info.xml" };
ApiCheck apiCheck = new ApiCheck();
@@ -452,6 +513,7 @@ public class ApiCheckTest extends TestCase {
assertEquals(Errors.CHANGED_CLASS, report.errors().iterator().next().error());
}
+ @Test
public void testChangedInterfaceToClass() {
String[] args = { "test/api/changed-class-info.xml", "test/api/changed-class-info2.xml" };
ApiCheck apiCheck = new ApiCheck();