| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.classification.tools; |
| |
| import com.sun.javadoc.AnnotationDesc; |
| import com.sun.javadoc.AnnotationTypeDoc; |
| import com.sun.javadoc.ClassDoc; |
| import com.sun.javadoc.ConstructorDoc; |
| import com.sun.javadoc.Doc; |
| import com.sun.javadoc.FieldDoc; |
| import com.sun.javadoc.MethodDoc; |
| import com.sun.javadoc.PackageDoc; |
| import com.sun.javadoc.ProgramElementDoc; |
| import com.sun.javadoc.RootDoc; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| import org.apache.hadoop.classification.InterfaceAudience; |
| import org.apache.hadoop.classification.InterfaceStability; |
| |
| /** |
| * Process the {@link RootDoc} by substituting with (nested) proxy objects that |
| * exclude elements with Private or LimitedPrivate annotations. |
| * <p> |
| * Based on code from http://www.sixlegs.com/blog/java/exclude-javadoc-tag.html. |
| */ |
| class RootDocProcessor { |
| |
| static String stability = StabilityOptions.UNSTABLE_OPTION; |
| static boolean treatUnannotatedClassesAsPrivate = false; |
| |
| public static RootDoc process(RootDoc root) { |
| return (RootDoc) process(root, RootDoc.class); |
| } |
| |
| private static Object process(Object obj, Class<?> type) { |
| if (obj == null) { |
| return null; |
| } |
| Class<?> cls = obj.getClass(); |
| if (cls.getName().startsWith("com.sun.")) { |
| return getProxy(obj); |
| } else if (obj instanceof Object[]) { |
| Class<?> componentType = type.isArray() ? type.getComponentType() |
| : cls.getComponentType(); |
| Object[] array = (Object[]) obj; |
| Object[] newArray = (Object[]) Array.newInstance(componentType, |
| array.length); |
| for (int i = 0; i < array.length; ++i) { |
| newArray[i] = process(array[i], componentType); |
| } |
| return newArray; |
| } |
| return obj; |
| } |
| |
| private static Map<Object, Object> proxies = |
| new WeakHashMap<Object, Object>(); |
| |
| private static Object getProxy(Object obj) { |
| Object proxy = proxies.get(obj); |
| if (proxy == null) { |
| proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), |
| obj.getClass().getInterfaces(), new ExcludeHandler(obj)); |
| proxies.put(obj, proxy); |
| } |
| return proxy; |
| } |
| |
| private static class ExcludeHandler implements InvocationHandler { |
| private Object target; |
| |
| public ExcludeHandler(Object target) { |
| this.target = target; |
| } |
| |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) |
| throws Throwable { |
| String methodName = method.getName(); |
| if (target instanceof Doc) { |
| if (methodName.equals("isIncluded")) { |
| Doc doc = (Doc) target; |
| return !exclude(doc) && doc.isIncluded(); |
| } |
| if (target instanceof RootDoc) { |
| if (methodName.equals("classes")) { |
| return filter(((RootDoc) target).classes(), ClassDoc.class); |
| } else if (methodName.equals("specifiedClasses")) { |
| return filter(((RootDoc) target).specifiedClasses(), ClassDoc.class); |
| } else if (methodName.equals("specifiedPackages")) { |
| return filter(((RootDoc) target).specifiedPackages(), PackageDoc.class); |
| } |
| } else if (target instanceof ClassDoc) { |
| if (isFiltered(args)) { |
| if (methodName.equals("methods")) { |
| return filter(((ClassDoc) target).methods(true), MethodDoc.class); |
| } else if (methodName.equals("fields")) { |
| return filter(((ClassDoc) target).fields(true), FieldDoc.class); |
| } else if (methodName.equals("innerClasses")) { |
| return filter(((ClassDoc) target).innerClasses(true), |
| ClassDoc.class); |
| } else if (methodName.equals("constructors")) { |
| return filter(((ClassDoc) target).constructors(true), |
| ConstructorDoc.class); |
| } |
| } |
| } else if (target instanceof PackageDoc) { |
| if (methodName.equals("allClasses")) { |
| if (isFiltered(args)) { |
| return filter(((PackageDoc) target).allClasses(true), |
| ClassDoc.class); |
| } else { |
| return filter(((PackageDoc) target).allClasses(), ClassDoc.class); |
| } |
| } else if (methodName.equals("annotationTypes")) { |
| return filter(((PackageDoc) target).annotationTypes(), |
| AnnotationTypeDoc.class); |
| } else if (methodName.equals("enums")) { |
| return filter(((PackageDoc) target).enums(), |
| ClassDoc.class); |
| } else if (methodName.equals("errors")) { |
| return filter(((PackageDoc) target).errors(), |
| ClassDoc.class); |
| } else if (methodName.equals("exceptions")) { |
| return filter(((PackageDoc) target).exceptions(), |
| ClassDoc.class); |
| } else if (methodName.equals("interfaces")) { |
| return filter(((PackageDoc) target).interfaces(), |
| ClassDoc.class); |
| } else if (methodName.equals("ordinaryClasses")) { |
| return filter(((PackageDoc) target).ordinaryClasses(), |
| ClassDoc.class); |
| } |
| } |
| } |
| |
| if (args != null) { |
| if (methodName.equals("compareTo") || methodName.equals("equals") |
| || methodName.equals("overrides") |
| || methodName.equals("subclassOf")) { |
| args[0] = unwrap(args[0]); |
| } |
| } |
| try { |
| return process(method.invoke(target, args), method.getReturnType()); |
| } catch (InvocationTargetException e) { |
| throw e.getTargetException(); |
| } |
| } |
| |
| private static boolean exclude(Doc doc) { |
| AnnotationDesc[] annotations = null; |
| if (doc instanceof ProgramElementDoc) { |
| annotations = ((ProgramElementDoc) doc).annotations(); |
| } else if (doc instanceof PackageDoc) { |
| annotations = ((PackageDoc) doc).annotations(); |
| } |
| if (annotations != null) { |
| for (AnnotationDesc annotation : annotations) { |
| String qualifiedTypeName = annotation.annotationType().qualifiedTypeName(); |
| if (qualifiedTypeName.equals( |
| InterfaceAudience.Private.class.getCanonicalName()) |
| || qualifiedTypeName.equals( |
| InterfaceAudience.LimitedPrivate.class.getCanonicalName())) { |
| return true; |
| } |
| if (stability.equals(StabilityOptions.EVOLVING_OPTION)) { |
| if (qualifiedTypeName.equals( |
| InterfaceStability.Unstable.class.getCanonicalName())) { |
| return true; |
| } |
| } |
| if (stability.equals(StabilityOptions.STABLE_OPTION)) { |
| if (qualifiedTypeName.equals( |
| InterfaceStability.Unstable.class.getCanonicalName()) |
| || qualifiedTypeName.equals( |
| InterfaceStability.Evolving.class.getCanonicalName())) { |
| return true; |
| } |
| } |
| } |
| for (AnnotationDesc annotation : annotations) { |
| String qualifiedTypeName = |
| annotation.annotationType().qualifiedTypeName(); |
| if (qualifiedTypeName.equals( |
| InterfaceAudience.Public.class.getCanonicalName())) { |
| return false; |
| } |
| } |
| } |
| if (treatUnannotatedClassesAsPrivate) { |
| return doc.isClass() || doc.isInterface() || doc.isAnnotationType(); |
| } |
| return false; |
| } |
| |
| private static Object[] filter(Doc[] array, Class<?> componentType) { |
| if (array == null || array.length == 0) { |
| return array; |
| } |
| List<Object> list = new ArrayList<Object>(array.length); |
| for (Doc entry : array) { |
| if (!exclude(entry)) { |
| list.add(process(entry, componentType)); |
| } |
| } |
| return list.toArray((Object[]) Array.newInstance(componentType, list |
| .size())); |
| } |
| |
| private Object unwrap(Object proxy) { |
| if (proxy instanceof Proxy) |
| return ((ExcludeHandler) Proxy.getInvocationHandler(proxy)).target; |
| return proxy; |
| } |
| |
| private boolean isFiltered(Object[] args) { |
| return args != null && Boolean.TRUE.equals(args[0]); |
| } |
| |
| } |
| |
| } |