Home | History | Annotate | Download | only in annotationprocessor
      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 
     17 package android.databinding.annotationprocessor;
     18 
     19 import android.databinding.BindingBuildInfo;
     20 import android.databinding.tool.CompilerChef;
     21 import android.databinding.tool.processing.Scope;
     22 import android.databinding.tool.reflection.ModelAnalyzer;
     23 import android.databinding.tool.util.L;
     24 import android.databinding.tool.util.Preconditions;
     25 import android.databinding.tool.writer.AnnotationJavaFileWriter;
     26 import android.databinding.tool.writer.BRWriter;
     27 import android.databinding.tool.writer.JavaFileWriter;
     28 
     29 import java.util.Arrays;
     30 import java.util.List;
     31 import java.util.Set;
     32 
     33 import javax.annotation.processing.AbstractProcessor;
     34 import javax.annotation.processing.ProcessingEnvironment;
     35 import javax.annotation.processing.RoundEnvironment;
     36 import javax.annotation.processing.SupportedAnnotationTypes;
     37 import javax.lang.model.SourceVersion;
     38 import javax.lang.model.element.TypeElement;
     39 import javax.xml.bind.JAXBException;
     40 
     41 @SupportedAnnotationTypes({
     42         "android.databinding.BindingAdapter",
     43         "android.databinding.Untaggable",
     44         "android.databinding.BindingMethods",
     45         "android.databinding.BindingConversion",
     46         "android.databinding.BindingBuildInfo"}
     47 )
     48 /**
     49  * Parent annotation processor that dispatches sub steps to ensure execution order.
     50  * Use initProcessingSteps to add a new step.
     51  */
     52 public class ProcessDataBinding extends AbstractProcessor {
     53     private List<ProcessingStep> mProcessingSteps;
     54     @Override
     55     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
     56         if (mProcessingSteps == null) {
     57             initProcessingSteps();
     58         }
     59         final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
     60         if (buildInfo == null) {
     61             return false;
     62         }
     63         boolean done = true;
     64         for (ProcessingStep step : mProcessingSteps) {
     65             try {
     66                 done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
     67             } catch (JAXBException e) {
     68                 L.e(e, "Exception while handling step %s", step);
     69             }
     70         }
     71         if (roundEnv.processingOver()) {
     72             for (ProcessingStep step : mProcessingSteps) {
     73                 step.onProcessingOver(roundEnv, processingEnv, buildInfo);
     74             }
     75         }
     76         Scope.assertNoError();
     77         return done;
     78     }
     79 
     80     @Override
     81     public SourceVersion getSupportedSourceVersion() {
     82         return SourceVersion.latest();
     83     }
     84 
     85     private void initProcessingSteps() {
     86         final ProcessBindable processBindable = new ProcessBindable();
     87         mProcessingSteps = Arrays.asList(
     88                 new ProcessMethodAdapters(),
     89                 new ProcessExpressions(),
     90                 processBindable
     91         );
     92         Callback dataBinderWriterCallback = new Callback() {
     93             CompilerChef mChef;
     94             BRWriter mBRWriter;
     95             boolean mLibraryProject;
     96             int mMinSdk;
     97 
     98             @Override
     99             public void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk) {
    100                 Preconditions.checkNull(mChef, "Cannot set compiler chef twice");
    101                 chef.addBRVariables(processBindable);
    102                 mChef = chef;
    103                 mLibraryProject = libraryProject;
    104                 mMinSdk = minSdk;
    105                 considerWritingMapper();
    106                 mChef.writeDynamicUtil();
    107             }
    108 
    109             private void considerWritingMapper() {
    110                 if (mLibraryProject || mChef == null || mBRWriter == null) {
    111                     return;
    112                 }
    113                 mChef.writeDataBinderMapper(mMinSdk, mBRWriter);
    114             }
    115 
    116             @Override
    117             public void onBrWriterReady(BRWriter brWriter) {
    118                 Preconditions.checkNull(mBRWriter, "Cannot set br writer twice");
    119                 mBRWriter = brWriter;
    120                 considerWritingMapper();
    121             }
    122         };
    123         AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
    124         for (ProcessingStep step : mProcessingSteps) {
    125             step.mJavaFileWriter = javaFileWriter;
    126             step.mCallback = dataBinderWriterCallback;
    127         }
    128     }
    129 
    130     @Override
    131     public synchronized void init(ProcessingEnvironment processingEnv) {
    132         super.init(processingEnv);
    133         ModelAnalyzer.setProcessingEnvironment(processingEnv);
    134     }
    135 
    136     /**
    137      * To ensure execution order and binding build information, we use processing steps.
    138      */
    139     public abstract static class ProcessingStep {
    140         private boolean mDone;
    141         private JavaFileWriter mJavaFileWriter;
    142         protected Callback mCallback;
    143 
    144         protected JavaFileWriter getWriter() {
    145             return mJavaFileWriter;
    146         }
    147 
    148         private boolean runStep(RoundEnvironment roundEnvironment,
    149                 ProcessingEnvironment processingEnvironment,
    150                 BindingBuildInfo buildInfo) throws JAXBException {
    151             if (mDone) {
    152                 return true;
    153             }
    154             mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo);
    155             return mDone;
    156         }
    157 
    158         /**
    159          * Invoked in each annotation processing step.
    160          *
    161          * @return True if it is done and should never be invoked again.
    162          */
    163         abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
    164                 ProcessingEnvironment processingEnvironment,
    165                 BindingBuildInfo buildInfo) throws JAXBException;
    166 
    167         /**
    168          * Invoked when processing is done. A good place to generate the output if the
    169          * processor requires multiple steps.
    170          */
    171         abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
    172                 ProcessingEnvironment processingEnvironment,
    173                 BindingBuildInfo buildInfo);
    174     }
    175 
    176     interface Callback {
    177         void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk);
    178         void onBrWriterReady(BRWriter brWriter);
    179     }
    180 }
    181