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

android.databinding.tool.LayoutBinder Maven / Gradle / Ivy

/*
 * 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.tool;

import android.databinding.tool.expr.Dependency;
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
import android.databinding.tool.expr.IdentifierExpr;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.scopes.FileScopeProvider;
import android.databinding.tool.store.Location;
import android.databinding.tool.store.ResourceBundle;
import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.LayoutBinderWriter;
import android.databinding.tool.writer.LayoutBinderWriterKt;

import org.antlr.v4.runtime.misc.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * Keeps all information about the bindings per layout file
 */
public class LayoutBinder implements FileScopeProvider {
    private static final Comparator COMPARE_FIELD_NAME = new Comparator() {
        @Override
        public int compare(BindingTarget first, BindingTarget second) {
            final String fieldName1 = LayoutBinderWriterKt.getFieldName(first);
            final String fieldName2 = LayoutBinderWriterKt.getFieldName(second);
            return fieldName1.compareTo(fieldName2);
        }
    };

    /*
    * val pkg: String, val projectPackage: String, val baseClassName: String,
        val layoutName:String, val lb: LayoutExprBinding*/
    private final ExprModel mExprModel;
    private final ExpressionParser mExpressionParser;
    private final List mBindingTargets;
    private final List mSortedBindingTargets;
    private String mModulePackage;
    private final HashMap mUserDefinedVariables = new HashMap();

    private LayoutBinderWriter mWriter;
    private ResourceBundle.LayoutFileBundle mBundle;
    private static final String[] sJavaLangClasses = {
            "Deprecated",
            "Override",
            "SafeVarargs",
            "SuppressWarnings",
            "Appendable",
            "AutoCloseable",
            "CharSequence",
            "Cloneable",
            "Comparable",
            "Iterable",
            "Readable",
            "Runnable",
            "Thread.UncaughtExceptionHandler",
            "Boolean",
            "Byte",
            "Character",
            "Character.Subset",
            "Character.UnicodeBlock",
            "Class",
            "ClassLoader",
            "Compiler",
            "Double",
            "Enum",
            "Float",
            "InheritableThreadLocal",
            "Integer",
            "Long",
            "Math",
            "Number",
            "Object",
            "Package",
            "Process",
            "ProcessBuilder",
            "Runtime",
            "RuntimePermission",
            "SecurityManager",
            "Short",
            "StackTraceElement",
            "StrictMath",
            "String",
            "StringBuffer",
            "StringBuilder",
            "System",
            "Thread",
            "ThreadGroup",
            "ThreadLocal",
            "Throwable",
            "Void",
            "Thread.State",
            "ArithmeticException",
            "ArrayIndexOutOfBoundsException",
            "ArrayStoreException",
            "ClassCastException",
            "ClassNotFoundException",
            "CloneNotSupportedException",
            "EnumConstantNotPresentException",
            "Exception",
            "IllegalAccessException",
            "IllegalArgumentException",
            "IllegalMonitorStateException",
            "IllegalStateException",
            "IllegalThreadStateException",
            "IndexOutOfBoundsException",
            "InstantiationException",
            "InterruptedException",
            "NegativeArraySizeException",
            "NoSuchFieldException",
            "NoSuchMethodException",
            "NullPointerException",
            "NumberFormatException",
            "ReflectiveOperationException",
            "RuntimeException",
            "SecurityException",
            "StringIndexOutOfBoundsException",
            "TypeNotPresentException",
            "UnsupportedOperationException",
            "AbstractMethodError",
            "AssertionError",
            "ClassCircularityError",
            "ClassFormatError",
            "Error",
            "ExceptionInInitializerError",
            "IllegalAccessError",
            "IncompatibleClassChangeError",
            "InstantiationError",
            "InternalError",
            "LinkageError",
            "NoClassDefFoundError",
            "NoSuchFieldError",
            "NoSuchMethodError",
            "OutOfMemoryError",
            "StackOverflowError",
            "ThreadDeath",
            "UnknownError",
            "UnsatisfiedLinkError",
            "UnsupportedClassVersionError",
            "VerifyError",
            "VirtualMachineError",
    };

    public LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle) {
        try {
            Scope.enter(this);
            mExprModel = new ExprModel();
            mExpressionParser = new ExpressionParser(mExprModel);
            mBindingTargets = new ArrayList();
            mBundle = layoutBundle;
            mModulePackage = layoutBundle.getModulePackage();
            HashSet names = new HashSet();
            // copy over data.
            for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) {
                addVariable(variable.name, variable.type, variable.location, variable.declared);
                names.add(variable.name);
            }

            for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
                mExprModel.addImport(userImport.name, userImport.type, userImport.location);
                names.add(userImport.name);
            }
            if (!names.contains("context")) {
                mExprModel.builtInVariable("context", "android.content.Context",
                        "getRoot().getContext()");
                names.add("context");
            }
            for (String javaLangClass : sJavaLangClasses) {
                mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
            }
            // First resolve all the View fields
            // Ensure there are no conflicts with variable names
            for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
                try {
                    Scope.enter(targetBundle);
                    final BindingTarget bindingTarget = createBindingTarget(targetBundle);
                    if (bindingTarget.getId() != null) {
                        final String fieldName = LayoutBinderWriterKt.
                                getReadableName(bindingTarget);
                        if (names.contains(fieldName)) {
                            L.w("View field %s collides with a variable or import", fieldName);
                        } else {
                            names.add(fieldName);
                            mExprModel.viewFieldExpr(bindingTarget);
                        }
                    }
                } finally {
                    Scope.exit();
                }
            }

            for (BindingTarget bindingTarget : mBindingTargets) {
                try {
                    Scope.enter(bindingTarget.mBundle);
                    final String className = getPackage() + "." + getClassName();
                    for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle
                            .getBindingBundleList()) {
                        try {
                            Scope.enter(bindingBundle.getValueLocation());
                            Expr expr = parse(bindingBundle.getExpr(),
                                    bindingBundle.getValueLocation());
                            bindingTarget.addBinding(bindingBundle.getName(), expr);
                            if (bindingBundle.isTwoWay()) {
                                bindingTarget.addInverseBinding(bindingBundle.getName(), expr,
                                        className);
                            }
                        } finally {
                            Scope.exit();
                        }
                    }
                    // resolve callbacks first because they introduce local variables.
                    bindingTarget.resolveCallbackParams();
                    bindingTarget.resolveTwoWayExpressions();
                    bindingTarget.resolveMultiSetters();
                    bindingTarget.resolveListeners();
                } finally {
                    Scope.exit();
                }
            }
            mSortedBindingTargets = new ArrayList(mBindingTargets);
            Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
        } finally {
            Scope.exit();
        }
    }

    public void resolveWhichExpressionsAreUsed() {
        List used = new ArrayList();
        for (BindingTarget target : mBindingTargets) {
            for (Binding binding : target.getBindings()) {
                binding.getExpr().markAsUsed();
                used.add(binding.getExpr());
            }
        }
        while (!used.isEmpty()) {
            Expr e = used.remove(used.size() - 1);
            for (Dependency dep : e.getDependencies()) {
                if (!dep.getOther().isUsed()) {
                    used.add(dep.getOther());
                    dep.getOther().markAsUsed();
                }
            }
        }
    }

    public IdentifierExpr addVariable(String name, String type, Location location,
            boolean declared) {
        Preconditions.check(!mUserDefinedVariables.containsKey(name),
                "%s has already been defined as %s", name, type);
        final IdentifierExpr id = mExprModel.identifier(name);
        id.setUserDefinedType(type);
        id.enableDirectInvalidation();
        if (location != null) {
            id.addLocation(location);
        }
        mUserDefinedVariables.put(name, type);
        if (declared) {
            id.setDeclared();
        }
        return id;
    }

    public HashMap getUserDefinedVariables() {
        return mUserDefinedVariables;
    }

    public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
        final BindingTarget target = new BindingTarget(targetBundle);
        mBindingTargets.add(target);
        target.setModel(mExprModel);
        return target;
    }

    public Expr parse(String input, @Nullable Location locationInFile) {
        final Expr parsed = mExpressionParser.parse(input, locationInFile);
        parsed.setBindingExpression(true);
        return parsed;
    }

    public List getBindingTargets() {
        return mBindingTargets;
    }

    public List getSortedTargets() {
        return mSortedBindingTargets;
    }

    public boolean isEmpty() {
        return mExprModel.size() == 0;
    }

    public ExprModel getModel() {
        return mExprModel;
    }

    private void ensureWriter() {
        if (mWriter == null) {
            mWriter = new LayoutBinderWriter(this);
        }
    }

    public void sealModel() {
        mExprModel.seal();
    }

    public String writeViewBinderBaseClass(boolean forLibrary) {
        ensureWriter();
        return mWriter.writeBaseClass(forLibrary);
    }

    public String writeViewBinder(int minSdk) {
        ensureWriter();
        Preconditions.checkNotNull(getPackage(), "package cannot be null");
        Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
        return mWriter.write(minSdk);
    }

    public String getPackage() {
        return mBundle.getBindingClassPackage();
    }

    public boolean isMerge() {
        return mBundle.isMerge();
    }

    public String getModulePackage() {
        return mModulePackage;
    }

    public String getLayoutname() {
        return mBundle.getFileName();
    }

    public String getImplementationName() {
        if (hasVariations()) {
            return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
        } else {
            return mBundle.getBindingClassName();
        }
    }
    
    public String getClassName() {
        return mBundle.getBindingClassName();
    }

    public String getTag() {
        return mBundle.getDirectory() + "/" + mBundle.getFileName();
    }

    public boolean hasVariations() {
        return mBundle.hasVariations();
    }

    @Override
    public String provideScopeFilePath() {
        return mBundle.getAbsoluteFilePath();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy