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)
102 throws Throwable {
103 String methodName = method.getName();
104 if (target instanceof Doc) {
105 if (methodName.equals("isIncluded")) {
106 Doc doc = (Doc) target;
107 return !exclude(doc) && doc.isIncluded();
108 }
109 if (target instanceof RootDoc) {
110 if (methodName.equals("classes")) {
111 return filter(((RootDoc) target).classes(), ClassDoc.class);
112 } else if (methodName.equals("specifiedClasses")) {
113 return filter(((RootDoc) target).specifiedClasses(), ClassDoc.class);
114 } else if (methodName.equals("specifiedPackages")) {
115 return filter(((RootDoc) target).specifiedPackages(), PackageDoc.class);
116 }
117 } else if (target instanceof ClassDoc) {
118 if (isFiltered(args)) {
119 if (methodName.equals("methods")) {
120 return filter(((ClassDoc) target).methods(true), MethodDoc.class);
121 } else if (methodName.equals("fields")) {
122 return filter(((ClassDoc) target).fields(true), FieldDoc.class);
123 } else if (methodName.equals("innerClasses")) {
124 return filter(((ClassDoc) target).innerClasses(true),
125 ClassDoc.class);
126 } else if (methodName.equals("constructors")) {
127 return filter(((ClassDoc) target).constructors(true),
128 ConstructorDoc.class);
129 }
130 }
131 } else if (target instanceof PackageDoc) {
132 if (methodName.equals("allClasses")) {
133 if (isFiltered(args)) {
134 return filter(((PackageDoc) target).allClasses(true),
135 ClassDoc.class);
136 } else {
137 return filter(((PackageDoc) target).allClasses(), ClassDoc.class);
138 }
139 } else if (methodName.equals("annotationTypes")) {
140 return filter(((PackageDoc) target).annotationTypes(),
141 AnnotationTypeDoc.class);
142 } else if (methodName.equals("enums")) {
143 return filter(((PackageDoc) target).enums(),
144 ClassDoc.class);
145 } else if (methodName.equals("errors")) {
146 return filter(((PackageDoc) target).errors(),
147 ClassDoc.class);
148 } else if (methodName.equals("exceptions")) {
149 return filter(((PackageDoc) target).exceptions(),
150 ClassDoc.class);
151 } else if (methodName.equals("interfaces")) {
152 return filter(((PackageDoc) target).interfaces(),
153 ClassDoc.class);
154 } else if (methodName.equals("ordinaryClasses")) {
155 return filter(((PackageDoc) target).ordinaryClasses(),
156 ClassDoc.class);
157 }
158 }
159 }
160
161 if (args != null) {
162 if (methodName.equals("compareTo") || methodName.equals("equals")
163 || methodName.equals("overrides")
164 || methodName.equals("subclassOf")) {
165 args[0] = unwrap(args[0]);
166 }
167 }
168 try {
169 return process(method.invoke(target, args), method.getReturnType());
170 } catch (InvocationTargetException e) {
171 throw e.getTargetException();
172 }
173 }
174
175 private static boolean exclude(Doc doc) {
176 AnnotationDesc[] annotations = null;
177 if (doc instanceof ProgramElementDoc) {
178 annotations = ((ProgramElementDoc) doc).annotations();
179 } else if (doc instanceof PackageDoc) {
180 annotations = ((PackageDoc) doc).annotations();
181 }
182 if (annotations != null) {
183 for (AnnotationDesc annotation : annotations) {
184 String qualifiedTypeName = annotation.annotationType().qualifiedTypeName();
185 if (qualifiedTypeName.equals(
186 InterfaceAudience.Private.class.getCanonicalName())
187 || qualifiedTypeName.equals(
188 InterfaceAudience.LimitedPrivate.class.getCanonicalName())) {
189 return true;
190 }
191 if (stability.equals(StabilityOptions.EVOLVING_OPTION)) {
192 if (qualifiedTypeName.equals(
193 InterfaceStability.Unstable.class.getCanonicalName())) {
194 return true;
195 }
196 }
197 if (stability.equals(StabilityOptions.STABLE_OPTION)) {
198 if (qualifiedTypeName.equals(
199 InterfaceStability.Unstable.class.getCanonicalName())
200 || qualifiedTypeName.equals(
201 InterfaceStability.Evolving.class.getCanonicalName())) {
202 return true;
203 }
204 }
205 }
206 for (AnnotationDesc annotation : annotations) {
207 String qualifiedTypeName =
208 annotation.annotationType().qualifiedTypeName();
209 if (qualifiedTypeName.equals(
210 InterfaceAudience.Public.class.getCanonicalName())) {
211 return false;
212 }
213 }
214 }
215 if (treatUnannotatedClassesAsPrivate) {
216 return doc.isClass() || doc.isInterface() || doc.isAnnotationType();
217 }
218 return false;
219 }
220
221 private static Object[] filter(Doc[] array, Class<?> componentType) {
222 if (array == null || array.length == 0) {
223 return array;
224 }
225 List<Object> list = new ArrayList<Object>(array.length);
226 for (Doc entry : array) {
227 if (!exclude(entry)) {
228 list.add(process(entry, componentType));
229 }
230 }
231 return list.toArray((Object[]) Array.newInstance(componentType, list
232 .size()));
233 }
234
235 private Object unwrap(Object proxy) {
236 if (proxy instanceof Proxy)
237 return ((ExcludeHandler) Proxy.getInvocationHandler(proxy)).target;
238 return proxy;
239 }
240
241 private boolean isFiltered(Object[] args) {
242 return args != null && Boolean.TRUE.equals(args[0]);
243 }
244
245 }
246
247 }