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; 17 18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.Lists; 20 import com.google.currysrc.Main; 21 import com.google.currysrc.api.RuleSet; 22 import com.google.currysrc.api.input.InputFileGenerator; 23 import com.google.currysrc.api.output.NullOutputSourceFileGenerator; 24 import com.google.currysrc.api.output.OutputSourceFileGenerator; 25 import com.google.currysrc.api.process.Context; 26 import com.google.currysrc.api.process.Processor; 27 import com.google.currysrc.api.process.Rule; 28 import com.google.currysrc.api.process.ast.BodyDeclarationLocators; 29 import com.google.currysrc.api.process.ast.TypeLocator; 30 31 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 32 import org.eclipse.jdt.core.dom.BodyDeclaration; 33 import org.eclipse.jdt.core.dom.CompilationUnit; 34 import org.eclipse.jdt.core.dom.EnumConstantDeclaration; 35 import org.eclipse.jdt.core.dom.Javadoc; 36 import org.eclipse.jdt.core.dom.TagElement; 37 import org.eclipse.jdt.core.dom.TypeDeclaration; 38 39 import java.io.File; 40 import java.lang.reflect.Modifier; 41 import java.util.Collections; 42 import java.util.List; 43 44 import static com.google.currysrc.api.process.Rules.createOptionalRule; 45 46 /** 47 * Generates text that can be injected into Icu4JTransform for describing source elements that 48 * should be hidden because they are deprecated. Only intended for use in capturing the 49 * <em>initial set</em> of ICU elements to be hidden. Typically, anything that ICU deprecates in 50 * future should remain public until they can safely be removed from Android's public APIs. 51 */ 52 public class CaptureDeprecatedElements { 53 54 private static final boolean DEBUG = true; 55 56 private static final String ANDROID_ICU_PREFIX = "android.icu."; 57 private static final String ORIGINAL_ICU_PREFIX = "com.ibm.icu."; 58 CaptureDeprecatedElements()59 private CaptureDeprecatedElements() { 60 } 61 62 /** 63 * Usage: 64 * java com.android.icu4j.srcgen.CaptureDeprecatedMethods {one or more source directories} 65 */ main(String[] args)66 public static void main(String[] args) throws Exception { 67 CaptureDeprecatedMethodsRules rules = new CaptureDeprecatedMethodsRules(args); 68 new Main(DEBUG).execute(rules); 69 List<String> deprecatedElements = rules.getCaptureRule().getDeprecatedElements(); 70 71 // ASCII order for easier maintenance of the source this goes into. 72 List<String> sortedDeprecatedElements = Lists.newArrayList(deprecatedElements); 73 Collections.sort(sortedDeprecatedElements); 74 for (String entry : sortedDeprecatedElements) { 75 String entryInAndroid = entry.replace(ORIGINAL_ICU_PREFIX, ANDROID_ICU_PREFIX); 76 System.out.println(" \"" + entryInAndroid + "\","); 77 } 78 } 79 80 private static class CaptureDeprecatedMethodsRules implements RuleSet { 81 82 private final InputFileGenerator inputFileGenerator; 83 84 private final CaptureDeprecatedProcessor captureTransformer; 85 CaptureDeprecatedMethodsRules(String[] args)86 public CaptureDeprecatedMethodsRules(String[] args) { 87 if (args.length < 1) { 88 throw new IllegalArgumentException("At least 1 argument required."); 89 } 90 inputFileGenerator = Icu4jTransformRules.createInputFileGenerator(args); 91 92 ImmutableList.Builder<TypeLocator> apiClassesAllowlistBuilder = ImmutableList.builder(); 93 for (String publicClassName : Icu4jTransform.PUBLIC_API_CLASSES) { 94 String originalIcuClassName = publicClassName.replace(ANDROID_ICU_PREFIX, 95 ORIGINAL_ICU_PREFIX); 96 apiClassesAllowlistBuilder.add(new TypeLocator(originalIcuClassName)); 97 } 98 captureTransformer = new CaptureDeprecatedProcessor(apiClassesAllowlistBuilder.build()); 99 } 100 101 @Override getRuleList(File file)102 public List<Rule> getRuleList(File file) { 103 return Lists.<Rule>newArrayList(createOptionalRule(captureTransformer)); 104 } 105 106 @Override getInputFileGenerator()107 public InputFileGenerator getInputFileGenerator() { 108 return inputFileGenerator; 109 } 110 111 @Override getOutputSourceFileGenerator()112 public OutputSourceFileGenerator getOutputSourceFileGenerator() { 113 return NullOutputSourceFileGenerator.INSTANCE; 114 } 115 getCaptureRule()116 public CaptureDeprecatedProcessor getCaptureRule() { 117 return captureTransformer; 118 } 119 } 120 121 private static class CaptureDeprecatedProcessor implements Processor { 122 123 private final List<TypeLocator> publicClassLocators; 124 private final List<String> deprecatedElements = Lists.newArrayList(); 125 CaptureDeprecatedProcessor(List<TypeLocator> publicClassLocators)126 public CaptureDeprecatedProcessor(List<TypeLocator> publicClassLocators) { 127 this.publicClassLocators = publicClassLocators; 128 } 129 130 @Override process(Context context, CompilationUnit cu)131 public void process(Context context, CompilationUnit cu) { 132 for (TypeLocator publicClassLocator : publicClassLocators) { 133 AbstractTypeDeclaration matchedType = publicClassLocator.find(cu); 134 if (matchedType != null) { 135 if (isDeprecated(matchedType)) { 136 List<String> locatorStrings = BodyDeclarationLocators.toLocatorStringForms(matchedType); 137 deprecatedElements.addAll(locatorStrings); 138 } 139 trackDeprecationsRecursively(matchedType); 140 } 141 } 142 } 143 trackDeprecationsRecursively(AbstractTypeDeclaration matchedType)144 private void trackDeprecationsRecursively(AbstractTypeDeclaration matchedType) { 145 for (BodyDeclaration bodyDeclaration 146 : (List<BodyDeclaration>) matchedType.bodyDeclarations()) { 147 if (isApiVisible(matchedType, bodyDeclaration) && isDeprecated(bodyDeclaration)) { 148 deprecatedElements.addAll(BodyDeclarationLocators.toLocatorStringForms(bodyDeclaration)); 149 if (bodyDeclaration instanceof AbstractTypeDeclaration) { 150 trackDeprecationsRecursively((AbstractTypeDeclaration) bodyDeclaration); 151 } 152 } 153 } 154 } 155 isApiVisible( AbstractTypeDeclaration matchedType, BodyDeclaration bodyDeclaration)156 private boolean isApiVisible( 157 AbstractTypeDeclaration matchedType, BodyDeclaration bodyDeclaration) { 158 // public members and those that might be inherited are ones that might show up in the APIs. 159 if (matchedType instanceof TypeDeclaration) { 160 TypeDeclaration typeDeclaration = (TypeDeclaration) matchedType; 161 if (typeDeclaration.isInterface()) { 162 // Interface declarations are public regardless of whether they are explicitly marked as 163 // such. 164 return true; 165 } 166 } 167 if (bodyDeclaration instanceof EnumConstantDeclaration) { 168 return true; 169 } 170 171 return (bodyDeclaration.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) > 0; 172 } 173 isDeprecated(BodyDeclaration bodyDeclaration)174 private static boolean isDeprecated(BodyDeclaration bodyDeclaration) { 175 Javadoc doc = bodyDeclaration.getJavadoc(); 176 // This only checks for the @deprecated javadoc tag, not the java.lang.Deprecated annotation. 177 if (doc != null) { 178 for (TagElement tag : (List<TagElement>) doc.tags()) { 179 if (tag.getTagName() != null && tag.getTagName().equalsIgnoreCase("@deprecated")) { 180 return true; 181 } 182 } 183 } 184 return false; 185 } 186 getDeprecatedElements()187 public List<String> getDeprecatedElements() { 188 return deprecatedElements; 189 } 190 191 @Override toString()192 public String toString() { 193 return "CaptureDeprecatedProcessor{" + 194 "publicClassLocators=" + publicClassLocators + 195 '}'; 196 } 197 } 198 } 199