Home | History | Annotate | Download | only in srcgen
      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