1a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar/*
2a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Copyright (C) 2015 The Android Open Source Project
3a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
4a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * you may not use this file except in compliance with the License.
6a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * You may obtain a copy of the License at
7a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
8a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
10a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Unless required by applicable law or agreed to in writing, software
11a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * See the License for the specific language governing permissions and
14a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * limitations under the License.
15a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar */
16a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.annotationprocessor;
18a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.BindingBuildInfo;
204df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyarimport android.databinding.tool.CompilerChef;
21731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.processing.Scope;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
2328e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport android.databinding.tool.util.L;
244df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyarimport android.databinding.tool.util.Preconditions;
25fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.writer.AnnotationJavaFileWriter;
264df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyarimport android.databinding.tool.writer.BRWriter;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.writer.JavaFileWriter;
28a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
29a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.Arrays;
30a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.List;
31a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.Set;
32a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
33a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.AbstractProcessor;
34a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.ProcessingEnvironment;
35a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.RoundEnvironment;
36a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.SupportedAnnotationTypes;
37a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.lang.model.SourceVersion;
38a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.lang.model.element.TypeElement;
3928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyarimport javax.xml.bind.JAXBException;
40a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
41a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar@SupportedAnnotationTypes({
42fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        "android.databinding.BindingAdapter",
43fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        "android.databinding.Untaggable",
44fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        "android.databinding.BindingMethods",
45fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        "android.databinding.BindingConversion",
46fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount        "android.databinding.BindingBuildInfo"}
47a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar)
48a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar/**
49a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Parent annotation processor that dispatches sub steps to ensure execution order.
50a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Use initProcessingSteps to add a new step.
51a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar */
52a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarpublic class ProcessDataBinding extends AbstractProcessor {
53a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private List<ProcessingStep> mProcessingSteps;
54a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    @Override
55a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
56a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (mProcessingSteps == null) {
57a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            initProcessingSteps();
58a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
59a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
60a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (buildInfo == null) {
61a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return false;
62a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
63a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        boolean done = true;
64a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (ProcessingStep step : mProcessingSteps) {
6528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            try {
6628e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
6728e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            } catch (JAXBException e) {
6828e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                L.e(e, "Exception while handling step %s", step);
6928e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar            }
70a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
71a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        if (roundEnv.processingOver()) {
72a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            for (ProcessingStep step : mProcessingSteps) {
73a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                step.onProcessingOver(roundEnv, processingEnv, buildInfo);
74a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
75a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
76731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        Scope.assertNoError();
77a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        return done;
78a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
79a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    @Override
819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public SourceVersion getSupportedSourceVersion() {
829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return SourceVersion.latest();
839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
85a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    private void initProcessingSteps() {
864df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        final ProcessBindable processBindable = new ProcessBindable();
87a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        mProcessingSteps = Arrays.asList(
88a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                new ProcessMethodAdapters(),
894df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                new ProcessExpressions(),
9061630faa88ee4817834d47294a0e17f19d8e1c51George Mount                processBindable
91a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        );
924df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        Callback dataBinderWriterCallback = new Callback() {
934df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            CompilerChef mChef;
944df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            BRWriter mBRWriter;
954df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            boolean mLibraryProject;
964df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            int mMinSdk;
974df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar
984df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            @Override
994df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            public void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk) {
1004df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                Preconditions.checkNull(mChef, "Cannot set compiler chef twice");
1014df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                chef.addBRVariables(processBindable);
1024df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                mChef = chef;
1034df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                mLibraryProject = libraryProject;
1044df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                mMinSdk = minSdk;
1054df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                considerWritingMapper();
10699d74030fe5856572bd4b569bc04d2a11258f202George Mount                mChef.writeDynamicUtil();
1074df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            }
1084df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar
1094df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            private void considerWritingMapper() {
1104df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                if (mLibraryProject || mChef == null || mBRWriter == null) {
1114df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                    return;
1124df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                }
1134df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                mChef.writeDataBinderMapper(mMinSdk, mBRWriter);
1144df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            }
1154df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar
1164df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            @Override
1174df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            public void onBrWriterReady(BRWriter brWriter) {
1184df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                Preconditions.checkNull(mBRWriter, "Cannot set br writer twice");
1194df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                mBRWriter = brWriter;
1204df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar                considerWritingMapper();
1214df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            }
1224df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        };
123a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
124a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (ProcessingStep step : mProcessingSteps) {
125a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            step.mJavaFileWriter = javaFileWriter;
1264df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar            step.mCallback = dataBinderWriterCallback;
127a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
128a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
129a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
130a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    @Override
131a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public synchronized void init(ProcessingEnvironment processingEnv) {
132a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        super.init(processingEnv);
133a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        ModelAnalyzer.setProcessingEnvironment(processingEnv);
134a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
135a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
136a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    /**
137a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     * To ensure execution order and binding build information, we use processing steps.
138a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar     */
139a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public abstract static class ProcessingStep {
140a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private boolean mDone;
141a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private JavaFileWriter mJavaFileWriter;
1424df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        protected Callback mCallback;
143a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
144a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        protected JavaFileWriter getWriter() {
145a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return mJavaFileWriter;
146a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
147a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
148a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private boolean runStep(RoundEnvironment roundEnvironment,
149a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                ProcessingEnvironment processingEnvironment,
15028e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                BindingBuildInfo buildInfo) throws JAXBException {
151a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            if (mDone) {
152a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                return true;
153a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
154a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo);
155a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return mDone;
156a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
157a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
158a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        /**
159a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         * Invoked in each annotation processing step.
160a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         *
161a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         * @return True if it is done and should never be invoked again.
162a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         */
163a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
164a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                ProcessingEnvironment processingEnvironment,
16528e7064d455e2ef9da31c817dfc05ec7405c60dfYigit Boyar                BindingBuildInfo buildInfo) throws JAXBException;
166a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
167a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        /**
168a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         * Invoked when processing is done. A good place to generate the output if the
169a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         * processor requires multiple steps.
170a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar         */
171a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
172a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                ProcessingEnvironment processingEnvironment,
173a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                BindingBuildInfo buildInfo);
174a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
1754df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar
1764df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar    interface Callback {
1774df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk);
1784df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar        void onBrWriterReady(BRWriter brWriter);
1794df4ba38a62b791bbbc25e923efe8d9c2f9a52e9Yigit Boyar    }
180a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar}
181