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