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