1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 /* 17 * Originally from Doclava project at 18 * https://android.googlesource.com/platform/external/doclava/+/master/src/com/google/doclava/Doclava.java 19 */ 20 21 package org.conscrypt.doclet; 22 23 import com.sun.javadoc.*; 24 import com.sun.tools.doclets.standard.Standard; 25 import com.sun.tools.javadoc.Main; 26 import java.io.FileNotFoundException; 27 import java.lang.reflect.Array; 28 import java.lang.reflect.InvocationHandler; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Proxy; 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * This Doclet filters out all classes, methods, fields, etc. that have the {@code @hide} annotation 37 * on them. 38 */ 39 public class FilterDoclet extends com.sun.tools.doclets.standard.Standard { main(String[] args)40 public static void main(String[] args) throws FileNotFoundException { 41 String name = FilterDoclet.class.getName(); 42 Main.execute(name, args); 43 } 44 start(RootDoc rootDoc)45 public static boolean start(RootDoc rootDoc) { 46 return Standard.start((RootDoc) filterHidden(rootDoc, RootDoc.class)); 47 } 48 49 /** 50 * Returns true if the given element has an @hide annotation. 51 */ hasHideAnnotation(Doc doc)52 private static boolean hasHideAnnotation(Doc doc) { 53 String comment = doc.getRawCommentText(); 54 return comment.contains("@hide"); 55 } 56 57 /** 58 * Returns true if the given element is hidden. 59 */ isHidden(Doc doc)60 private static boolean isHidden(Doc doc) { 61 // Methods, fields, constructors. 62 if (doc instanceof MemberDoc) { 63 return hasHideAnnotation(doc); 64 } 65 // Classes, interfaces, enums, annotation types. 66 if (doc instanceof ClassDoc) { 67 ClassDoc classDoc = (ClassDoc) doc; 68 // Check the containing package. 69 if (hasHideAnnotation(classDoc.containingPackage())) { 70 return true; 71 } 72 // Check the class doc and containing class docs if this is a 73 // nested class. 74 ClassDoc current = classDoc; 75 do { 76 if (hasHideAnnotation(current)) { 77 return true; 78 } 79 current = current.containingClass(); 80 } while (current != null); 81 } 82 return false; 83 } 84 85 /** 86 * Filters out hidden elements. 87 */ filterHidden(Object o, Class<?> expected)88 private static Object filterHidden(Object o, Class<?> expected) { 89 if (o == null) { 90 return null; 91 } 92 93 Class<?> type = o.getClass(); 94 if (type.getName().startsWith("com.sun.")) { 95 // TODO: Implement interfaces from superclasses, too. 96 return Proxy.newProxyInstance( 97 type.getClassLoader(), type.getInterfaces(), new HideHandler(o)); 98 } else if (o instanceof Object[]) { 99 Class<?> componentType = expected.getComponentType(); 100 if (componentType == null) { 101 return o; 102 } 103 104 Object[] array = (Object[]) o; 105 List<Object> list = new ArrayList<Object>(array.length); 106 for (Object entry : array) { 107 if ((entry instanceof Doc) && isHidden((Doc) entry)) { 108 continue; 109 } 110 list.add(filterHidden(entry, componentType)); 111 } 112 return list.toArray((Object[]) Array.newInstance(componentType, list.size())); 113 } else { 114 return o; 115 } 116 } 117 118 /** 119 * Filters hidden elements. 120 */ 121 private static class HideHandler implements InvocationHandler { 122 private final Object target; 123 HideHandler(Object target)124 public HideHandler(Object target) { 125 this.target = target; 126 } 127 128 @Override invoke(Object proxy, Method method, Object[] args)129 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 130 String methodName = method.getName(); 131 if (args != null) { 132 if (methodName.equals("compareTo") || methodName.equals("equals") 133 || methodName.equals("overrides") || methodName.equals("subclassOf")) { 134 args[0] = unwrap(args[0]); 135 } 136 } 137 138 try { 139 return filterHidden(method.invoke(target, args), method.getReturnType()); 140 } catch (InvocationTargetException e) { 141 e.printStackTrace(); 142 throw e.getTargetException(); 143 } 144 } 145 unwrap(Object proxy)146 private static Object unwrap(Object proxy) { 147 if (proxy instanceof Proxy) 148 return ((HideHandler) Proxy.getInvocationHandler(proxy)).target; 149 return proxy; 150 } 151 } 152 } 153