-/*\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
- // check methods\r
- for (Method mtd : cls.getDeclaredMethods()) {\r
- // Note: We exclude built-in methods in a Java Enum instance\r
- if (!isPublicOrProtected(mtd.getModifiers()) || isBuiltinEnumMethod(mtd)) {\r
- continue;\r
- }\r
-\r
- String mtdName = mtd.getName();\r
- List<String> paramNames = getParamNames(mtd);\r
- api = findMethodInfo(apiInfoSet, enumName, mtdName, paramNames);\r
-\r
- if (api == null) {\r
- pw.println("## Error ## Method " + enumName + "#" + 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), enumName, mtdName\r
- + formatParams(paramNames), "Method");\r
-\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
- // Check if a method is automatically generated for a each Enum\r
- private static boolean isBuiltinEnumMethod(Method mtd) {\r
- // Just check method name for now\r
- String name = mtd.getName();\r
- return name.equals("values") || name.equals("valueOf");\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.length() > 0) {\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
+/*
+ *******************************************************************************
+ * 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<APIInfo> 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<APIInfo> apiInfoSet;
+ private PrintWriter pw;
+
+ public DeprecatedAPIChecker(Set<APIInfo> 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<String, String> apiClassNameMap = new TreeMap<String, String>();
+ 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<String, String> 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<String, String> 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<String> 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<String> 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<String, String> 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");
+ }
+
+ // check methods
+ for (Method mtd : cls.getDeclaredMethods()) {
+ // Note: We exclude built-in methods in a Java Enum instance
+ if (!isPublicOrProtected(mtd.getModifiers()) || isBuiltinEnumMethod(mtd)) {
+ continue;
+ }
+
+ String mtdName = mtd.getName();
+ List<String> paramNames = getParamNames(mtd);
+ api = findMethodInfo(apiInfoSet, enumName, mtdName, paramNames);
+
+ if (api == null) {
+ pw.println("## Error ## Method " + enumName + "#" + mtdName + formatParams(paramNames)
+ + " is not found in the API signature data.");
+ errCount++;
+ continue;
+ }
+
+ compareDeprecated(isAPIDeprecated(api), mtd.isAnnotationPresent(Deprecated.class), enumName, mtdName
+ + formatParams(paramNames), "Method");
+
+ }
+ }
+
+ 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);
+ }
+
+ // Check if a method is automatically generated for a each Enum
+ private static boolean isBuiltinEnumMethod(Method mtd) {
+ // Just check method name for now
+ String name = mtd.getName();
+ return name.equals("values") || name.equals("valueOf");
+ }
+
+ private static boolean isAPIDeprecated(APIInfo api) {
+ return api.isDeprecated() || api.isInternal() || api.isObsolete();
+ }
+
+ private static APIInfo findClassInfo(Set<APIInfo> 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<APIInfo> 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<APIInfo> apis, String cls, List<String> params) {
+ for (APIInfo api : apis) {
+ String clsName = api.getPackageName() + "." + api.getClassName();
+ if (api.isConstructor() && clsName.equals(cls)) {
+ // check params
+ List<String> 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<APIInfo> apis, String cls, String method, List<String> params) {
+ for (APIInfo api : apis) {
+ String clsName = api.getPackageName() + "." + api.getClassName();
+ if (api.isMethod() && clsName.equals(cls) && api.getName().equals(method)) {
+ // check params
+ List<String> 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<APIInfo> 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<APIInfo> 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<String> getParamNames(APIInfo api) {
+ if (!api.isMethod() && !api.isConstructor()) {
+ throw new IllegalArgumentException(api.toString() + " is not a constructor or a method.");
+ }
+
+ List<String> nameList = new ArrayList<String>();
+ 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.length() > 0) {
+ 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<String> getParamNames(Constructor<?> ctor) {
+ return toTypeNameList(ctor.getGenericParameterTypes());
+ }
+
+ private static List<String> 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<String> toTypeNameList(Type[] types) {
+ List<String> nameList = new ArrayList<String>();
+
+ 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, "<T extends Object> 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<String> 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();
+ }
+}