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 process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)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 getSupportedSourceVersion()81 public SourceVersion getSupportedSourceVersion() { 82 return SourceVersion.latest(); 83 } 84 initProcessingSteps()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 init(ProcessingEnvironment processingEnv)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 getWriter()144 protected JavaFileWriter getWriter() { 145 return mJavaFileWriter; 146 } 147 runStep(RoundEnvironment roundEnvironment, ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)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 */ onHandleStep(RoundEnvironment roundEnvironment, ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)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 */ onProcessingOver(RoundEnvironment roundEnvironment, ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)171 abstract public void onProcessingOver(RoundEnvironment roundEnvironment, 172 ProcessingEnvironment processingEnvironment, 173 BindingBuildInfo buildInfo); 174 } 175 176 interface Callback { onChefReady(CompilerChef chef, boolean libraryProject, int minSdk)177 void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk); onBrWriterReady(BRWriter brWriter)178 void onBrWriterReady(BRWriter brWriter); 179 } 180 } 181