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