ProcessDataBinding.java revision 28e7064d455e2ef9da31c817dfc05ec7405c60df
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.L;
24import android.databinding.tool.util.Preconditions;
25import android.databinding.tool.writer.AnnotationJavaFileWriter;
26import android.databinding.tool.writer.BRWriter;
27import android.databinding.tool.writer.JavaFileWriter;
28
29import java.util.Arrays;
30import java.util.List;
31import java.util.Set;
32
33import javax.annotation.processing.AbstractProcessor;
34import javax.annotation.processing.ProcessingEnvironment;
35import javax.annotation.processing.RoundEnvironment;
36import javax.annotation.processing.SupportedAnnotationTypes;
37import javax.lang.model.SourceVersion;
38import javax.lang.model.element.TypeElement;
39import 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 */
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            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
81    public SourceVersion getSupportedSourceVersion() {
82        return SourceVersion.latest();
83    }
84
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
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
144        protected JavaFileWriter getWriter() {
145            return mJavaFileWriter;
146        }
147
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         */
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         */
171        abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
172                ProcessingEnvironment processingEnvironment,
173                BindingBuildInfo buildInfo);
174    }
175
176    interface Callback {
177        void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk);
178        void onBrWriterReady(BRWriter brWriter);
179    }
180}
181