]> granicus.if.org Git - icu/commitdiff
ICU-10905 A new tool used for checking deprected tag/annotation consistency. Also...
authorYoshito Umaoka <y.umaoka@gmail.com>
Thu, 22 May 2014 20:05:59 +0000 (20:05 +0000)
committerYoshito Umaoka <y.umaoka@gmail.com>
Thu, 22 May 2014 20:05:59 +0000 (20:05 +0000)
X-SVN-Rev: 35736

12 files changed:
.gitattributes
icu4j/build.xml
icu4j/main/classes/core/src/com/ibm/icu/text/Normalizer.java
icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java
icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeSet.java
icu4j/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java
icu4j/main/classes/translit/src/com/ibm/icu/text/Transliterator.java
icu4j/samples/build.properties
icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/APIData.java
icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/DeprecatedAPIChecker.java [new file with mode: 0644]
icu4j/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java

index bbfca153435b21bb74ae4f0c272c7aae25105201..e990c504352d06c9499548f7ea8dc6526634463c 100644 (file)
@@ -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
index 54583791cda18d35d63c9438a3ae2893d49e330d..37b4ff572ae4d253472097d94d336c99c5ce9430 100644 (file)
         </java>
     </target>
 
+    <target name="checkDeprecated" depends="info, build-tools, gatherapi, main"
+        description="Check consistency between javadoc @deprecated and @Deprecated annotation">
+        <java classname="com.ibm.icu.dev.tool.docs.DeprecatedAPIChecker"
+                failonerror="true">
+            <arg value="${out.dir}/icu4j${api.report.version}.api3.gz" />
+            <classpath>
+                <pathelement location="${icu4j.build-tools.jar}"/>
+                <pathelement location="${icu4j.core.jar}"/>
+                <pathelement location="${icu4j.collate.jar}"/>
+                <pathelement location="${icu4j.charset.jar}"/>
+                <pathelement location="${icu4j.currdata.jar}"/>
+                <pathelement location="${icu4j.langdata.jar}"/>
+                <pathelement location="${icu4j.regiondata.jar}"/>
+                <pathelement location="${icu4j.translit.jar}"/>
+            </classpath>
+        </java>
+    </target>
+
     <target name="draftAPIs" depends="info, gatherapi" description="Run API collector tool and generate draft API report">
         <java classname="com.ibm.icu.dev.tool.docs.CollectAPI"
                 classpath="${icu4j.build-tools.jar}"
index de310fc3d50bc55b8aa6a54d249f3ec75d3dcf21..63d3840d904ebb8e7aff82e777e6e0eb4978f6c4 100644 (file)
@@ -222,6 +222,14 @@ public final class Normalizer implements Cloneable {
      * @stable ICU 2.8
      */
     public static abstract class Mode {
+        /**
+         * Sole constructor
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        protected Mode() {
+        }
+
         /**
          * @internal
          * @deprecated This API is ICU internal only.
index 4e3620e3c6d3e1a006ebc66aac1596dfd1a30f52..fd97ecbd4b99798e95ae2919d8c0084850c9d4b1 100644 (file)
@@ -196,6 +196,14 @@ public class PluralRules implements Serializable {
      */
     @Deprecated
     public static abstract class Factory {
+        /**
+         * Sole constructor
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        protected Factory() {
+        }
+
         /**
          * Provides access to the predefined <code>PluralRules</code> 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<Double> explicits,
             Output<Double> uniqueValue, SampleType sampleType) {
index 0e7a7083d9c1715e4ed0bb84c867c953f9302d59..8832ae9c28e651252351f271c63bbd629bfcc3b9 100644 (file)
@@ -422,6 +422,7 @@ public abstract class TimeZoneNames implements Serializable {
      * The super class of <code>TimeZoneNames</code> 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 <code>TimeZoneNames</code>.
          * @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() {
+        }
     }
 
     /**
index bcab647c0aadc0ddc1dc4a7ac89fed763dad5071..6c1f0e050d432442c2bf936aad597666594b9fc5 100644 (file)
@@ -4571,6 +4571,7 @@ public class UnicodeSet extends UnicodeFilter implements Iterable<String>, 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<String>, Compa
     /**
      * Set the default symbol table. Null means ordinary processing. For internal use only. Will affect all subsequent parsing
      * of UnicodeSets.
- * <p>
- * 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}.
- * 
    * <p>
    * 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;
index 1e5b124edf97c4985ce3e93ab22ff5a37c674f74..99e514c9604a1b679773c53f0c1b1e1e2dd417a2 100644 (file)
@@ -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) {
index e76f30f997b4856e4af0c3e07a21588741dda5eb..581563ea8d75eb3bdbd3a4f7a7a5de478b423860 100644 (file)
@@ -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) {
index 9657b5c4e686c11e95ffbe72ffcf04ae13ae74ed..597f2b3797850593d843291d16cc4fd7093d3a31 100644 (file)
@@ -1,5 +1,5 @@
 #*******************************************************************************\r
-#* Copyright (C) 2011-2014, International Business Machines Corporation and         *\r
+#* Copyright (C) 2011-2014, International Business Machines Corporation and    *\r
 #* others. All Rights Reserved.                                                *\r
 #*******************************************************************************\r
-#shared.dir = ../main/shared\r
+shared.dir = ../main/shared\r
index 7612a91b605e04f9c1354e628001b2c1ad6ce728..94635094952939ebedbf58abd9ba85e069b910a3 100644 (file)
@@ -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<APIInfo> 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 (file)
index 0000000..567fedb
--- /dev/null
@@ -0,0 +1,470 @@
+/*\r
+ *******************************************************************************\r
+ * Copyright (C) 2014, International Business Machines Corporation and         *\r
+ * others. All Rights Reserved.                                                *\r
+ *******************************************************************************\r
+ */\r
+package com.ibm.icu.dev.tool.docs;\r
+\r
+import java.io.File;\r
+import java.io.PrintWriter;\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.GenericArrayType;\r
+import java.lang.reflect.Method;\r
+import java.lang.reflect.Modifier;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+import java.lang.reflect.TypeVariable;\r
+import java.lang.reflect.WildcardType;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+\r
+public class DeprecatedAPIChecker {\r
+\r
+    public static void main(String[] args) {\r
+        if (args.length != 1) {\r
+            System.err.println("Illegal command argument. Specify the API signature file path.");\r
+        }\r
+        // Load the ICU4J API signature file\r
+        Set<APIInfo> apiInfoSet = APIData.read(new File(args[0]), true).getAPIInfoSet();\r
+\r
+        DeprecatedAPIChecker checker = new DeprecatedAPIChecker(apiInfoSet, new PrintWriter(System.err, true));\r
+        checker.checkDeprecated();\r
+        System.exit(checker.errCount);\r
+    }\r
+\r
+    private int errCount = 0;\r
+    private Set<APIInfo> apiInfoSet;\r
+    private PrintWriter pw;\r
+\r
+    public DeprecatedAPIChecker(Set<APIInfo> apiInfoSet, PrintWriter pw) {\r
+        this.apiInfoSet = apiInfoSet;\r
+        this.pw = pw;\r
+    }\r
+\r
+    public int errorCount() {\r
+        return errCount;\r
+    }\r
+\r
+    public void checkDeprecated() {\r
+        // Gather API class/enum names and its names that can be\r
+        // used for Class.forName()\r
+        Map<String, String> apiClassNameMap = new TreeMap<String, String>();\r
+        for (APIInfo api : apiInfoSet) {\r
+            if (!api.isPublic() && !api.isProtected()) {\r
+                continue;\r
+            }\r
+            if (!api.isClass() && !api.isEnum()) {\r
+                continue;\r
+            }\r
+            String packageName = api.getPackageName();\r
+            String className = api.getName();\r
+\r
+            // Replacing separator for nested class/enum (replacing '.' with\r
+            // '$'), so we can use the name for Class.forName(String)\r
+            String classNamePath = className.contains(".") ? className.replace('.', '$') : className;\r
+\r
+            apiClassNameMap.put(packageName + "." + classNamePath, packageName + "." + className);\r
+        }\r
+\r
+        // Walk through API classes using reflection\r
+        for (Entry<String, String> classEntry : apiClassNameMap.entrySet()) {\r
+            String classNamePath = classEntry.getKey();\r
+            try {\r
+                Class<?> cls = Class.forName(classNamePath);\r
+                if (cls.isEnum()) {\r
+                    checkEnum(cls, apiClassNameMap);\r
+                } else {\r
+                    checkClass(cls, apiClassNameMap);\r
+                }\r
+            } catch (ClassNotFoundException e) {\r
+                pw.println("## Error ## Class " + classNamePath + " is not found.");\r
+                errCount++;\r
+            }\r
+        }\r
+    }\r
+\r
+    private void checkClass(Class<?> cls, Map<String, String> clsNameMap) {\r
+        assert !cls.isEnum();\r
+\r
+        String clsPath = cls.getName();\r
+        String clsName = clsNameMap.get(clsPath);\r
+        APIInfo api = null;\r
+\r
+        if (clsName != null) {\r
+            api = findClassInfo(apiInfoSet, clsName);\r
+        }\r
+        if (api == null) {\r
+            pw.println("## Error ## Class " + clsName + " is not found in the API signature data.");\r
+            errCount++;\r
+        }\r
+\r
+        // check class\r
+        compareDeprecated(isAPIDeprecated(api), cls.isAnnotationPresent(Deprecated.class), clsName, null, "Class");\r
+\r
+        // check fields\r
+        for (Field f : cls.getDeclaredFields()) {\r
+            if (!isPublicOrProtected(f.getModifiers())) {\r
+                continue;\r
+            }\r
+\r
+            String fName = f.getName();\r
+            api = findFieldInfo(apiInfoSet, clsName, fName);\r
+            if (api == null) {\r
+                pw.println("## Error ## Field " + clsName + "." + fName + " is not found in the API signature data.");\r
+                errCount++;\r
+                continue;\r
+            }\r
+\r
+            compareDeprecated(isAPIDeprecated(api), f.isAnnotationPresent(Deprecated.class), clsName, fName, "Field");\r
+        }\r
+\r
+        // check constructors\r
+        for (Constructor<?> ctor : cls.getDeclaredConstructors()) {\r
+            if (!isPublicOrProtected(ctor.getModifiers())) {\r
+                continue;\r
+            }\r
+\r
+            List<String> paramNames = getParamNames(ctor);\r
+            api = findConstructorInfo(apiInfoSet, clsName, paramNames);\r
+\r
+            if (api == null) {\r
+                pw.println("## Error ## Constructor " + clsName + formatParams(paramNames)\r
+                        + " is not found in the API signature data.");\r
+                errCount++;\r
+                continue;\r
+            }\r
+\r
+            compareDeprecated(isAPIDeprecated(api), ctor.isAnnotationPresent(Deprecated.class), clsName,\r
+                    api.getClassName() + formatParams(paramNames), "Constructor");\r
+        }\r
+\r
+        // check methods\r
+        for (Method mtd : cls.getDeclaredMethods()) {\r
+            // Note: We exclude synthetic method.\r
+            if (!isPublicOrProtected(mtd.getModifiers()) || mtd.isSynthetic()) {\r
+                continue;\r
+            }\r
+\r
+            String mtdName = mtd.getName();\r
+            List<String> paramNames = getParamNames(mtd);\r
+            api = findMethodInfo(apiInfoSet, clsName, mtdName, paramNames);\r
+\r
+            if (api == null) {\r
+                pw.println("## Error ## Method " + clsName + "#" + mtdName + formatParams(paramNames)\r
+                        + " is not found in the API signature data.");\r
+                errCount++;\r
+                continue;\r
+            }\r
+\r
+            compareDeprecated(isAPIDeprecated(api), mtd.isAnnotationPresent(Deprecated.class), clsName, mtdName\r
+                    + formatParams(paramNames), "Method");\r
+\r
+        }\r
+    }\r
+\r
+    private void checkEnum(Class<?> cls, Map<String, String> clsNameMap) {\r
+        assert cls.isEnum();\r
+\r
+        String enumPath = cls.getName();\r
+        String enumName = clsNameMap.get(enumPath);\r
+        APIInfo api = null;\r
+\r
+        if (enumName != null) {\r
+            api = findEnumInfo(apiInfoSet, enumName);\r
+        }\r
+        if (api == null) {\r
+            pw.println("## Error ## Enum " + enumName + " is not found in the API signature data.");\r
+            errCount++;\r
+        }\r
+\r
+        // check enum\r
+        compareDeprecated(isAPIDeprecated(api), cls.isAnnotationPresent(Deprecated.class), enumName, null, "Enum");\r
+\r
+        // check enum constants\r
+        for (Field ec : cls.getDeclaredFields()) {\r
+            if (!ec.isEnumConstant()) {\r
+                continue;\r
+            }\r
+            String ecName = ec.getName();\r
+            api = findEnumConstantInfo(apiInfoSet, enumName, ecName);\r
+            if (api == null) {\r
+                pw.println("## Error ## Enum constant " + enumName + "." + ecName\r
+                        + " is not found in the API signature data.");\r
+                errCount++;\r
+                continue;\r
+            }\r
+\r
+            compareDeprecated(isAPIDeprecated(api), ec.isAnnotationPresent(Deprecated.class), enumName, ecName,\r
+                    "Enum Constant");\r
+        }\r
+    }\r
+\r
+    private void compareDeprecated(boolean depTag, boolean depAnt, String cls, String name, String type) {\r
+        if (depTag != depAnt) {\r
+            String apiName = cls;\r
+            if (name != null) {\r
+                apiName += "." + name;\r
+            }\r
+            if (depTag) {\r
+                pw.println("No @Deprecated annotation: [" + type + "] " + apiName);\r
+            } else {\r
+                pw.println("No @deprecated JavaDoc tag: [" + type + "] " + apiName);\r
+            }\r
+            errCount++;\r
+        }\r
+    }\r
+\r
+    private static boolean isPublicOrProtected(int modifier) {\r
+        return ((modifier & Modifier.PUBLIC) != 0) || ((modifier & Modifier.PROTECTED) != 0);\r
+    }\r
+\r
+    private static boolean isAPIDeprecated(APIInfo api) {\r
+        return api.isDeprecated() || api.isInternal() || api.isObsolete();\r
+    }\r
+\r
+    private static APIInfo findClassInfo(Set<APIInfo> apis, String cls) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getName();\r
+            if (api.isClass() && clsName.equals(cls)) {\r
+                return api;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static APIInfo findFieldInfo(Set<APIInfo> apis, String cls, String field) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getClassName();\r
+            if (api.isField() && clsName.equals(cls) && api.getName().equals(field)) {\r
+                return api;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static APIInfo findConstructorInfo(Set<APIInfo> apis, String cls, List<String> params) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getClassName();\r
+            if (api.isConstructor() && clsName.equals(cls)) {\r
+                // check params\r
+                List<String> paramsFromApi = getParamNames(api);\r
+                if (paramsFromApi.size() == params.size()) {\r
+                    boolean match = true;\r
+                    for (int i = 0; i < params.size(); i++) {\r
+                        if (!params.get(i).equals(paramsFromApi.get(i))) {\r
+                            match = false;\r
+                            break;\r
+                        }\r
+                    }\r
+                    if (match) {\r
+                        return api;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static APIInfo findMethodInfo(Set<APIInfo> apis, String cls, String method, List<String> params) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getClassName();\r
+            if (api.isMethod() && clsName.equals(cls) && api.getName().equals(method)) {\r
+                // check params\r
+                List<String> paramsFromApi = getParamNames(api);\r
+                if (paramsFromApi.size() == params.size()) {\r
+                    boolean match = true;\r
+                    for (int i = 0; i < params.size(); i++) {\r
+                        if (!params.get(i).equals(paramsFromApi.get(i))) {\r
+                            match = false;\r
+                            break;\r
+                        }\r
+                    }\r
+                    if (match) {\r
+                        return api;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static APIInfo findEnumInfo(Set<APIInfo> apis, String ecls) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getName();\r
+            if (api.isEnum() && clsName.equals(ecls)) {\r
+                return api;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static APIInfo findEnumConstantInfo(Set<APIInfo> apis, String ecls, String econst) {\r
+        for (APIInfo api : apis) {\r
+            String clsName = api.getPackageName() + "." + api.getClassName();\r
+            if (api.isEnumConstant() && clsName.equals(ecls) && api.getName().equals(econst)) {\r
+                return api;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static List<String> getParamNames(APIInfo api) {\r
+        if (!api.isMethod() && !api.isConstructor()) {\r
+            throw new IllegalArgumentException(api.toString() + " is not a constructor or a method.");\r
+        }\r
+\r
+        List<String> nameList = new ArrayList<String>();\r
+        String signature = api.getSignature();\r
+        int start = signature.indexOf('(');\r
+        int end = signature.indexOf(')');\r
+\r
+        if (start < 0 || end < 0 || start > end) {\r
+            throw new RuntimeException(api.toString() + " has bad API signature: " + signature);\r
+        }\r
+\r
+        String paramsSegment = signature.substring(start + 1, end);\r
+        // erase generic args\r
+        if (paramsSegment.indexOf('<') >= 0) {\r
+            StringBuilder buf = new StringBuilder();\r
+            boolean inGenericsParams = false;\r
+            for (int i = 0; i < paramsSegment.length(); i++) {\r
+                char c = paramsSegment.charAt(i);\r
+                if (inGenericsParams) {\r
+                    if (c == '>') {\r
+                        inGenericsParams = false;\r
+                    }\r
+                } else {\r
+                    if (c == '<') {\r
+                        inGenericsParams = true;\r
+                    } else {\r
+                        buf.append(c);\r
+                    }\r
+                }\r
+            }\r
+            paramsSegment = buf.toString();\r
+        }\r
+\r
+        if (!paramsSegment.isEmpty()) {\r
+            String[] params = paramsSegment.split("\\s*,\\s*");\r
+            for (String p : params) {\r
+                if (p.endsWith("...")) {\r
+                    // varargs to array\r
+                    p = p.substring(0, p.length() - 3) + "[]";\r
+                }\r
+                nameList.add(p);\r
+            }\r
+        }\r
+\r
+        return nameList;\r
+    }\r
+\r
+    private static List<String> getParamNames(Constructor<?> ctor) {\r
+        return toTypeNameList(ctor.getGenericParameterTypes());\r
+    }\r
+\r
+    private static List<String> getParamNames(Method method) {\r
+        return toTypeNameList(method.getGenericParameterTypes());\r
+    }\r
+\r
+    private static final String[] PRIMITIVES = { "byte", "short", "int", "long", "float", "double", "boolean", "char" };\r
+    private static char[] PRIMITIVE_SIGNATURES = { 'B', 'S', 'I', 'J', 'F', 'D', 'Z', 'C' };\r
+\r
+    private static List<String> toTypeNameList(Type[] types) {\r
+        List<String> nameList = new ArrayList<String>();\r
+\r
+        for (Type t : types) {\r
+            StringBuilder s = new StringBuilder();\r
+            if (t instanceof ParameterizedType) {\r
+                // throw away generics parameters\r
+                ParameterizedType prdType = (ParameterizedType) t;\r
+                Class<?> rawType = (Class<?>) prdType.getRawType();\r
+                s.append(rawType.getCanonicalName());\r
+            } else if (t instanceof WildcardType) {\r
+                // we don't need to worry about WildcardType,\r
+                // because this tool erases generics parameters\r
+                // for comparing method/constructor parameters\r
+                throw new RuntimeException("WildcardType not supported by this tool");\r
+            } else if (t instanceof TypeVariable) {\r
+                // this tool does not try to resolve actual parameter\r
+                // type - for example, "<T extends Object> void foo(T in)"\r
+                // this tool just use the type variable "T" for API signature\r
+                // comparison. This is actually not perfect, but should be\r
+                // sufficient for our purpose.\r
+                TypeVariable<?> tVar = (TypeVariable<?>) t;\r
+                s.append(tVar.getName());\r
+            } else if (t instanceof GenericArrayType) {\r
+                // same as TypeVariable. "T[]" is sufficient enough.\r
+                GenericArrayType tGenArray = (GenericArrayType) t;\r
+                s.append(tGenArray.toString());\r
+            } else if (t instanceof Class) {\r
+                Class<?> tClass = (Class<?>) t;\r
+                String tName = tClass.getCanonicalName();\r
+\r
+                if (tName.charAt(0) == '[') {\r
+                    // Array type\r
+                    int idx = 0;\r
+                    for (; idx < tName.length(); idx++) {\r
+                        if (tName.charAt(idx) != '[') {\r
+                            break;\r
+                        }\r
+                    }\r
+                    int dimension = idx;\r
+                    char sigChar = tName.charAt(dimension);\r
+\r
+                    String elemType = null;\r
+                    if (sigChar == 'L') {\r
+                        // class\r
+                        elemType = tName.substring(dimension + 1, tName.length() - 1);\r
+                    } else {\r
+                        // primitive\r
+                        for (int i = 0; i < PRIMITIVE_SIGNATURES.length; i++) {\r
+                            if (sigChar == PRIMITIVE_SIGNATURES[i]) {\r
+                                elemType = PRIMITIVES[i];\r
+                                break;\r
+                            }\r
+                        }\r
+                    }\r
+\r
+                    if (elemType == null) {\r
+                        throw new RuntimeException("Unexpected array type: " + tName);\r
+                    }\r
+\r
+                    s.append(elemType);\r
+                    for (int i = 0; i < dimension; i++) {\r
+                        s.append("[]");\r
+                    }\r
+                } else {\r
+                    s.append(tName);\r
+                }\r
+            } else {\r
+                throw new IllegalArgumentException("Unknown type: " + t);\r
+            }\r
+\r
+            nameList.add(s.toString());\r
+        }\r
+\r
+        return nameList;\r
+    }\r
+\r
+    private static String formatParams(List<String> paramNames) {\r
+        StringBuilder buf = new StringBuilder("(");\r
+        boolean isFirst = true;\r
+        for (String p : paramNames) {\r
+            if (isFirst) {\r
+                isFirst = false;\r
+            } else {\r
+                buf.append(", ");\r
+            }\r
+            buf.append(p);\r
+        }\r
+        buf.append(")");\r
+\r
+        return buf.toString();\r
+    }\r
+}\r
index cf56f5069c6c5991e3291dc20cb546704b973022..ecc1f92b06b5a81738c3653885003e0bae87518f 100644 (file)
@@ -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;
             }