All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.databinding.annotationprocessor.ProcessDataBinding Maven / Gradle / Ivy

Go to download

The annotation processor for Data Binding. Generates binding classes for runtime.

The newest version!
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.databinding.annotationprocessor;

import android.databinding.BindingBuildInfo;
import android.databinding.tool.CompilerChef;
import android.databinding.tool.DataBindingCompilerArgs;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.ScopedException;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.util.GenerationalClassUtil;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.AnnotationJavaFileWriter;
import android.databinding.tool.writer.BRWriter;
import android.databinding.tool.writer.JavaFileWriter;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.xml.bind.JAXBException;

@SupportedAnnotationTypes({
        "android.databinding.BindingAdapter",
        "android.databinding.InverseBindingMethods",
        "android.databinding.InverseBindingAdapter",
        "android.databinding.InverseMethod",
        "android.databinding.Untaggable",
        "android.databinding.BindingMethods",
        "android.databinding.BindingConversion",
        "android.databinding.BindingBuildInfo"}
)
/**
 * Parent annotation processor that dispatches sub steps to ensure execution order.
 * Use initProcessingSteps to add a new step.
 */
public class ProcessDataBinding extends AbstractProcessor {
    private List mProcessingSteps;
    private DataBindingCompilerArgs mCompilerArgs;
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        if (mProcessingSteps == null) {
            readArguments();
            initProcessingSteps();
        }
        if (mCompilerArgs == null) {
            return false;
        }
        if (mCompilerArgs.isTestVariant() && !mCompilerArgs.isEnabledForTests() &&
                !mCompilerArgs.isLibrary()) {
            L.d("data binding processor is invoked but not enabled, skipping...");
            return false;
        }
        boolean done = true;
        for (ProcessingStep step : mProcessingSteps) {
            try {
                done = step.runStep(roundEnv, processingEnv, mCompilerArgs) && done;
            } catch (JAXBException e) {
                L.e(e, "Exception while handling step %s", step);
            }
        }
        if (roundEnv.processingOver()) {
            for (ProcessingStep step : mProcessingSteps) {
                step.onProcessingOver(roundEnv, processingEnv, mCompilerArgs);
            }
        }
        Scope.assertNoError();
        return done;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latest();
    }

    private void initProcessingSteps() {
        final ProcessBindable processBindable = new ProcessBindable();
        mProcessingSteps = Arrays.asList(
                new ProcessMethodAdapters(),
                new ProcessExpressions(),
                processBindable
        );
        Callback dataBinderWriterCallback = new Callback() {
            CompilerChef mChef;
            BRWriter mBRWriter;

            @Override
            public void onChefReady(CompilerChef chef) {
                Preconditions.checkNull(mChef, "Cannot set compiler chef twice");
                chef.addBRVariables(processBindable);
                mChef = chef;
                considerWritingMapper();
                if (mCompilerArgs.isApp() != mCompilerArgs.isTestVariant() ||
                        mCompilerArgs.isEnabledForTests()) {
                    mChef.writeDynamicUtil();
                }
            }

            private void considerWritingMapper() {
                boolean justLibrary =
                        mCompilerArgs.artifactType() == DataBindingCompilerArgs.Type.LIBRARY &&
                        !mCompilerArgs.isTestVariant();
                if (justLibrary || mChef == null || mBRWriter == null) {
                    return;
                }
                mChef.writeDataBinderMapper(mCompilerArgs, mBRWriter);
            }

            @Override
            public void onBrWriterReady(BRWriter brWriter) {
                Preconditions.checkNull(mBRWriter, "Cannot set br writer twice");
                mBRWriter = brWriter;
                considerWritingMapper();
            }
        };
        AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
        for (ProcessingStep step : mProcessingSteps) {
            step.mJavaFileWriter = javaFileWriter;
            step.mCallback = dataBinderWriterCallback;
        }
    }

    /**
     * use this instead of init method so that we won't become a problem when data binding happens
     * to be in annotation processor classpath by chance
     */
    public synchronized void readArguments() {
        try {
            mCompilerArgs = DataBindingCompilerArgs
                    .readFromOptions(processingEnv.getOptions());
            L.setDebugLog(mCompilerArgs.enableDebugLogs());
            ScopedException.encodeOutput(mCompilerArgs.shouldPrintEncodedErrorLogs());
        } catch (Throwable t) {
            String allParam = processingEnv.getOptions().entrySet().stream().map(
                    (entry) -> entry.getKey() + " : " + entry.getValue())
                    .collect(Collectors.joining("\n"));
            throw new RuntimeException("Failed to parse data binding compiler options. Params:\n"
                    + allParam, t);
        }
        GenerationalClassUtil.init(mCompilerArgs);
        ModelAnalyzer.setProcessingEnvironment(processingEnv);
    }

    @Override
    public Set getSupportedOptions() {
        return DataBindingCompilerArgs.ALL_PARAMS;
    }

    /**
     * To ensure execution order and binding build information, we use processing steps.
     */
    public abstract static class ProcessingStep {
        private boolean mDone;
        private JavaFileWriter mJavaFileWriter;
        protected Callback mCallback;

        protected JavaFileWriter getWriter() {
            return mJavaFileWriter;
        }

        private boolean runStep(RoundEnvironment roundEnvironment,
                ProcessingEnvironment processingEnvironment,
                DataBindingCompilerArgs args) throws JAXBException {
            if (mDone) {
                return true;
            }
            mDone = onHandleStep(roundEnvironment, processingEnvironment, args);
            return mDone;
        }

        /**
         * Invoked in each annotation processing step.
         *
         * @return True if it is done and should never be invoked again.
         */
        abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
                ProcessingEnvironment processingEnvironment,
                DataBindingCompilerArgs args) throws JAXBException;

        /**
         * Invoked when processing is done. A good place to generate the output if the
         * processor requires multiple steps.
         */
        abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
                ProcessingEnvironment processingEnvironment,
                DataBindingCompilerArgs args);
    }

    interface Callback {
        void onChefReady(CompilerChef chef);
        void onBrWriterReady(BRWriter brWriter);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy