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