Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f73037ae authored by Qiwen Zhao's avatar Qiwen Zhao
Browse files

remove data-binding from base/tools. it should not be here since it depends on support lib.

Change-Id: Iac90ab2fb797617429a6a4e1e429f085455154e2
parent c8f786b1
Loading
Loading
Loading
Loading
+0 −54
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.
 */

apply plugin: 'java'
apply plugin: 'maven'

sourceCompatibility = 1.7
targetCompatibility = 1.7

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
    }
}

repositories {
    mavenCentral()
}

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
        }
    }
}

dependencies {
    compile project(":baseLibrary")
    compile project(":compiler")
    compile 'commons-codec:commons-codec:1.10'
}

uploadArchives {
    repositories {
        mavenDeployer {
            pom.artifactId = 'annotationprocessor'
        }
    }
}
+0 −41
Original line number Diff line number Diff line
/*
 * 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 java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;

class AnnotationUtil {

    /**
     * Returns only the elements that are annotated with the given class. For some reason
     * RoundEnvironment is returning elements annotated by other annotations.
     */
    static List<Element> getElementsAnnotatedWith(RoundEnvironment roundEnv,
            Class<? extends Annotation> annotationClass) {
        ArrayList<Element> elements = new ArrayList<>();
        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
            if (element.getAnnotation(annotationClass) != null) {
                elements.add(element);
            }
        }
        return elements;
    }
}
+0 −51
Original line number Diff line number Diff line
/*
 * 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 com.google.common.base.Preconditions;

import android.databinding.BindingBuildInfo;

import java.lang.annotation.Annotation;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;

public class BuildInfoUtil {
    private static BindingBuildInfo sCached;
    public static BindingBuildInfo load(RoundEnvironment roundEnvironment) {
        if (sCached == null) {
            sCached = extractNotNull(roundEnvironment, BindingBuildInfo.class);
        }
        return sCached;
    }

    private static <T extends Annotation> T extractNotNull(RoundEnvironment roundEnv,
            Class<T> annotationClass) {
        T result = null;
        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
            final T info = element.getAnnotation(annotationClass);
            if (info == null) {
                continue; // It gets confused between BindingAppInfo and BinderBundle
            }
            Preconditions.checkState(result == null, "Should have only one %s",
                    annotationClass.getCanonicalName());
            result = info;
        }
        return result;
    }
}
+0 −304
Original line number Diff line number Diff line
/*
 * 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 com.google.common.base.Preconditions;

import android.databinding.Bindable;
import android.databinding.BindingBuildInfo;
import android.databinding.tool.CompilerChef.BindableHolder;
import android.databinding.tool.util.GenerationalClassUtil;
import android.databinding.tool.util.L;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;

// binding app info and library info are necessary to trigger this.
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ProcessBindable extends ProcessDataBinding.ProcessingStep implements BindableHolder {
    private static final String INTERMEDIATE_FILE_EXT = "-br.bin";
    Intermediate mProperties;
    HashMap<String, HashSet<String>> mLayoutVariables = new HashMap<>();

    @Override
    public boolean onHandleStep(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
            BindingBuildInfo buildInfo) {
        if (mProperties == null) {
            mProperties = new IntermediateV1(buildInfo.modulePackage());
            mergeLayoutVariables();
            mLayoutVariables.clear();
            for (Element element : AnnotationUtil
                    .getElementsAnnotatedWith(roundEnv, Bindable.class)) {
                Element enclosingElement = element.getEnclosingElement();
                ElementKind kind = enclosingElement.getKind();
                if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
                    L.e("Bindable must be on a member field or method. The enclosing type is %s",
                            enclosingElement.getKind());
                }
                TypeElement enclosing = (TypeElement) enclosingElement;
                String name = getPropertyName(element);
                if (name != null) {
                    Preconditions
                            .checkNotNull(mProperties, "Must receive app / library info before "
                                    + "Bindable fields.");
                    mProperties.addProperty(enclosing.getQualifiedName().toString(), name);
                }
            }
            if (mProperties.hasValues()) {
                GenerationalClassUtil.writeIntermediateFile(processingEnv,
                        mProperties.getPackage(),
                        createIntermediateFileName(mProperties.getPackage()), mProperties);
                generateBRClasses(!buildInfo.isLibrary(), mProperties.getPackage());
            }
        }
        return false;
    }

    @Override
    public void addVariable(String variableName, String containingClassName) {
        HashSet<String> variableNames = mLayoutVariables.get(containingClassName);
        if (variableNames == null) {
            variableNames = new HashSet<>();
            mLayoutVariables.put(containingClassName, variableNames);
        }
        variableNames.add(variableName);
    }

    @Override
    public void onProcessingOver(RoundEnvironment roundEnvironment,
            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
    }

    private String createIntermediateFileName(String appPkg) {
        return appPkg + INTERMEDIATE_FILE_EXT;
    }

    private void generateBRClasses(boolean useFinalFields, String pkg) {
        L.d("************* Generating BR file %s. use final: %s", pkg, useFinalFields);
        HashSet<String> properties = new HashSet<>();
        mProperties.captureProperties(properties);
        List<Intermediate> previousIntermediates = loadPreviousBRFiles();
        for (Intermediate intermediate : previousIntermediates) {
            intermediate.captureProperties(properties);
        }
        writeBRClass(useFinalFields, pkg, properties);
        if (useFinalFields) {
            // generate BR for all previous packages
            for (Intermediate intermediate : previousIntermediates) {
                writeBRClass(true, intermediate.getPackage(),
                        properties);
            }
        }
    }

    private void writeBRClass(boolean useFinalFields, String pkg, HashSet<String> properties) {
        ArrayList<String> sortedProperties = new ArrayList<String>();
        sortedProperties.addAll(properties);
        Collections.sort(sortedProperties);
        StringBuilder out = new StringBuilder();
        String modifier = "public static " + (useFinalFields ? "final" : "") + " int ";
        out.append("package " + pkg + ";\n\n" +
                        "public class BR {\n" +
                        "    " + modifier + "_all = 0;\n"
        );
        int id = 0;
        for (String property : sortedProperties) {
            id++;
            out.append("    " + modifier + property + " = " + id + ";\n");
        }
        out.append("    public static int getId(String key) {\n");
        out.append("        switch(key) {\n");
        id = 0;
        for (String property : sortedProperties) {
            id++;
            out.append("            case \"" + property + "\": return " + id + ";\n");
        }
        out.append("        }\n");
        out.append("        return -1;\n");
        out.append("    }");
        out.append("}\n");

        getWriter().writeToFile(pkg + ".BR", out.toString() );
    }

    private String getPropertyName(Element element) {
        switch (element.getKind()) {
            case FIELD:
                return stripPrefixFromField((VariableElement) element);
            case METHOD:
                return stripPrefixFromMethod((ExecutableElement) element);
            default:
                L.e("@Bindable is not allowed on %s", element.getKind());
                return null;
        }
    }

    private static String stripPrefixFromField(VariableElement element) {
        Name name = element.getSimpleName();
        if (name.length() >= 2) {
            char firstChar = name.charAt(0);
            char secondChar = name.charAt(1);
            if (name.length() > 2 && firstChar == 'm' && secondChar == '_') {
                char thirdChar = name.charAt(2);
                if (Character.isJavaIdentifierStart(thirdChar)) {
                    return "" + Character.toLowerCase(thirdChar) +
                            name.subSequence(3, name.length());
                }
            } else if ((firstChar == 'm' && Character.isUpperCase(secondChar)) ||
                    (firstChar == '_' && Character.isJavaIdentifierStart(secondChar))) {
                return "" + Character.toLowerCase(secondChar) + name.subSequence(2, name.length());
            }
        }
        return name.toString();
    }

    private String stripPrefixFromMethod(ExecutableElement element) {
        Name name = element.getSimpleName();
        CharSequence propertyName;
        if (isGetter(element) || isSetter(element)) {
            propertyName = name.subSequence(3, name.length());
        } else if (isBooleanGetter(element)) {
            propertyName = name.subSequence(2, name.length());
        } else {
            L.e("@Bindable associated with method must follow JavaBeans convention %s", element);
            return null;
        }
        char firstChar = propertyName.charAt(0);
        return "" + Character.toLowerCase(firstChar) +
                propertyName.subSequence(1, propertyName.length());
    }

    private void mergeLayoutVariables() {
        for (String containingClass : mLayoutVariables.keySet()) {
            for (String variable : mLayoutVariables.get(containingClass)) {
                mProperties.addProperty(containingClass, variable);
            }
        }
    }

    private static boolean prefixes(CharSequence sequence, String prefix) {
        boolean prefixes = false;
        if (sequence.length() > prefix.length()) {
            int count = prefix.length();
            prefixes = true;
            for (int i = 0; i < count; i++) {
                if (sequence.charAt(i) != prefix.charAt(i)) {
                    prefixes = false;
                    break;
                }
            }
        }
        return prefixes;
    }

    private static boolean isGetter(ExecutableElement element) {
        Name name = element.getSimpleName();
        return prefixes(name, "get") &&
                Character.isJavaIdentifierStart(name.charAt(3)) &&
                element.getParameters().isEmpty() &&
                element.getReturnType().getKind() != TypeKind.VOID;
    }

    private static boolean isSetter(ExecutableElement element) {
        Name name = element.getSimpleName();
        return prefixes(name, "set") &&
                Character.isJavaIdentifierStart(name.charAt(3)) &&
                element.getParameters().size() == 1 &&
                element.getReturnType().getKind() == TypeKind.VOID;
    }

    private static boolean isBooleanGetter(ExecutableElement element) {
        Name name = element.getSimpleName();
        return prefixes(name, "is") &&
                Character.isJavaIdentifierStart(name.charAt(2)) &&
                element.getParameters().isEmpty() &&
                element.getReturnType().getKind() == TypeKind.BOOLEAN;
    }

    private List<Intermediate> loadPreviousBRFiles() {
        return GenerationalClassUtil
                .loadObjects(getClass().getClassLoader(),
                        new GenerationalClassUtil.ExtensionFilter(INTERMEDIATE_FILE_EXT));
    }

    private interface Intermediate extends Serializable {

        void captureProperties(Set<String> properties);

        void addProperty(String className, String propertyName);

        boolean hasValues();

        String getPackage();
    }

    private static class IntermediateV1 implements Serializable, Intermediate {

        private static final long serialVersionUID = 2L;

        private String mPackage;
        private final HashMap<String, HashSet<String>> mProperties = new HashMap<>();

        public IntermediateV1(String aPackage) {
            mPackage = aPackage;
        }

        @Override
        public void captureProperties(Set<String> properties) {
            for (HashSet<String> propertySet : mProperties.values()) {
                properties.addAll(propertySet);
            }
        }

        @Override
        public void addProperty(String className, String propertyName) {
            HashSet<String> properties = mProperties.get(className);
            if (properties == null) {
                properties = new HashSet<>();
                mProperties.put(className, properties);
            }
            properties.add(propertyName);
        }

        @Override
        public boolean hasValues() {
            return !mProperties.isEmpty();
        }

        @Override
        public String getPackage() {
            return mPackage;
        }
    }
}
+0 −128
Original line number Diff line number Diff line
/*
 * 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.reflection.ModelAnalyzer;
import android.databinding.tool.writer.AnnotationJavaFileWriter;
import android.databinding.tool.writer.JavaFileWriter;

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

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes({
        "android.databinding.BindingAdapter",
        "android.databinding.Untaggable",
        "android.databinding.BindingMethods",
        "android.databinding.BindingConversion",
        "android.databinding.BindingBuildInfo"}
)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
/**
 * 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<ProcessingStep> mProcessingSteps;
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (mProcessingSteps == null) {
            initProcessingSteps();
        }
        final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
        if (buildInfo == null) {
            return false;
        }
        boolean done = true;
        for (ProcessingStep step : mProcessingSteps) {
            done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
        }
        if (roundEnv.processingOver()) {
            for (ProcessingStep step : mProcessingSteps) {
                step.onProcessingOver(roundEnv, processingEnv, buildInfo);
            }
        }
        return done;
    }

    private void initProcessingSteps() {
        ProcessBindable processBindable = new ProcessBindable();
        mProcessingSteps = Arrays.asList(
                new ProcessMethodAdapters(),
                new ProcessExpressions(processBindable),
                processBindable
        );
        AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
        for (ProcessingStep step : mProcessingSteps) {
            step.mJavaFileWriter = javaFileWriter;
        }
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        ModelAnalyzer.setProcessingEnvironment(processingEnv);
    }

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

        protected JavaFileWriter getWriter() {
            return mJavaFileWriter;
        }

        private boolean runStep(RoundEnvironment roundEnvironment,
                ProcessingEnvironment processingEnvironment,
                BindingBuildInfo buildInfo) {
            if (mDone) {
                return true;
            }
            mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo);
            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,
                BindingBuildInfo buildInfo);

        /**
         * 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,
                BindingBuildInfo buildInfo);
    }
}
Loading