• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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