From 8e230621955b4c245a458f2202bc5e24160609ef Mon Sep 17 00:00:00 2001 From: Yoshito Umaoka Date: Thu, 22 May 2014 20:05:59 +0000 Subject: [PATCH] ICU-10905 A new tool used for checking deprected tag/annotation consistency. Also added an ant target to run the tool. Some synthetic constructors in abstract class were added (this is our policy) and tighten status tag consistency check. Actual fix for missing @Deprecated annotation will follow. X-SVN-Rev: 35736 --- .gitattributes | 1 + icu4j/build.xml | 18 + .../core/src/com/ibm/icu/text/Normalizer.java | 8 + .../src/com/ibm/icu/text/PluralRules.java | 12 +- .../src/com/ibm/icu/text/TimeZoneNames.java | 10 + .../core/src/com/ibm/icu/text/UnicodeSet.java | 14 +- .../src/com/ibm/icu/util/HebrewCalendar.java | 1 + .../src/com/ibm/icu/text/Transliterator.java | 2 + icu4j/samples/build.properties | 4 +- .../com/ibm/icu/dev/tool/docs/APIData.java | 10 +- .../dev/tool/docs/DeprecatedAPIChecker.java | 470 ++++++++++++++++++ .../ibm/icu/dev/tool/docs/GatherAPIData.java | 55 +- 12 files changed, 585 insertions(+), 20 deletions(-) create mode 100644 icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java diff --git a/.gitattributes b/.gitattributes index bbfca153435..e990c504352 100644 --- a/.gitattributes +++ b/.gitattributes @@ -619,6 +619,7 @@ icu4j/tools/build/icu4j51.api3.gz -text icu4j/tools/build/icu4j52.api3.gz -text icu4j/tools/build/icu4j53.api3.gz -text icu4j/tools/build/manifest.stub -text +icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java -text icu4j/tools/misc/.settings/org.eclipse.core.resources.prefs -text icu4j/tools/misc/manifest.stub -text tools/currency/.classpath -text diff --git a/icu4j/build.xml b/icu4j/build.xml index 54583791cda..37b4ff572ae 100644 --- a/icu4j/build.xml +++ b/icu4j/build.xml @@ -1076,6 +1076,24 @@ + + + + + + + + + + + + + + + + PluralRules for a given locale and the plural type. * @@ -1769,8 +1777,8 @@ public class PluralRules implements Serializable { } /** - * @deprecated This API is ICU internal only. * @internal + * @deprecated This API is ICU internal only. */ @Deprecated public enum StandardPluralCategories { @@ -2263,7 +2271,7 @@ public class PluralRules implements Serializable { * If non null, set to the unique value. * @return the KeywordStatus * @internal - * @provisional This API might change or be removed in a future release. + * @deprecated This API is ICU internal only. */ public KeywordStatus getKeywordStatus(String keyword, int offset, Set explicits, Output uniqueValue, SampleType sampleType) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java b/icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java index 0e7a7083d9c..8832ae9c28e 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java @@ -422,6 +422,7 @@ public abstract class TimeZoneNames implements Serializable { * The super class of TimeZoneNames service factory classes. * * @internal + * @deprecated This API is ICU internal only. */ public static abstract class Factory { /** @@ -431,8 +432,17 @@ public abstract class TimeZoneNames implements Serializable { * The display locale * @return An instance of TimeZoneNames. * @internal + * @deprecated This API is ICU internal only. */ public abstract TimeZoneNames getTimeZoneNames(ULocale locale); + + /** + * Sole constructor + * @internal + * @deprecated This API is ICU internal only. + */ + protected Factory() { + } } /** diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeSet.java b/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeSet.java index bcab647c0aa..6c1f0e050d4 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeSet.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeSet.java @@ -4571,6 +4571,7 @@ public class UnicodeSet extends UnicodeFilter implements Iterable, Compa * Get the default symbol table. Null means ordinary processing. For internal use only. * @return the symbol table * @internal + * @deprecated This API is ICU internal only. */ public static XSymbolTable getDefaultXSymbolTable() { return XSYMBOL_TABLE; @@ -4579,14 +4580,15 @@ public class UnicodeSet extends UnicodeFilter implements Iterable, Compa /** * Set the default symbol table. Null means ordinary processing. For internal use only. Will affect all subsequent parsing * of UnicodeSets. - *

- * WARNING: If this function is used with a UnicodeProperty, and the - * Unassigned characters (gc=Cn) are different than in ICU other than in ICU, you MUST call - * {@code UnicodeProperty.ResetCacheProperties} afterwards. If you then call {@code UnicodeSet.setDefaultXSymbolTable} - * with null to clear the value, you MUST also call {@code UnicodeProperty.ResetCacheProperties}. - * + *

+ * WARNING: If this function is used with a UnicodeProperty, and the + * Unassigned characters (gc=Cn) are different than in ICU other than in ICU, you MUST call + * {@code UnicodeProperty.ResetCacheProperties} afterwards. If you then call {@code UnicodeSet.setDefaultXSymbolTable} + * with null to clear the value, you MUST also call {@code UnicodeProperty.ResetCacheProperties}. + * * @param xSymbolTable the new default symbol table. * @internal + * @deprecated This API is ICU internal only. */ public static void setDefaultXSymbolTable(XSymbolTable xSymbolTable) { XSYMBOL_TABLE = xSymbolTable; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java b/icu4j/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java index 1e5b124edf9..99e514c9604 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java @@ -753,6 +753,7 @@ public class HebrewCalendar extends Calendar { * Overrides {@link Calendar#validateField(int)} to provide * special handling for month validation for Hebrew calendar. * @internal + * @deprecated This API is ICU internal only. */ protected void validateField(int field) { if (field == MONTH && !isLeapYear(handleGetExtendedYear()) && internalGet(MONTH) == ADAR_1) { diff --git a/icu4j/main/classes/translit/src/com/ibm/icu/text/Transliterator.java b/icu4j/main/classes/translit/src/com/ibm/icu/text/Transliterator.java index e76f30f997b..581563ea8d7 100644 --- a/icu4j/main/classes/translit/src/com/ibm/icu/text/Transliterator.java +++ b/icu4j/main/classes/translit/src/com/ibm/icu/text/Transliterator.java @@ -1629,6 +1629,7 @@ public abstract class Transliterator implements StringTransform { * @param targetSet TODO * @see #getTargetSet * @internal + * @deprecated This API is ICU internal only. */ public void addSourceTargetSet(UnicodeSet inputFilter, UnicodeSet sourceSet, UnicodeSet targetSet) { UnicodeSet myFilter = getFilterAsUnicodeSet(inputFilter); @@ -1649,6 +1650,7 @@ public abstract class Transliterator implements StringTransform { * The externalFilter must be frozen (it is frozen if not). * The result may be frozen, so don't attempt to modify. * @internal + * @deprecated This API is ICU internal only. */ // TODO change to getMergedFilter public UnicodeSet getFilterAsUnicodeSet(UnicodeSet externalFilter) { diff --git a/icu4j/samples/build.properties b/icu4j/samples/build.properties index 9657b5c4e68..597f2b37978 100644 --- a/icu4j/samples/build.properties +++ b/icu4j/samples/build.properties @@ -1,5 +1,5 @@ #******************************************************************************* -#* Copyright (C) 2011-2014, International Business Machines Corporation and * +#* Copyright (C) 2011-2014, International Business Machines Corporation and * #* others. All Rights Reserved. * #******************************************************************************* -#shared.dir = ../main/shared +shared.dir = ../main/shared diff --git a/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/APIData.java b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/APIData.java index 7612a91b605..94635094952 100644 --- a/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/APIData.java +++ b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/APIData.java @@ -1,6 +1,6 @@ /** ******************************************************************************* -* Copyright (C) 2004-2013, International Business Machines Corporation and * +* Copyright (C) 2004-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -18,8 +18,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; +import java.util.Set; import java.util.TreeSet; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; @@ -61,7 +63,7 @@ public final class APIData { } } - static APIData read(File file, boolean internal) { + public static APIData read(File file, boolean internal) { String fileName = file.getName(); try { InputStream is; @@ -135,6 +137,10 @@ public final class APIData { pw.println("total apis: " + tt); } + public Set getAPIInfoSet() { + return Collections.unmodifiableSet(set); + } + public static void main(String[] args) { PrintWriter pw = new PrintWriter(System.out); diff --git a/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java new file mode 100644 index 00000000000..567fedbd8b5 --- /dev/null +++ b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java @@ -0,0 +1,470 @@ +/* + ******************************************************************************* + * Copyright (C) 2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +package com.ibm.icu.dev.tool.docs; + +import java.io.File; +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +public class DeprecatedAPIChecker { + + public static void main(String[] args) { + if (args.length != 1) { + System.err.println("Illegal command argument. Specify the API signature file path."); + } + // Load the ICU4J API signature file + Set apiInfoSet = APIData.read(new File(args[0]), true).getAPIInfoSet(); + + DeprecatedAPIChecker checker = new DeprecatedAPIChecker(apiInfoSet, new PrintWriter(System.err, true)); + checker.checkDeprecated(); + System.exit(checker.errCount); + } + + private int errCount = 0; + private Set apiInfoSet; + private PrintWriter pw; + + public DeprecatedAPIChecker(Set apiInfoSet, PrintWriter pw) { + this.apiInfoSet = apiInfoSet; + this.pw = pw; + } + + public int errorCount() { + return errCount; + } + + public void checkDeprecated() { + // Gather API class/enum names and its names that can be + // used for Class.forName() + Map apiClassNameMap = new TreeMap(); + for (APIInfo api : apiInfoSet) { + if (!api.isPublic() && !api.isProtected()) { + continue; + } + if (!api.isClass() && !api.isEnum()) { + continue; + } + String packageName = api.getPackageName(); + String className = api.getName(); + + // Replacing separator for nested class/enum (replacing '.' with + // '$'), so we can use the name for Class.forName(String) + String classNamePath = className.contains(".") ? className.replace('.', '$') : className; + + apiClassNameMap.put(packageName + "." + classNamePath, packageName + "." + className); + } + + // Walk through API classes using reflection + for (Entry classEntry : apiClassNameMap.entrySet()) { + String classNamePath = classEntry.getKey(); + try { + Class cls = Class.forName(classNamePath); + if (cls.isEnum()) { + checkEnum(cls, apiClassNameMap); + } else { + checkClass(cls, apiClassNameMap); + } + } catch (ClassNotFoundException e) { + pw.println("## Error ## Class " + classNamePath + " is not found."); + errCount++; + } + } + } + + private void checkClass(Class cls, Map clsNameMap) { + assert !cls.isEnum(); + + String clsPath = cls.getName(); + String clsName = clsNameMap.get(clsPath); + APIInfo api = null; + + if (clsName != null) { + api = findClassInfo(apiInfoSet, clsName); + } + if (api == null) { + pw.println("## Error ## Class " + clsName + " is not found in the API signature data."); + errCount++; + } + + // check class + compareDeprecated(isAPIDeprecated(api), cls.isAnnotationPresent(Deprecated.class), clsName, null, "Class"); + + // check fields + for (Field f : cls.getDeclaredFields()) { + if (!isPublicOrProtected(f.getModifiers())) { + continue; + } + + String fName = f.getName(); + api = findFieldInfo(apiInfoSet, clsName, fName); + if (api == null) { + pw.println("## Error ## Field " + clsName + "." + fName + " is not found in the API signature data."); + errCount++; + continue; + } + + compareDeprecated(isAPIDeprecated(api), f.isAnnotationPresent(Deprecated.class), clsName, fName, "Field"); + } + + // check constructors + for (Constructor ctor : cls.getDeclaredConstructors()) { + if (!isPublicOrProtected(ctor.getModifiers())) { + continue; + } + + List paramNames = getParamNames(ctor); + api = findConstructorInfo(apiInfoSet, clsName, paramNames); + + if (api == null) { + pw.println("## Error ## Constructor " + clsName + formatParams(paramNames) + + " is not found in the API signature data."); + errCount++; + continue; + } + + compareDeprecated(isAPIDeprecated(api), ctor.isAnnotationPresent(Deprecated.class), clsName, + api.getClassName() + formatParams(paramNames), "Constructor"); + } + + // check methods + for (Method mtd : cls.getDeclaredMethods()) { + // Note: We exclude synthetic method. + if (!isPublicOrProtected(mtd.getModifiers()) || mtd.isSynthetic()) { + continue; + } + + String mtdName = mtd.getName(); + List paramNames = getParamNames(mtd); + api = findMethodInfo(apiInfoSet, clsName, mtdName, paramNames); + + if (api == null) { + pw.println("## Error ## Method " + clsName + "#" + mtdName + formatParams(paramNames) + + " is not found in the API signature data."); + errCount++; + continue; + } + + compareDeprecated(isAPIDeprecated(api), mtd.isAnnotationPresent(Deprecated.class), clsName, mtdName + + formatParams(paramNames), "Method"); + + } + } + + private void checkEnum(Class cls, Map clsNameMap) { + assert cls.isEnum(); + + String enumPath = cls.getName(); + String enumName = clsNameMap.get(enumPath); + APIInfo api = null; + + if (enumName != null) { + api = findEnumInfo(apiInfoSet, enumName); + } + if (api == null) { + pw.println("## Error ## Enum " + enumName + " is not found in the API signature data."); + errCount++; + } + + // check enum + compareDeprecated(isAPIDeprecated(api), cls.isAnnotationPresent(Deprecated.class), enumName, null, "Enum"); + + // check enum constants + for (Field ec : cls.getDeclaredFields()) { + if (!ec.isEnumConstant()) { + continue; + } + String ecName = ec.getName(); + api = findEnumConstantInfo(apiInfoSet, enumName, ecName); + if (api == null) { + pw.println("## Error ## Enum constant " + enumName + "." + ecName + + " is not found in the API signature data."); + errCount++; + continue; + } + + compareDeprecated(isAPIDeprecated(api), ec.isAnnotationPresent(Deprecated.class), enumName, ecName, + "Enum Constant"); + } + } + + private void compareDeprecated(boolean depTag, boolean depAnt, String cls, String name, String type) { + if (depTag != depAnt) { + String apiName = cls; + if (name != null) { + apiName += "." + name; + } + if (depTag) { + pw.println("No @Deprecated annotation: [" + type + "] " + apiName); + } else { + pw.println("No @deprecated JavaDoc tag: [" + type + "] " + apiName); + } + errCount++; + } + } + + private static boolean isPublicOrProtected(int modifier) { + return ((modifier & Modifier.PUBLIC) != 0) || ((modifier & Modifier.PROTECTED) != 0); + } + + private static boolean isAPIDeprecated(APIInfo api) { + return api.isDeprecated() || api.isInternal() || api.isObsolete(); + } + + private static APIInfo findClassInfo(Set apis, String cls) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getName(); + if (api.isClass() && clsName.equals(cls)) { + return api; + } + } + return null; + } + + private static APIInfo findFieldInfo(Set apis, String cls, String field) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getClassName(); + if (api.isField() && clsName.equals(cls) && api.getName().equals(field)) { + return api; + } + } + return null; + } + + private static APIInfo findConstructorInfo(Set apis, String cls, List params) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getClassName(); + if (api.isConstructor() && clsName.equals(cls)) { + // check params + List paramsFromApi = getParamNames(api); + if (paramsFromApi.size() == params.size()) { + boolean match = true; + for (int i = 0; i < params.size(); i++) { + if (!params.get(i).equals(paramsFromApi.get(i))) { + match = false; + break; + } + } + if (match) { + return api; + } + } + } + } + return null; + } + + private static APIInfo findMethodInfo(Set apis, String cls, String method, List params) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getClassName(); + if (api.isMethod() && clsName.equals(cls) && api.getName().equals(method)) { + // check params + List paramsFromApi = getParamNames(api); + if (paramsFromApi.size() == params.size()) { + boolean match = true; + for (int i = 0; i < params.size(); i++) { + if (!params.get(i).equals(paramsFromApi.get(i))) { + match = false; + break; + } + } + if (match) { + return api; + } + } + } + } + return null; + } + + private static APIInfo findEnumInfo(Set apis, String ecls) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getName(); + if (api.isEnum() && clsName.equals(ecls)) { + return api; + } + } + return null; + } + + private static APIInfo findEnumConstantInfo(Set apis, String ecls, String econst) { + for (APIInfo api : apis) { + String clsName = api.getPackageName() + "." + api.getClassName(); + if (api.isEnumConstant() && clsName.equals(ecls) && api.getName().equals(econst)) { + return api; + } + } + return null; + } + + private static List getParamNames(APIInfo api) { + if (!api.isMethod() && !api.isConstructor()) { + throw new IllegalArgumentException(api.toString() + " is not a constructor or a method."); + } + + List nameList = new ArrayList(); + String signature = api.getSignature(); + int start = signature.indexOf('('); + int end = signature.indexOf(')'); + + if (start < 0 || end < 0 || start > end) { + throw new RuntimeException(api.toString() + " has bad API signature: " + signature); + } + + String paramsSegment = signature.substring(start + 1, end); + // erase generic args + if (paramsSegment.indexOf('<') >= 0) { + StringBuilder buf = new StringBuilder(); + boolean inGenericsParams = false; + for (int i = 0; i < paramsSegment.length(); i++) { + char c = paramsSegment.charAt(i); + if (inGenericsParams) { + if (c == '>') { + inGenericsParams = false; + } + } else { + if (c == '<') { + inGenericsParams = true; + } else { + buf.append(c); + } + } + } + paramsSegment = buf.toString(); + } + + if (!paramsSegment.isEmpty()) { + String[] params = paramsSegment.split("\\s*,\\s*"); + for (String p : params) { + if (p.endsWith("...")) { + // varargs to array + p = p.substring(0, p.length() - 3) + "[]"; + } + nameList.add(p); + } + } + + return nameList; + } + + private static List getParamNames(Constructor ctor) { + return toTypeNameList(ctor.getGenericParameterTypes()); + } + + private static List getParamNames(Method method) { + return toTypeNameList(method.getGenericParameterTypes()); + } + + private static final String[] PRIMITIVES = { "byte", "short", "int", "long", "float", "double", "boolean", "char" }; + private static char[] PRIMITIVE_SIGNATURES = { 'B', 'S', 'I', 'J', 'F', 'D', 'Z', 'C' }; + + private static List toTypeNameList(Type[] types) { + List nameList = new ArrayList(); + + for (Type t : types) { + StringBuilder s = new StringBuilder(); + if (t instanceof ParameterizedType) { + // throw away generics parameters + ParameterizedType prdType = (ParameterizedType) t; + Class rawType = (Class) prdType.getRawType(); + s.append(rawType.getCanonicalName()); + } else if (t instanceof WildcardType) { + // we don't need to worry about WildcardType, + // because this tool erases generics parameters + // for comparing method/constructor parameters + throw new RuntimeException("WildcardType not supported by this tool"); + } else if (t instanceof TypeVariable) { + // this tool does not try to resolve actual parameter + // type - for example, " void foo(T in)" + // this tool just use the type variable "T" for API signature + // comparison. This is actually not perfect, but should be + // sufficient for our purpose. + TypeVariable tVar = (TypeVariable) t; + s.append(tVar.getName()); + } else if (t instanceof GenericArrayType) { + // same as TypeVariable. "T[]" is sufficient enough. + GenericArrayType tGenArray = (GenericArrayType) t; + s.append(tGenArray.toString()); + } else if (t instanceof Class) { + Class tClass = (Class) t; + String tName = tClass.getCanonicalName(); + + if (tName.charAt(0) == '[') { + // Array type + int idx = 0; + for (; idx < tName.length(); idx++) { + if (tName.charAt(idx) != '[') { + break; + } + } + int dimension = idx; + char sigChar = tName.charAt(dimension); + + String elemType = null; + if (sigChar == 'L') { + // class + elemType = tName.substring(dimension + 1, tName.length() - 1); + } else { + // primitive + for (int i = 0; i < PRIMITIVE_SIGNATURES.length; i++) { + if (sigChar == PRIMITIVE_SIGNATURES[i]) { + elemType = PRIMITIVES[i]; + break; + } + } + } + + if (elemType == null) { + throw new RuntimeException("Unexpected array type: " + tName); + } + + s.append(elemType); + for (int i = 0; i < dimension; i++) { + s.append("[]"); + } + } else { + s.append(tName); + } + } else { + throw new IllegalArgumentException("Unknown type: " + t); + } + + nameList.add(s.toString()); + } + + return nameList; + } + + private static String formatParams(List paramNames) { + StringBuilder buf = new StringBuilder("("); + boolean isFirst = true; + for (String p : paramNames) { + if (isFirst) { + isFirst = false; + } else { + buf.append(", "); + } + buf.append(p); + } + buf.append(")"); + + return buf.toString(); + } +} diff --git a/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java index cf56f5069c6..ecc1f92b06b 100644 --- a/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java +++ b/icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java @@ -1,6 +1,6 @@ /** ******************************************************************************* - * Copyright (C) 2004-2013, International Business Machines Corporation and * + * Copyright (C) 2004-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -252,12 +252,18 @@ public class GatherAPIData { // care if we didn't properly document the draft status of // default constructors for abstract classes. + // Update: We mandate a no-arg synthetic constructor with explicit + // javadoc comments by the policy. So, we no longer ignore abstract + // class's no-arg constructor blindly. -Yoshito 2014-05-21 + private boolean isAbstractClassDefaultConstructor(ProgramElementDoc doc) { return doc.isConstructor() && doc.containingClass().isAbstract() && "()".equals(((ConstructorDoc) doc).signature()); } + private static final boolean IGNORE_NO_ARG_ABSTRACT_CTOR = false; + private boolean ignore(ProgramElementDoc doc) { if (doc == null) return true; if (doc.isPrivate() || doc.isPackagePrivate()) return true; @@ -268,7 +274,8 @@ public class GatherAPIData { if (isIgnoredEnumMethod(doc)) { return true; } - if (isAbstractClassDefaultConstructor(doc)) { + + if (IGNORE_NO_ARG_ABSTRACT_CTOR && isAbstractClassDefaultConstructor(doc)) { return true; } @@ -445,27 +452,59 @@ public class GatherAPIData { private int tagStatus(final ProgramElementDoc doc, String[] version) { class Result { + boolean deprecatedFlag = false; int res = -1; void set(int val) { if (res != -1) { + boolean isValid = true; if (val == APIInfo.STA_DEPRECATED) { - // ok to have both a 'standard' tag and deprecated - return; - } else if (res != APIInfo.STA_DEPRECATED) { - // if already not deprecated, this is an error + // @internal and @obsolete should be always used along with @deprecated. + // no change for status + isValid = (res == APIInfo.STA_INTERNAL || res == APIInfo.STA_OBSOLETE); + deprecatedFlag = true; + } else if (val == APIInfo.STA_INTERNAL) { + // @deprecated should be always used along with @internal. + // update status + if (res == APIInfo.STA_DEPRECATED) { + res = val; // APIInfo.STA_INTERNAL + } else { + isValid = false; + } + } else if (val == APIInfo.STA_OBSOLETE) { + // @deprecated should be always used along with @obsolete. + // update status + if (res == APIInfo.STA_DEPRECATED) { + res = val; // APIInfo.STA_OBSOLETE + } else { + isValid = false; + } + } else { + // two different status tags must not co-exist, except for + // following two cases: + // 1. @internal and @deprecated + // 2. @obsolete and @deprecated + isValid = false; + } + if (!isValid) { System.err.println("bad doc: " + doc + " both: " + APIInfo.getTypeValName(APIInfo.STA, res) + " and: " + APIInfo.getTypeValName(APIInfo.STA, val)); return; } + } else { + // ok to replace with new tag + res = val; + if (val == APIInfo.STA_DEPRECATED) { + deprecatedFlag = true; + } } - // ok to replace with new tag - res = val; } int get() { if (res == -1) { System.err.println("warning: no tag for " + doc); return 0; + } else if (res == APIInfo.STA_INTERNAL && !deprecatedFlag) { + System.err.println("warning: no @deprecated tag for @internal API: " + doc); } return res; } -- 2.40.0