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