1 /* 2 * Copyright (C) 2015 The Android Open Source Project 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 package com.android.icu4j.srcgen.checker; 17 18 import com.google.common.collect.Lists; 19 import com.google.currysrc.api.Rules; 20 import com.google.currysrc.api.input.InputFileGenerator; 21 import com.google.currysrc.api.output.NullOutputSourceFileGenerator; 22 import com.google.currysrc.api.output.OutputSourceFileGenerator; 23 import com.google.currysrc.api.process.Context; 24 import com.google.currysrc.api.process.Processor; 25 import com.google.currysrc.api.process.Rule; 26 import com.google.currysrc.api.process.ast.BodyDeclarationLocators; 27 import com.google.currysrc.api.process.ast.TypeLocator; 28 29 import org.eclipse.jdt.core.dom.ASTNode; 30 import org.eclipse.jdt.core.dom.ASTVisitor; 31 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 32 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; 33 import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; 34 import org.eclipse.jdt.core.dom.BodyDeclaration; 35 import org.eclipse.jdt.core.dom.CompilationUnit; 36 import org.eclipse.jdt.core.dom.EnumConstantDeclaration; 37 import org.eclipse.jdt.core.dom.EnumDeclaration; 38 import org.eclipse.jdt.core.dom.FieldDeclaration; 39 import org.eclipse.jdt.core.dom.Javadoc; 40 import org.eclipse.jdt.core.dom.MethodDeclaration; 41 import org.eclipse.jdt.core.dom.Modifier; 42 import org.eclipse.jdt.core.dom.TagElement; 43 import org.eclipse.jdt.core.dom.TypeDeclaration; 44 45 import java.io.File; 46 import java.util.Collections; 47 import java.util.List; 48 49 import static com.android.icu4j.srcgen.Icu4jTransformRules.createOptionalRule; 50 51 /** 52 * Rules that operate over a set of files and record the public API (according to Android's rules 53 * for @hide). 54 */ 55 class RecordPublicApiRules implements Rules { 56 57 private final InputFileGenerator inputFileGenerator; 58 59 private final RecordPublicApi recordPublicApi; 60 61 public RecordPublicApiRules(InputFileGenerator inputFileGenerator) { 62 this.inputFileGenerator = inputFileGenerator; 63 recordPublicApi = new RecordPublicApi(); 64 } 65 66 @Override public InputFileGenerator getInputFileGenerator() { 67 return inputFileGenerator; 68 } 69 70 @Override public List<Rule> getRuleList(File file) { 71 return Collections.<Rule>singletonList(createOptionalRule(recordPublicApi)); 72 } 73 74 @Override public OutputSourceFileGenerator getOutputSourceFileGenerator() { 75 return NullOutputSourceFileGenerator.INSTANCE; 76 } 77 78 public List<String> publicMembers() { 79 return recordPublicApi.publicMembers(); 80 } 81 82 private static class RecordPublicApi implements Processor { 83 private final List<String> publicMembers = Lists.newArrayList(); 84 85 @Override public void process(Context context, CompilationUnit cu) { 86 cu.accept(new ASTVisitor() { 87 @Override public boolean visit(AnnotationTypeDeclaration node) { 88 throw new AssertionError("Not supported"); 89 } 90 91 @Override public boolean visit(AnnotationTypeMemberDeclaration node) { 92 throw new AssertionError("Not supported"); 93 } 94 95 @Override public boolean visit(EnumConstantDeclaration node) { 96 return handleMemberDeclarationNode(node); 97 } 98 99 @Override public boolean visit(EnumDeclaration node) { 100 return handleTypeDeclarationNode(node); 101 } 102 103 @Override public boolean visit(FieldDeclaration node) { 104 return handleMemberDeclarationNode(node); 105 } 106 107 @Override public boolean visit(MethodDeclaration node) { 108 return handleMemberDeclarationNode(node); 109 } 110 111 @Override public boolean visit(TypeDeclaration node) { 112 return handleTypeDeclarationNode(node); 113 } 114 }); 115 } 116 117 @Override 118 public String toString() { 119 return "RecordPublicApi{}"; 120 } 121 122 private boolean handleTypeDeclarationNode(AbstractTypeDeclaration node) { 123 handleDeclarationNode(node); 124 // Continue processing for nested types / methods. 125 return true; 126 } 127 128 private boolean handleMemberDeclarationNode(BodyDeclaration node) { 129 handleDeclarationNode(node); 130 // Leaf declaration (i.e. a method, fields, enum constant). 131 return false; 132 } 133 134 private void handleDeclarationNode(BodyDeclaration node) { 135 if (isExplicitlyHidden(node)) { 136 return; 137 } 138 139 AbstractTypeDeclaration typeDeclaration = TypeLocator.findTypeDeclarationNode(node); 140 if (typeDeclaration == null) { 141 // Not unusual: methods / fields defined on anonymous types are like this. The parent 142 // is a constructor expression, not a declaration. 143 return; 144 } 145 146 boolean isNonTypeDeclaration = typeDeclaration != node; 147 if (isNonTypeDeclaration) { 148 if (isExplicitlyHidden(node) || !isMemberPublicApiEligible(typeDeclaration, node)) { 149 return; 150 } 151 } 152 while (typeDeclaration != null) { 153 if (isExplicitlyHidden(typeDeclaration) || !isTypePublicApiEligible(typeDeclaration)) { 154 return; 155 } 156 typeDeclaration = TypeLocator.findEnclosingTypeDeclaration(typeDeclaration); 157 } 158 // The node is appropriately public and is not hidden. 159 publicMembers.addAll(BodyDeclarationLocators.toLocatorStringForms(node)); 160 } 161 162 private boolean isTypePublicApiEligible(AbstractTypeDeclaration typeDeclaration) { 163 int typeModifiers = typeDeclaration.getModifiers(); 164 return ((typeModifiers & Modifier.PUBLIC) != 0); 165 } 166 167 public boolean isMemberPublicApiEligible(AbstractTypeDeclaration type, BodyDeclaration node) { 168 if (node.getNodeType() == ASTNode.ENUM_DECLARATION 169 || node.getNodeType() == ASTNode.TYPE_DECLARATION) { 170 throw new AssertionError("Unsupported node type: " + node); 171 } 172 173 if (type instanceof TypeDeclaration) { 174 TypeDeclaration typeDeclaration = (TypeDeclaration) type; 175 // All methods are public on interfaces. Not sure if true on Java 8. 176 if (typeDeclaration.isInterface()) { 177 return true; 178 } 179 } 180 int memberModifiers = node.getModifiers(); 181 return ((memberModifiers & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0); 182 } 183 184 private boolean isExplicitlyHidden(BodyDeclaration node) { 185 Javadoc javadoc = node.getJavadoc(); 186 if (javadoc == null) { 187 return false; 188 } 189 final Boolean[] isHidden = new Boolean[] { false }; 190 javadoc.accept(new ASTVisitor(true /* visitDocNodes */) { 191 @Override public boolean visit(TagElement node) { 192 String tagName = node.getTagName(); 193 if (tagName == null) { 194 return true; 195 } 196 if (tagName.equals("@hide")) { 197 isHidden[0] = true; 198 return false; 199 } 200 return true; 201 } 202 }); 203 return isHidden[0]; 204 } 205 206 public List<String> publicMembers() { 207 return publicMembers; 208 } 209 } 210 } 211