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 @Internal} 37 * annotation 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 @Internal annotation. 51 */ hasHideAnnotation(ProgramElementDoc doc)52 private static boolean hasHideAnnotation(ProgramElementDoc doc) { 53 for (AnnotationDesc ann : doc.annotations()) { 54 if (ann.annotationType().qualifiedTypeName().equals("org.conscrypt.Internal")) { 55 return true; 56 } 57 } 58 return false; 59 } 60 61 /** 62 * Returns true if the given element is hidden. 63 */ isHidden(Doc doc)64 private static boolean isHidden(Doc doc) { 65 // Methods, fields, constructors. 66 if (doc instanceof MemberDoc) { 67 return hasHideAnnotation((MemberDoc) doc); 68 } 69 // Classes, interfaces, enums, annotation types. 70 if (doc instanceof ClassDoc) { 71 // Check the class doc and containing class docs if this is a 72 // nested class. 73 ClassDoc current = (ClassDoc) doc; 74 do { 75 if (hasHideAnnotation(current)) { 76 return true; 77 } 78 current = current.containingClass(); 79 } while (current != null); 80 } 81 return false; 82 } 83 84 /** 85 * Filters out hidden elements. 86 */ filterHidden(Object o, Class<?> expected)87 private static Object filterHidden(Object o, Class<?> expected) { 88 if (o == null) { 89 return null; 90 } 91 92 Class<?> type = o.getClass(); 93 if (type.getName().startsWith("com.sun.")) { 94 // TODO: Implement interfaces from superclasses, too. 95 return Proxy.newProxyInstance( 96 type.getClassLoader(), type.getInterfaces(), new HideHandler(o)); 97 } else if (o instanceof Object[]) { 98 Class<?> componentType = expected.getComponentType(); 99 if (componentType == null) { 100 return o; 101 } 102 103 Object[] array = (Object[]) o; 104 List<Object> list = new ArrayList<Object>(array.length); 105 for (Object entry : array) { 106 if ((entry instanceof Doc) && isHidden((Doc) entry)) { 107 continue; 108 } 109 list.add(filterHidden(entry, componentType)); 110 } 111 return list.toArray((Object[]) Array.newInstance(componentType, list.size())); 112 } else { 113 return o; 114 } 115 } 116 117 /** 118 * Filters hidden elements. 119 */ 120 private static class HideHandler implements InvocationHandler { 121 private final Object target; 122 HideHandler(Object target)123 public HideHandler(Object target) { 124 this.target = target; 125 } 126 127 @Override invoke(Object proxy, Method method, Object[] args)128 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 129 String methodName = method.getName(); 130 if (args != null) { 131 if (methodName.equals("compareTo") || methodName.equals("equals") 132 || methodName.equals("overrides") || methodName.equals("subclassOf")) { 133 args[0] = unwrap(args[0]); 134 } 135 } 136 137 try { 138 return filterHidden(method.invoke(target, args), method.getReturnType()); 139 } catch (InvocationTargetException e) { 140 e.printStackTrace(); 141 throw e.getTargetException(); 142 } 143 } 144 unwrap(Object proxy)145 private static Object unwrap(Object proxy) { 146 if (proxy instanceof Proxy) 147 return ((HideHandler) Proxy.getInvocationHandler(proxy)).target; 148 return proxy; 149 } 150 } 151 } 152