1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.classification.tools;
19
20 import com.sun.javadoc.AnnotationDesc;
21 import com.sun.javadoc.AnnotationTypeDoc;
22 import com.sun.javadoc.ClassDoc;
23 import com.sun.javadoc.ConstructorDoc;
24 import com.sun.javadoc.Doc;
25 import com.sun.javadoc.FieldDoc;
26 import com.sun.javadoc.MethodDoc;
27 import com.sun.javadoc.PackageDoc;
28 import com.sun.javadoc.ProgramElementDoc;
29 import com.sun.javadoc.RootDoc;
30
31 import java.lang.reflect.Array;
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Proxy;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.WeakHashMap;
40
41 import org.apache.hadoop.hbase.classification.InterfaceAudience;
42 import org.apache.hadoop.hbase.classification.InterfaceStability;
43
44
45
46
47
48
49
50 class RootDocProcessor {
51
52 static String stability = StabilityOptions.UNSTABLE_OPTION;
53 static boolean treatUnannotatedClassesAsPrivate = false;
54
55 public static RootDoc process(RootDoc root) {
56 return (RootDoc) process(root, RootDoc.class);
57 }
58
59 private static Object process(Object obj, Class<?> type) {
60 if (obj == null) {
61 return null;
62 }
63 Class<?> cls = obj.getClass();
64 if (cls.getName().startsWith("com.sun.")) {
65 return getProxy(obj);
66 } else if (obj instanceof Object[]) {
67 Class<?> componentType = type.isArray() ? type.getComponentType()
68 : cls.getComponentType();
69 Object[] array = (Object[]) obj;
70 Object[] newArray = (Object[]) Array.newInstance(componentType,
71 array.length);
72 for (int i = 0; i < array.length; ++i) {
73 newArray[i] = process(array[i], componentType);
74 }
75 return newArray;
76 }
77 return obj;
78 }
79
80 private static Map<Object, Object> proxies =
81 new WeakHashMap<Object, Object>();
82
83 private static Object getProxy(Object obj) {
84 Object proxy = proxies.get(obj);
85 if (proxy == null) {
86 proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
87 obj.getClass().getInterfaces(), new ExcludeHandler(obj));
88 proxies.put(obj, proxy);
89 }
90 return proxy;
91 }
92
93 private static class ExcludeHandler implements InvocationHandler {
94 private Object target;
95
96 public ExcludeHandler(Object target) {
97 this.target = target;
98 }
99
100 @Override
101 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
102 String methodName = method.getName();
103 if (target instanceof Doc) {
104 if (methodName.equals("isIncluded")) {
105 Doc doc = (Doc) target;
106 return !exclude(doc) && doc.isIncluded();
107 }
108 if (target instanceof RootDoc) {
109 if (methodName.equals("classes")) {
110 return filter(((RootDoc) target).classes(), ClassDoc.class);
111 } else if (methodName.equals("specifiedClasses")) {
112 return filter(((RootDoc) target).specifiedClasses(), ClassDoc.class);
113 } else if (methodName.equals("specifiedPackages")) {
114 return filter(((RootDoc) target).specifiedPackages(), PackageDoc.class);
115 }
116 } else if (target instanceof ClassDoc) {
117 if (isFiltered(args)) {
118 if (methodName.equals("methods")) {
119 return filter(((ClassDoc) target).methods(true), MethodDoc.class);
120 } else if (methodName.equals("fields")) {
121 return filter(((ClassDoc) target).fields(true), FieldDoc.class);
122 } else if (methodName.equals("innerClasses")) {
123 return filter(((ClassDoc) target).innerClasses(true), ClassDoc.class);
124 } else if (methodName.equals("constructors")) {
125 return filter(((ClassDoc) target).constructors(true), ConstructorDoc.class);
126 }
127 }
128 } else if (target instanceof PackageDoc) {
129 if (methodName.equals("allClasses")) {
130 if (isFiltered(args)) {
131 return filter(((PackageDoc) target).allClasses(true), ClassDoc.class);
132 } else {
133 return filter(((PackageDoc) target).allClasses(), ClassDoc.class);
134 }
135 } else if (methodName.equals("annotationTypes")) {
136 return filter(((PackageDoc) target).annotationTypes(), AnnotationTypeDoc.class);
137 } else if (methodName.equals("enums")) {
138 return filter(((PackageDoc) target).enums(), ClassDoc.class);
139 } else if (methodName.equals("errors")) {
140 return filter(((PackageDoc) target).errors(), ClassDoc.class);
141 } else if (methodName.equals("exceptions")) {
142 return filter(((PackageDoc) target).exceptions(), ClassDoc.class);
143 } else if (methodName.equals("interfaces")) {
144 return filter(((PackageDoc) target).interfaces(), ClassDoc.class);
145 } else if (methodName.equals("ordinaryClasses")) {
146 return filter(((PackageDoc) target).ordinaryClasses(), ClassDoc.class);
147 }
148 }
149 }
150
151 if (args != null) {
152 if (methodName.equals("compareTo") || methodName.equals("equals")
153 || methodName.equals("overrides") || methodName.equals("subclassOf")) {
154 args[0] = unwrap(args[0]);
155 }
156 }
157 try {
158 return process(method.invoke(target, args), method.getReturnType());
159 } catch (InvocationTargetException e) {
160 throw e.getTargetException();
161 }
162 }
163
164 private static boolean exclude(Doc doc) {
165 AnnotationDesc[] annotations = null;
166 if (doc instanceof ProgramElementDoc) {
167 annotations = ((ProgramElementDoc) doc).annotations();
168 } else if (doc instanceof PackageDoc) {
169 annotations = ((PackageDoc) doc).annotations();
170 }
171 if (annotations != null) {
172 for (AnnotationDesc annotation : annotations) {
173 String qualifiedTypeName = annotation.annotationType().qualifiedTypeName();
174 if (qualifiedTypeName.equals(InterfaceAudience.Private.class.getCanonicalName())
175 || qualifiedTypeName
176 .equals(InterfaceAudience.LimitedPrivate.class.getCanonicalName())) {
177 return true;
178 }
179 if (stability.equals(StabilityOptions.EVOLVING_OPTION)) {
180 if (qualifiedTypeName.equals(InterfaceStability.Unstable.class.getCanonicalName())) {
181 return true;
182 }
183 }
184 if (stability.equals(StabilityOptions.STABLE_OPTION)) {
185 if (qualifiedTypeName.equals(InterfaceStability.Unstable.class.getCanonicalName())
186 || qualifiedTypeName.equals(InterfaceStability.Evolving.class.getCanonicalName())) {
187 return true;
188 }
189 }
190 }
191 for (AnnotationDesc annotation : annotations) {
192 String qualifiedTypeName = annotation.annotationType().qualifiedTypeName();
193 if (qualifiedTypeName.equals(InterfaceAudience.Public.class.getCanonicalName())) {
194 return false;
195 }
196 }
197 }
198 if (treatUnannotatedClassesAsPrivate) {
199 return doc.isClass() || doc.isInterface() || doc.isAnnotationType();
200 }
201 return false;
202 }
203
204 private static Object[] filter(Doc[] array, Class<?> componentType) {
205 if (array == null || array.length == 0) {
206 return array;
207 }
208 List<Object> list = new ArrayList<Object>(array.length);
209 for (Doc entry : array) {
210 if (!exclude(entry)) {
211 list.add(process(entry, componentType));
212 }
213 }
214 return list.toArray((Object[]) Array.newInstance(componentType, list.size()));
215 }
216
217 private Object unwrap(Object proxy) {
218 if (proxy instanceof Proxy)
219 return ((ExcludeHandler) Proxy.getInvocationHandler(proxy)).target;
220 return proxy;
221 }
222
223 private boolean isFiltered(Object[] args) {
224 return args != null && Boolean.TRUE.equals(args[0]);
225 }
226
227 }
228
229 }