am 54004373: Fix handling of double-byte chars for tags, keywords.
* commit '5400437364666d0a7d3bfd6ea721fe15e813d657':
Fix handling of double-byte chars for tags, keywords.
diff --git a/build.gradle b/build.gradle
index 79d856e..50c36d4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,7 @@
apply plugin: 'java'
+import com.android.internal.BuildUtils
+
sourceSets {
main {
java {
@@ -10,20 +12,9 @@
}
}
}
-// TODO put this function in a plugin
-String findToolsJar() {
- new ByteArrayOutputStream().withStream { os ->
- project.exec {
- executable "$rootDir/build/core/find-jdk-tools-jar.sh"
-
- standardOutput = os
- }
- return os.toString().trim()
- }
-}
dependencies {
- compile files(findToolsJar())
+ compile files(BuildUtils.findToolsJar(project))
compile project(path: ':antlr', configuration: 'antlrRuntime')
compile project(':jsilver')
compile project(':tagsoup')
diff --git a/src/com/google/doclava/AttrTagInfo.java b/src/com/google/doclava/AttrTagInfo.java
index 909cacf..04e5626 100644
--- a/src/com/google/doclava/AttrTagInfo.java
+++ b/src/com/google/doclava/AttrTagInfo.java
@@ -22,6 +22,12 @@
import java.util.regex.Matcher;
public class AttrTagInfo extends TagInfo {
+ public static final AttrTagInfo[] EMPTY_ARRAY = new AttrTagInfo[0];
+
+ public static AttrTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new AttrTagInfo[size];
+ }
+
private static final String REF_COMMAND = "ref";
private static final String NAME_COMMAND = "name";
private static final String DESCRIPTION_COMMAND = "description";
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index a5ee7d4..616cf23 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -2082,7 +2082,7 @@
}
if (mi == null) {
Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public method "
- + mInfo.qualifiedName());
+ + mInfo.prettyQualifiedSignature());
consistent = false;
}
}
@@ -2096,7 +2096,7 @@
MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this);
if (mi == null) {
Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method "
- + mInfo.qualifiedName());
+ + mInfo.prettyQualifiedSignature());
consistent = false;
}
}
@@ -2109,14 +2109,14 @@
}
} else {
Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public constructor "
- + mInfo.prettySignature());
+ + mInfo.prettyQualifiedSignature());
consistent = false;
}
}
for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) {
if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor "
- + mInfo.prettySignature());
+ + mInfo.prettyQualifiedSignature());
consistent = false;
}
}
@@ -2201,7 +2201,7 @@
if (!isDeprecated() == cl.isDeprecated()) {
consistent = false;
Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
- + " has changed deprecation state");
+ + " has changed deprecation state " + isDeprecated() + " --> " + cl.isDeprecated());
}
if (superclassName() != null) { // java.lang.Object can't have a superclass.
diff --git a/src/com/google/doclava/CodeTagInfo.java b/src/com/google/doclava/CodeTagInfo.java
index 1a4a864..56ffd13 100644
--- a/src/com/google/doclava/CodeTagInfo.java
+++ b/src/com/google/doclava/CodeTagInfo.java
@@ -17,6 +17,12 @@
package com.google.doclava;
public class CodeTagInfo extends TagInfo {
+ public static final CodeTagInfo[] EMPTY_ARRAY = new CodeTagInfo[0];
+
+ public static CodeTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new CodeTagInfo[size];
+ }
+
private static String encode(String t) {
t = t.replace("&", "&");
t = t.replace("<", "<");
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index cfb7aaf..33dee3c 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -19,12 +19,15 @@
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
public class Comment {
static final Pattern FIRST_SENTENCE =
Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
- private static final String[] KNOWN_TAGS = new String[] {
+ private static final Set<String> KNOWN_TAGS = new HashSet<String>(Arrays.asList(new String[] {
"@author",
"@since",
"@version",
@@ -38,7 +41,7 @@
"@sample",
"@include",
"@serial",
- };
+ }));
public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
mText = text;
@@ -296,7 +299,14 @@
}
private boolean isWhitespaceChar(char c) {
- return c == ' ' || c == '\r' || c == '\t' || c == '\n';
+ switch (c) {
+ case ' ':
+ case '\r':
+ case '\t':
+ case '\n':
+ return true;
+ }
+ return false;
}
private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) {
@@ -346,13 +356,7 @@
} else if (name.equals("@include") || name.equals("@sample")) {
mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
} else {
- boolean known = false;
- for (String s : KNOWN_TAGS) {
- if (s.equals(name)) {
- known = true;
- break;
- }
- }
+ boolean known = KNOWN_TAGS.contains(name);
if (!known) {
known = Doclava.knownTags.contains(name);
}
@@ -416,7 +420,7 @@
results.add(t);
}
}
- return results.toArray(new TagInfo[results.size()]);
+ return results.toArray(TagInfo.getArray(results.size()));
}
public ParamTagInfo[] paramTags() {
@@ -516,18 +520,17 @@
mText = null;
mInitialized = true;
- mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
- mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
- mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
- mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
- mReturnTags =
- ParsedTagInfo.joinTags(mReturnTagsList.toArray(new ParsedTagInfo[mReturnTagsList.size()]));
- mDeprecatedTags =
- ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(new ParsedTagInfo[mDeprecatedTagsList
- .size()]));
- mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
- mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
- mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
+ mInlineTags = mInlineTagsList.toArray(TagInfo.getArray(mInlineTagsList.size()));
+ mParamTags = mParamTagsList.toArray(ParamTagInfo.getArray(mParamTagsList.size()));
+ mSeeTags = mSeeTagsList.toArray(SeeTagInfo.getArray(mSeeTagsList.size()));
+ mThrowsTags = mThrowsTagsList.toArray(ThrowsTagInfo.getArray(mThrowsTagsList.size()));
+ mReturnTags = ParsedTagInfo.joinTags(
+ mReturnTagsList.toArray(ParsedTagInfo.getArray(mReturnTagsList.size())));
+ mDeprecatedTags = ParsedTagInfo.joinTags(
+ mDeprecatedTagsList.toArray(ParsedTagInfo.getArray(mDeprecatedTagsList.size())));
+ mUndeprecateTags = mUndeprecateTagsList.toArray(TagInfo.getArray(mUndeprecateTagsList.size()));
+ mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
+ mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
mParamTagsList = null;
mSeeTagsList = null;
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index e620bf3..3153b41 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -107,12 +107,14 @@
return (ClassInfo[]) mClasses.all();
}
+ private static final MethodDoc[] EMPTY_METHOD_DOC = new MethodDoc[0];
+
private static void initClass(ClassDoc c, ClassInfo cl) {
MethodDoc[] annotationElements;
if (c instanceof AnnotationTypeDoc) {
annotationElements = ((AnnotationTypeDoc) c).elements();
} else {
- annotationElements = new MethodDoc[0];
+ annotationElements = EMPTY_METHOD_DOC;
}
cl.init(Converter.obtainType(c),
new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.interfaces()))),
@@ -192,7 +194,7 @@
public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base) {
int len = tags.length;
- TagInfo[] out = new TagInfo[len];
+ TagInfo[] out = TagInfo.getArray(len);
for (int i = 0; i < len; i++) {
Tag t = tags[i];
/*
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 94331ee..e2f7d88 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -718,8 +718,9 @@
}
int i = 0;
- for (String s : sorted.keySet()) {
- PackageInfo pkg = sorted.get(s);
+ for (Map.Entry<String, PackageInfo> entry : sorted.entrySet()) {
+ String s = entry.getKey();
+ PackageInfo pkg = entry.getValue();
if (pkg.isHiddenOrRemoved()) {
continue;
diff --git a/src/com/google/doclava/FieldInfo.java b/src/com/google/doclava/FieldInfo.java
index ce80e9e..83d748a 100644
--- a/src/com/google/doclava/FieldInfo.java
+++ b/src/com/google/doclava/FieldInfo.java
@@ -423,7 +423,7 @@
boolean consistent = true;
if (!mType.equals(fInfo.mType)) {
Errors.error(Errors.CHANGED_TYPE, fInfo.position(), "Field " + fInfo.qualifiedName()
- + " has changed type");
+ + " has changed type from " + mType + " to " + fInfo.mType);
consistent = false;
} else if (!this.valueEquals(fInfo)) {
Errors.error(Errors.CHANGED_VALUE, fInfo.position(), "Field " + fInfo.qualifiedName()
@@ -467,7 +467,7 @@
if (isDeprecated() != fInfo.isDeprecated()) {
Errors.error(Errors.CHANGED_DEPRECATED, fInfo.position(), "Field " + fInfo.qualifiedName()
- + " has changed deprecation state");
+ + " has changed deprecation state " + isDeprecated() + " --> " + fInfo.isDeprecated());
consistent = false;
}
diff --git a/src/com/google/doclava/LiteralTagInfo.java b/src/com/google/doclava/LiteralTagInfo.java
index 1feb276..e6b9115 100644
--- a/src/com/google/doclava/LiteralTagInfo.java
+++ b/src/com/google/doclava/LiteralTagInfo.java
@@ -17,6 +17,12 @@
package com.google.doclava;
public class LiteralTagInfo extends TagInfo {
+ public static final LiteralTagInfo[] EMPTY_ARRAY = new LiteralTagInfo[0];
+
+ public static LiteralTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new LiteralTagInfo[size];
+ }
+
private static String encode(String t) {
t = t.replace("&", "&");
t = t.replace("<", "<");
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index f1659f3..5dc217c 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -333,6 +333,10 @@
public String prettySignature() {
return name() + prettyParameters();
}
+
+ public String prettyQualifiedSignature() {
+ return qualifiedName() + prettyParameters();
+ }
/**
* Returns a printable version of the parameters of this method's signature.
@@ -405,7 +409,8 @@
containingClass(), position()));
}
}
- mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
+
+ mThrowsTags = rv.toArray(ThrowsTagInfo.getArray(rv.size()));
}
return mThrowsTags;
}
@@ -424,6 +429,12 @@
if (mParamTags == null) {
final int N = mParameters.size();
+ if (N == 0) {
+ // Early out for empty case.
+ mParamTags = ParamTagInfo.EMPTY_ARRAY;
+ return ParamTagInfo.EMPTY_ARRAY;
+ }
+
String[] names = new String[N];
String[] comments = new String[N];
SourcePositionInfo[] positions = new SourcePositionInfo[N];
@@ -464,7 +475,7 @@
}
// construct the results, and cache them for next time
- mParamTags = new ParamTagInfo[N];
+ mParamTags = ParamTagInfo.getArray(N);
for (i = 0; i < N; i++) {
mParamTags[i] =
new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
@@ -721,6 +732,8 @@
public String qualifiedName() {
String parentQName = (containingClass() != null)
? (containingClass().qualifiedName() + ".") : "";
+ // TODO: This logic doesn't work well with constructors, as name() for constructors already
+ // contains the containingClass's name, leading to things like A.B.B() being rendered as A.B.A.B()
return parentQName + name();
}
@@ -769,21 +782,22 @@
}
if (!consistent) {
- Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed return type from " + mReturnType + " to " + mInfo.mReturnType);
+ Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has changed return type from " + mReturnType
+ + " to " + mInfo.mReturnType);
}
}
if (mIsAbstract != mInfo.mIsAbstract) {
consistent = false;
- Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed 'abstract' qualifier");
+ Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has changed 'abstract' qualifier");
}
if (mIsNative != mInfo.mIsNative) {
consistent = false;
- Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed 'native' qualifier");
+ Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has changed 'native' qualifier");
}
if (!mIsStatic) {
@@ -793,30 +807,32 @@
// and (b) the method is not already inferred to be 'final' by virtue of its class.
if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) {
consistent = false;
- Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has added 'final' qualifier");
+ Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has added 'final' qualifier");
} else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) {
consistent = false;
- Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has removed 'final' qualifier");
+ Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has removed 'final' qualifier");
}
}
if (mIsStatic != mInfo.mIsStatic) {
consistent = false;
- Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed 'static' qualifier");
+ Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has changed 'static' qualifier");
}
if (!scope().equals(mInfo.scope())) {
consistent = false;
- Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " changed scope from " + scope() + " to " + mInfo.scope());
+ Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " changed scope from " + scope()
+ + " to " + mInfo.scope());
}
if (!isDeprecated() == mInfo.isDeprecated()) {
- Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " has changed deprecation state " + isDeprecated() + " --> " + mInfo.isDeprecated());
+ Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " has changed deprecation state " + isDeprecated()
+ + " --> " + mInfo.isDeprecated());
consistent = false;
}
@@ -835,8 +851,9 @@
if (!mInfo.throwsException(exception)) {
// exclude 'throws' changes to finalize() overrides with no arguments
if (!name().equals("finalize") || (!mParameters.isEmpty())) {
- Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " no longer throws exception " + exception.qualifiedName());
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " no longer throws exception "
+ + exception.qualifiedName());
consistent = false;
}
}
@@ -846,8 +863,9 @@
// exclude 'throws' changes to finalize() overrides with no arguments
if (!throwsException(exec)) {
if (!name().equals("finalize") || (!mParameters.isEmpty())) {
- Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
- + " added thrown exception " + exec.qualifiedName());
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
+ + mInfo.prettyQualifiedSignature() + " added thrown exception "
+ + exec.qualifiedName());
consistent = false;
}
}
diff --git a/src/com/google/doclava/PackageInfo.java b/src/com/google/doclava/PackageInfo.java
index 02beaf7..c0f10da 100644
--- a/src/com/google/doclava/PackageInfo.java
+++ b/src/com/google/doclava/PackageInfo.java
@@ -394,9 +394,15 @@
return mClasses;
}
- public boolean isConsistent(PackageInfo pInfo) {
+ public boolean isConsistent(PackageInfo pInfo, Collection<String> ignoredClasses) {
boolean consistent = true;
for (ClassInfo cInfo : mClasses.values()) {
+ // TODO: Add support for matching inner classes (e.g, something like
+ // example.Type.* should match example.Type.InnerType)
+ if (ignoredClasses != null && ignoredClasses.contains(cInfo.qualifiedName())) {
+ // TODO: Log skipping this?
+ continue;
+ }
if (pInfo.mClasses.containsKey(cInfo.name())) {
if (!cInfo.isConsistent(pInfo.mClasses.get(cInfo.name()))) {
consistent = false;
@@ -408,6 +414,10 @@
}
}
for (ClassInfo cInfo : pInfo.mClasses.values()) {
+ if (ignoredClasses != null && ignoredClasses.contains(cInfo.qualifiedName())) {
+ // TODO: Log skipping this?
+ continue;
+ }
if (!mClasses.containsKey(cInfo.name())) {
Errors.error(Errors.ADDED_CLASS, cInfo.position(), "Added class " + cInfo.name()
+ " to package " + pInfo.name());
@@ -416,4 +426,8 @@
}
return consistent;
}
+
+ public boolean isConsistent(PackageInfo pInfo) {
+ return isConsistent(pInfo, null);
+ }
}
diff --git a/src/com/google/doclava/ParamTagInfo.java b/src/com/google/doclava/ParamTagInfo.java
index f8329bc..13eb30b 100644
--- a/src/com/google/doclava/ParamTagInfo.java
+++ b/src/com/google/doclava/ParamTagInfo.java
@@ -22,6 +22,12 @@
import java.util.regex.Matcher;
public class ParamTagInfo extends ParsedTagInfo {
+ public static final ParamTagInfo[] EMPTY_ARRAY = new ParamTagInfo[0];
+
+ public static ParamTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new ParamTagInfo[size];
+ }
+
static final Pattern PATTERN = Pattern.compile("([^ \t\r\n]+)[ \t\r\n]+(.*)", Pattern.DOTALL);
private boolean mIsTypeParameter;
diff --git a/src/com/google/doclava/ParsedTagInfo.java b/src/com/google/doclava/ParsedTagInfo.java
index bcb9230..aad3767 100755
--- a/src/com/google/doclava/ParsedTagInfo.java
+++ b/src/com/google/doclava/ParsedTagInfo.java
@@ -19,6 +19,12 @@
import java.util.ArrayList;
public class ParsedTagInfo extends TagInfo {
+ public static final ParsedTagInfo[] EMPTY_ARRAY = new ParsedTagInfo[0];
+
+ public static ParsedTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new ParsedTagInfo[size];
+ }
+
private ContainerInfo mContainer;
private String mCommentText;
private Comment mComment;
@@ -50,6 +56,6 @@
list.add(t[j]);
}
}
- return list.toArray(new TagInfo[list.size()]);
+ return list.toArray(TagInfo.getArray(list.size()));
}
}
diff --git a/src/com/google/doclava/SampleTagInfo.java b/src/com/google/doclava/SampleTagInfo.java
index a69a3f3..66eb9ac 100644
--- a/src/com/google/doclava/SampleTagInfo.java
+++ b/src/com/google/doclava/SampleTagInfo.java
@@ -44,6 +44,12 @@
* samples/ApiDemos/src/com/google/app/Notification1.java Bleh}
*/
public class SampleTagInfo extends TagInfo {
+ public static final SampleTagInfo[] EMPTY_ARRAY = new SampleTagInfo[0];
+
+ public static SampleTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new SampleTagInfo[size];
+ }
+
static final int STATE_BEGIN = 0;
static final int STATE_MATCHING = 1;
diff --git a/src/com/google/doclava/SeeTagInfo.java b/src/com/google/doclava/SeeTagInfo.java
index 34e6aed..9f38655 100644
--- a/src/com/google/doclava/SeeTagInfo.java
+++ b/src/com/google/doclava/SeeTagInfo.java
@@ -19,6 +19,12 @@
import com.google.clearsilver.jsilver.data.Data;
public class SeeTagInfo extends TagInfo {
+ public static final SeeTagInfo[] EMPTY_ARRAY = new SeeTagInfo[0];
+
+ public static SeeTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new SeeTagInfo[size];
+ }
+
private ContainerInfo mBase;
LinkReference mLink;
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index bc5e586..6157fa9 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -27,8 +27,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.regex.Pattern;
public class Stubs {
public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
@@ -161,9 +163,10 @@
// packages contains all the notStrippable classes mapped by their containing packages
HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
+ final HashSet<Pattern> stubPackageWildcards = extractWildcards(stubPackages);
for (ClassInfo cl : notStrippable) {
if (!cl.isDocOnly()) {
- if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) {
+ if (shouldWriteStub(cl.containingPackage().name(), stubPackages, stubPackageWildcards)) {
// write out the stubs
if (stubsDir != null) {
writeClassFile(stubsDir, notStrippable, cl);
@@ -211,6 +214,46 @@
}
}
+ private static boolean shouldWriteStub(final String packageName,
+ final HashSet<String> stubPackages, final HashSet<Pattern> stubPackageWildcards) {
+ if (stubPackages == null) {
+ // There aren't any stub packages set, write all stubs
+ return true;
+ }
+ if (stubPackages.contains(packageName)) {
+ // Stub packages contains package, return true
+ return true;
+ }
+ if (stubPackageWildcards != null) {
+ // Else, we will iterate through the wildcards to see if there's a match
+ for (Pattern wildcard : stubPackageWildcards) {
+ if (wildcard.matcher(packageName).matches()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static HashSet<Pattern> extractWildcards(HashSet<String> stubPackages) {
+ HashSet<Pattern> wildcards = null;
+ if (stubPackages != null) {
+ for (Iterator<String> i = stubPackages.iterator(); i.hasNext();) {
+ final String pkg = i.next();
+ if (pkg.indexOf('*') != -1) {
+ if (wildcards == null) {
+ wildcards = new HashSet<Pattern>();
+ }
+ // Add the compiled wildcard, replacing * with the regex equivalent
+ wildcards.add(Pattern.compile(pkg.replace("*", ".*?")));
+ // And remove the raw wildcard from the packages
+ i.remove();
+ }
+ }
+ }
+ return wildcards;
+ }
+
private static ClassInfo findHiddenClasses(TypeInfo ti) {
ClassInfo ci = ti.asClassInfo();
if (ci == null) return null;
@@ -1057,7 +1100,7 @@
returnString = returnString.replaceAll("<", "<");
returnString = returnString.replaceAll(">", ">");
returnString = returnString.replaceAll("\"", """);
- returnString = returnString.replaceAll("'", "&pos;");
+ returnString = returnString.replaceAll("'", "'");
return returnString;
}
diff --git a/src/com/google/doclava/TagInfo.java b/src/com/google/doclava/TagInfo.java
index ec1f811..7bb640e 100644
--- a/src/com/google/doclava/TagInfo.java
+++ b/src/com/google/doclava/TagInfo.java
@@ -19,6 +19,12 @@
import com.google.clearsilver.jsilver.data.Data;
public class TagInfo {
+ public static final TagInfo[] EMPTY_ARRAY = new TagInfo[0];
+
+ public static TagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new TagInfo[size];
+ }
+
private String mName;
private String mText;
private String mKind;
diff --git a/src/com/google/doclava/TextTagInfo.java b/src/com/google/doclava/TextTagInfo.java
index 35a486b..7403883 100644
--- a/src/com/google/doclava/TextTagInfo.java
+++ b/src/com/google/doclava/TextTagInfo.java
@@ -17,6 +17,12 @@
package com.google.doclava;
public class TextTagInfo extends TagInfo {
+ public static final TextTagInfo[] EMPTY_ARRAY = new TextTagInfo[0];
+
+ public static TextTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new TextTagInfo[size];
+ }
+
TextTagInfo(String n, String k, String t, SourcePositionInfo p) {
super(n, k, Doclava.escape(t), p);
}
diff --git a/src/com/google/doclava/ThrowsTagInfo.java b/src/com/google/doclava/ThrowsTagInfo.java
index 5f49485..9a9e72f 100644
--- a/src/com/google/doclava/ThrowsTagInfo.java
+++ b/src/com/google/doclava/ThrowsTagInfo.java
@@ -22,7 +22,14 @@
import java.util.regex.Matcher;
public class ThrowsTagInfo extends ParsedTagInfo {
+ public static final ThrowsTagInfo[] EMPTY_ARRAY = new ThrowsTagInfo[0];
+
+ public static ThrowsTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new ThrowsTagInfo[size];
+ }
+
static final Pattern PATTERN = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL);
+
private ClassInfo mException;
public ThrowsTagInfo(String name, String kind, String text, ContainerInfo base,
diff --git a/src/com/google/doclava/apicheck/ApiCheck.java b/src/com/google/doclava/apicheck/ApiCheck.java
index 28d7ce0..9698c89 100644
--- a/src/com/google/doclava/apicheck/ApiCheck.java
+++ b/src/com/google/doclava/apicheck/ApiCheck.java
@@ -23,6 +23,7 @@
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
@@ -39,7 +40,8 @@
for (i = 0; i < allArgs.size(); i++) {
// flags with one value attached
String flag = allArgs.get(i);
- if (flag.equals("-error") || flag.equals("-warning") || flag.equals("-hide")) {
+ if (flag.equals("-error") || flag.equals("-warning") || flag.equals("-hide")
+ || flag.equals("-ignoreClass") || flag.equals("-ignorePackage")) {
String[] arg = new String[2];
arg[0] = flag;
arg[1] = allArgs.get(++i);
@@ -81,6 +83,11 @@
args.add(a);
}
+ // Not having having any classes or packages ignored is the common case.
+ // Avoid a hashCode call in a common loop by not passing in a HashSet in this case.
+ Set<String> ignoredPackages = null;
+ Set<String> ignoredClasses = null;
+
ArrayList<String[]> flags = ApiCheck.parseFlags(args);
for (String[] a : flags) {
if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
@@ -98,6 +105,16 @@
System.err.println("Bad argument: " + a[0] + " " + a[1]);
return new Report(2, Errors.getErrors());
}
+ } else if (a[0].equals("-ignoreClass")) {
+ if (ignoredClasses == null) {
+ ignoredClasses = new HashSet<String>();
+ }
+ ignoredClasses.add(a[1]);
+ } else if (a[0].equals("-ignorePackage")) {
+ if (ignoredPackages == null) {
+ ignoredPackages = new HashSet<String>();
+ }
+ ignoredPackages.add(a[1]);
}
}
@@ -121,11 +138,11 @@
// only run the consistency check if we haven't had XML parse errors
if (!Errors.hadError) {
- oldApi.isConsistent(newApi);
+ oldApi.isConsistent(newApi, ignoredPackages, ignoredClasses);
}
if (!Errors.hadError) {
- oldRemovedApi.isConsistent(newRemovedApi);
+ oldRemovedApi.isConsistent(newRemovedApi, ignoredPackages, ignoredClasses);
}
return new Report(Errors.hadError ? 1 : 0, Errors.getErrors());
diff --git a/src/com/google/doclava/apicheck/ApiInfo.java b/src/com/google/doclava/apicheck/ApiInfo.java
index 711a9f4..2752f3a 100644
--- a/src/com/google/doclava/apicheck/ApiInfo.java
+++ b/src/com/google/doclava/apicheck/ApiInfo.java
@@ -20,6 +20,7 @@
import com.google.doclava.Errors;
import com.google.doclava.PackageInfo;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -58,12 +59,25 @@
/**
* Checks to see if this api is consistent with a newer version.
+ *
+ * @param otherApi the other api to test consistency against
+ * @param ignoredPackages packages to skip consistency checks (will match by exact name)
+ * @param ignoredClasses classes to skip consistency checks (will match by exact fully qualified
+ * name)
*/
- public boolean isConsistent(ApiInfo otherApi) {
+ public boolean isConsistent(ApiInfo otherApi,
+ Collection<String> ignoredPackages, Collection<String> ignoredClasses) {
boolean consistent = true;
for (PackageInfo pInfo : mPackages.values()) {
+ // TODO: Add support for matching subpackages (e.g, something like
+ // test.example.* should match test.example.subpackage, and
+ // test.example.** should match the above AND test.example.subpackage.more)
+ if (ignoredPackages != null && ignoredPackages.contains(pInfo.name())) {
+ // TODO: Log skipping this?
+ continue;
+ }
if (otherApi.getPackages().containsKey(pInfo.name())) {
- if (!pInfo.isConsistent(otherApi.getPackages().get(pInfo.name()))) {
+ if (!pInfo.isConsistent(otherApi.getPackages().get(pInfo.name()), ignoredClasses)) {
consistent = false;
}
} else {
@@ -72,6 +86,10 @@
}
}
for (PackageInfo pInfo : otherApi.mPackages.values()) {
+ if (ignoredPackages != null && ignoredPackages.contains(pInfo.name())) {
+ // TODO: Log skipping this?
+ continue;
+ }
if (!mPackages.containsKey(pInfo.name())) {
Errors.error(Errors.ADDED_PACKAGE, pInfo.position(), "Added package " + pInfo.name());
consistent = false;
@@ -80,6 +98,13 @@
return consistent;
}
+ /**
+ * Checks to see if this api is consistent with a newer version.
+ */
+ public boolean isConsistent(ApiInfo otherApi) {
+ return isConsistent(otherApi, null, null);
+ }
+
public HashMap<String, PackageInfo> getPackages() {
return mPackages;
}