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

android.databinding.tool.store.ResourceBundle 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.store;

import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.ScopedException;
import android.databinding.tool.processing.scopes.FileScopeProvider;
import android.databinding.tool.processing.scopes.LocationScopeProvider;
import android.databinding.tool.util.L;
import android.databinding.tool.util.ParserHelper;
import android.databinding.tool.util.Preconditions;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * This is a serializable class that can keep the result of parsing layout files.
 */
public class ResourceBundle implements Serializable {
    private static final String[] ANDROID_VIEW_PACKAGE_VIEWS = new String[]
            {"View", "ViewGroup", "ViewStub", "TextureView", "SurfaceView"};
    private String mAppPackage;

    private HashMap> mLayoutBundles
            = new HashMap>();

    private List mRemovedFiles = new ArrayList();

    public ResourceBundle(String appPackage) {
        mAppPackage = appPackage;
    }

    public void addLayoutBundle(LayoutFileBundle bundle) {
        if (bundle.mFileName == null) {
            L.e("File bundle must have a name. %s does not have one.", bundle);
            return;
        }
        if (!mLayoutBundles.containsKey(bundle.mFileName)) {
            mLayoutBundles.put(bundle.mFileName, new ArrayList());
        }
        final List bundles = mLayoutBundles.get(bundle.mFileName);
        for (LayoutFileBundle existing : bundles) {
            if (existing.equals(bundle)) {
                L.d("skipping layout bundle %s because it already exists.", bundle);
                return;
            }
        }
        L.d("adding bundle %s", bundle);
        bundles.add(bundle);
    }

    public HashMap> getLayoutBundles() {
        return mLayoutBundles;
    }

    public String getAppPackage() {
        return mAppPackage;
    }

    public void validateMultiResLayouts() {
        for (List layoutFileBundles : mLayoutBundles.values()) {
            for (LayoutFileBundle layoutFileBundle : layoutFileBundles) {
                List unboundIncludes = new ArrayList();
                for (BindingTargetBundle target : layoutFileBundle.getBindingTargetBundles()) {
                    if (target.isBinder()) {
                        List boundTo =
                                mLayoutBundles.get(target.getIncludedLayout());
                        if (boundTo == null || boundTo.isEmpty()) {
                            L.d("There is no binding for %s, reverting to plain layout",
                                    target.getIncludedLayout());
                            if (target.getId() == null) {
                                unboundIncludes.add(target);
                            } else {
                                target.setIncludedLayout(null);
                                target.setInterfaceType("android.view.View");
                                target.mViewName = "android.view.View";
                            }
                        } else {
                            String binding = boundTo.get(0).getFullBindingClass();
                            target.setInterfaceType(binding);
                        }
                    }
                }
                layoutFileBundle.getBindingTargetBundles().removeAll(unboundIncludes);
            }
        }

        for (Map.Entry> bundles : mLayoutBundles.entrySet()) {
            if (bundles.getValue().size() < 2) {
                continue;
            }

            // validate all ids are in correct view types
            // and all variables have the same name
            for (LayoutFileBundle bundle : bundles.getValue()) {
                bundle.mHasVariations = true;
            }
            String bindingClass = validateAndGetSharedClassName(bundles.getValue());
            Map variableTypes = validateAndMergeNameTypeLocations(
                    bundles.getValue(), ErrorMessages.MULTI_CONFIG_VARIABLE_TYPE_MISMATCH,
                    new ValidateAndFilterCallback() {
                        @Override
                        public List get(LayoutFileBundle bundle) {
                            return bundle.mVariables;
                        }
                    });

            Map importTypes = validateAndMergeNameTypeLocations(
                    bundles.getValue(), ErrorMessages.MULTI_CONFIG_IMPORT_TYPE_MISMATCH,
                    new ValidateAndFilterCallback() {
                        @Override
                        public List get(LayoutFileBundle bundle) {
                            return bundle.mImports;
                        }
                    });

            for (LayoutFileBundle bundle : bundles.getValue()) {
                // now add missing ones to each to ensure they can be referenced
                L.d("checking for missing variables in %s / %s", bundle.mFileName,
                        bundle.mConfigName);
                for (Map.Entry variable : variableTypes.entrySet()) {
                    if (!NameTypeLocation.contains(bundle.mVariables, variable.getKey())) {
                        NameTypeLocation orig = variable.getValue();
                        bundle.addVariable(orig.name, orig.type, orig.location, false);
                        L.d("adding missing variable %s to %s / %s", variable.getKey(),
                                bundle.mFileName, bundle.mConfigName);
                    }
                }
                for (Map.Entry userImport : importTypes.entrySet()) {
                    if (!NameTypeLocation.contains(bundle.mImports, userImport.getKey())) {
                        bundle.mImports.add(userImport.getValue());
                        L.d("adding missing import %s to %s / %s", userImport.getKey(),
                                bundle.mFileName, bundle.mConfigName);
                    }
                }
            }

            Set includeBindingIds = new HashSet();
            Set viewBindingIds = new HashSet();
            Map viewTypes = new HashMap();
            Map includes = new HashMap();
            L.d("validating ids for %s", bundles.getKey());
            Set conflictingIds = new HashSet();
            for (LayoutFileBundle bundle : bundles.getValue()) {
                try {
                    Scope.enter(bundle);
                    for (BindingTargetBundle target : bundle.mBindingTargetBundles) {
                        try {
                            Scope.enter(target);
                            L.d("checking %s %s %s", target.getId(), target.getFullClassName(),
                                    target.isBinder());
                            if (target.mId != null) {
                                if (target.isBinder()) {
                                    if (viewBindingIds.contains(target.mId)) {
                                        L.d("%s is conflicting", target.mId);
                                        conflictingIds.add(target.mId);
                                        continue;
                                    }
                                    includeBindingIds.add(target.mId);
                                } else {
                                    if (includeBindingIds.contains(target.mId)) {
                                        L.d("%s is conflicting", target.mId);
                                        conflictingIds.add(target.mId);
                                        continue;
                                    }
                                    viewBindingIds.add(target.mId);
                                }
                                String existingType = viewTypes.get(target.mId);
                                if (existingType == null) {
                                    L.d("assigning %s as %s", target.getId(),
                                            target.getFullClassName());
                                            viewTypes.put(target.mId, target.getFullClassName());
                                    if (target.isBinder()) {
                                        includes.put(target.mId, target.getIncludedLayout());
                                    }
                                } else if (!existingType.equals(target.getFullClassName())) {
                                    if (target.isBinder()) {
                                        L.d("overriding %s as base binder", target.getId());
                                        viewTypes.put(target.mId,
                                                "android.databinding.ViewDataBinding");
                                        includes.put(target.mId, target.getIncludedLayout());
                                    } else {
                                        L.d("overriding %s as base view", target.getId());
                                        viewTypes.put(target.mId, "android.view.View");
                                    }
                                }
                            }
                        } catch (ScopedException ex) {
                            Scope.defer(ex);
                        } finally {
                            Scope.exit();
                        }
                    }
                } finally {
                    Scope.exit();
                }
            }

            if (!conflictingIds.isEmpty()) {
                for (LayoutFileBundle bundle : bundles.getValue()) {
                    for (BindingTargetBundle target : bundle.mBindingTargetBundles) {
                        if (conflictingIds.contains(target.mId)) {
                            Scope.registerError(String.format(
                                            ErrorMessages.MULTI_CONFIG_ID_USED_AS_IMPORT,
                                            target.mId), bundle, target);
                        }
                    }
                }
            }

            for (LayoutFileBundle bundle : bundles.getValue()) {
                try {
                    Scope.enter(bundle);
                    for (Map.Entry viewType : viewTypes.entrySet()) {
                        BindingTargetBundle target = bundle.getBindingTargetById(viewType.getKey());
                        if (target == null) {
                            String include = includes.get(viewType.getKey());
                            if (include == null) {
                                bundle.createBindingTarget(viewType.getKey(), viewType.getValue(),
                                        false, null, null, null);
                            } else {
                                BindingTargetBundle bindingTargetBundle = bundle
                                        .createBindingTarget(
                                                viewType.getKey(), null, false, null, null, null);
                                bindingTargetBundle
                                        .setIncludedLayout(includes.get(viewType.getKey()));
                                bindingTargetBundle.setInterfaceType(viewType.getValue());
                            }
                        } else {
                            L.d("setting interface type on %s (%s) as %s", target.mId,
                                    target.getFullClassName(), viewType.getValue());
                            target.setInterfaceType(viewType.getValue());
                        }
                    }
                } catch (ScopedException ex) {
                    Scope.defer(ex);
                } finally {
                    Scope.exit();
                }
            }
        }
        // assign class names to each
        for (Map.Entry> entry : mLayoutBundles.entrySet()) {
            for (LayoutFileBundle bundle : entry.getValue()) {
                final String configName;
                if (bundle.hasVariations()) {
                    // append configuration specifiers.
                    final String parentFileName = bundle.mDirectory;
                    L.d("parent file for %s is %s", bundle.getFileName(), parentFileName);
                    if ("layout".equals(parentFileName)) {
                        configName = "";
                    } else {
                        configName = ParserHelper.toClassName(parentFileName.substring("layout-".length()));
                    }
                } else {
                    configName = "";
                }
                bundle.mConfigName = configName;
            }
        }
    }

    /**
     * Receives a list of bundles which are representations of the same layout file in different
     * configurations.
     * @param bundles
     * @return The map for variables and their types
     */
    private Map validateAndMergeNameTypeLocations(
            List bundles, String errorMessage,
            ValidateAndFilterCallback callback) {
        Map result = new HashMap();
        Set mismatched = new HashSet();
        for (LayoutFileBundle bundle : bundles) {
            for (NameTypeLocation item : callback.get(bundle)) {
                NameTypeLocation existing = result.get(item.name);
                if (existing != null && !existing.type.equals(item.type)) {
                    mismatched.add(item.name);
                    continue;
                }
                result.put(item.name, item);
            }
        }
        if (mismatched.isEmpty()) {
            return result;
        }
        // create exceptions. We could get more clever and find the outlier but for now, listing
        // each file w/ locations seems enough
        for (String mismatch : mismatched) {
            for (LayoutFileBundle bundle : bundles) {
                NameTypeLocation found = null;
                for (NameTypeLocation item : callback.get(bundle)) {
                    if (mismatch.equals(item.name)) {
                        found = item;
                        break;
                    }
                }
                if (found == null) {
                    // variable is not defined in this layout, continue
                    continue;
                }
                Scope.registerError(String.format(
                                errorMessage, found.name, found.type,
                                bundle.mDirectory + "/" + bundle.getFileName()), bundle,
                        found.location.createScope());
            }
        }
        return result;
    }

    /**
     * Receives a list of bundles which are representations of the same layout file in different
     * configurations.
     * @param bundles
     * @return The shared class name for these bundles
     */
    private String validateAndGetSharedClassName(List bundles) {
        String sharedClassName = null;
        boolean hasMismatch = false;
        for (LayoutFileBundle bundle : bundles) {
            bundle.mHasVariations = true;
            String fullBindingClass = bundle.getFullBindingClass();
            if (sharedClassName == null) {
                sharedClassName = fullBindingClass;
            } else if (!sharedClassName.equals(fullBindingClass)) {
                hasMismatch = true;
                break;
            }
        }
        if (!hasMismatch) {
            return sharedClassName;
        }
        // generate proper exceptions for each
        for (LayoutFileBundle bundle : bundles) {
            Scope.registerError(String.format(ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH,
                    bundle.getFullBindingClass(), bundle.mDirectory + "/" + bundle.getFileName()),
                    bundle, bundle.getClassNameLocationProvider());
        }
        return sharedClassName;
    }

    public void addRemovedFile(File file) {
        mRemovedFiles.add(file);
    }

    public List getRemovedFiles() {
        return mRemovedFiles;
    }

    @XmlAccessorType(XmlAccessType.NONE)
    @XmlRootElement(name="Layout")
    public static class LayoutFileBundle implements Serializable, FileScopeProvider {
        @XmlAttribute(name="layout", required = true)
        public String mFileName;
        @XmlAttribute(name="modulePackage", required = true)
        public String mModulePackage;
        @XmlAttribute(name="absoluteFilePath", required = true)
        public String mAbsoluteFilePath;
        private String mConfigName;

        // The binding class as given by the user
        @XmlAttribute(name="bindingClass", required = false)
        public String mBindingClass;

        // The location of the name of the generated class, optional
        @XmlElement(name = "ClassNameLocation", required = false)
        private Location mClassNameLocation;
        // The full package and class name as determined from mBindingClass and mModulePackage
        private String mFullBindingClass;

        // The simple binding class name as determined from mBindingClass and mModulePackage
        private String mBindingClassName;

        // The package of the binding class as determined from mBindingClass and mModulePackage
        private String mBindingPackage;

        @XmlAttribute(name="directory", required = true)
        public String mDirectory;
        public boolean mHasVariations;

        @XmlElement(name="Variables")
        public List mVariables = new ArrayList();

        @XmlElement(name="Imports")
        public List mImports = new ArrayList();

        @XmlElementWrapper(name="Targets")
        @XmlElement(name="Target")
        public List mBindingTargetBundles = new ArrayList();

        @XmlAttribute(name="isMerge", required = true)
        private boolean mIsMerge;

        private LocationScopeProvider mClassNameLocationProvider;

        // for XML binding
        public LayoutFileBundle() {
        }

        /**
         * Updates configuration fields from the given bundle but does not change variables,
         * binding expressions etc.
         */
        public void inheritConfigurationFrom(LayoutFileBundle other) {
            mFileName = other.mFileName;
            mModulePackage = other.mModulePackage;
            mBindingClass = other.mBindingClass;
            mFullBindingClass = other.mFullBindingClass;
            mBindingClassName = other.mBindingClassName;
            mBindingPackage = other.mBindingPackage;
            mHasVariations = other.mHasVariations;
            mIsMerge = other.mIsMerge;
        }

        public LayoutFileBundle(File file, String fileName, String directory,
                String modulePackage, boolean isMerge) {
            mFileName = fileName;
            mDirectory = directory;
            mModulePackage = modulePackage;
            mIsMerge = isMerge;
            mAbsoluteFilePath = file.getAbsolutePath();
        }

        public LocationScopeProvider getClassNameLocationProvider() {
            if (mClassNameLocationProvider == null && mClassNameLocation != null
                    && mClassNameLocation.isValid()) {
                mClassNameLocationProvider = mClassNameLocation.createScope();
            }
            return mClassNameLocationProvider;
        }

        public void addVariable(String name, String type, Location location, boolean declared) {
            Preconditions.check(!NameTypeLocation.contains(mVariables, name),
                    "Cannot use same variable name twice. %s in %s", name, location);
            mVariables.add(new VariableDeclaration(name, type, location, declared));
        }

        public void addImport(String alias, String type, Location location) {
            Preconditions.check(!NameTypeLocation.contains(mImports, alias),
                    "Cannot import same alias twice. %s in %s", alias, location);
            mImports.add(new NameTypeLocation(alias, type, location));
        }

        public BindingTargetBundle createBindingTarget(String id, String viewName,
                boolean used, String tag, String originalTag, Location location) {
            BindingTargetBundle target = new BindingTargetBundle(id, viewName, used, tag,
                    originalTag, location);
            mBindingTargetBundles.add(target);
            return target;
        }

        public boolean isEmpty() {
            return mVariables.isEmpty() && mImports.isEmpty() && mBindingTargetBundles.isEmpty();
        }

        public BindingTargetBundle getBindingTargetById(String key) {
            for (BindingTargetBundle target : mBindingTargetBundles) {
                if (key.equals(target.mId)) {
                    return target;
                }
            }
            return null;
        }

        public String getFileName() {
            return mFileName;
        }

        public String getConfigName() {
            return mConfigName;
        }

        public String getDirectory() {
            return mDirectory;
        }

        public boolean hasVariations() {
            return mHasVariations;
        }

        public List getVariables() {
            return mVariables;
        }

        public List getImports() {
            return mImports;
        }

        public boolean isMerge() {
            return mIsMerge;
        }

        public String getBindingClassName() {
            if (mBindingClassName == null) {
                String fullClass = getFullBindingClass();
                int dotIndex = fullClass.lastIndexOf('.');
                mBindingClassName = fullClass.substring(dotIndex + 1);
            }
            return mBindingClassName;
        }

        public void setBindingClass(String bindingClass, Location location) {
            mBindingClass = bindingClass;
            mClassNameLocation = location;
        }

        public String getBindingClassPackage() {
            if (mBindingPackage == null) {
                String fullClass = getFullBindingClass();
                int dotIndex = fullClass.lastIndexOf('.');
                mBindingPackage = fullClass.substring(0, dotIndex);
            }
            return mBindingPackage;
        }

        private String getFullBindingClass() {
            if (mFullBindingClass == null) {
                if (mBindingClass == null) {
                    mFullBindingClass = getModulePackage() + ".databinding." +
                            ParserHelper.toClassName(getFileName()) + "Binding";
                } else if (mBindingClass.startsWith(".")) {
                    mFullBindingClass = getModulePackage() + mBindingClass;
                } else if (mBindingClass.indexOf('.') < 0) {
                    mFullBindingClass = getModulePackage() + ".databinding." + mBindingClass;
                } else {
                    mFullBindingClass = mBindingClass;
                }
            }
            return mFullBindingClass;
        }

        public List getBindingTargetBundles() {
            return mBindingTargetBundles;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            LayoutFileBundle bundle = (LayoutFileBundle) o;

            if (mConfigName != null ? !mConfigName.equals(bundle.mConfigName)
                    : bundle.mConfigName != null) {
                return false;
            }
            if (mDirectory != null ? !mDirectory.equals(bundle.mDirectory)
                    : bundle.mDirectory != null) {
                return false;
            }
            return !(mFileName != null ? !mFileName.equals(bundle.mFileName)
                    : bundle.mFileName != null);

        }

        @Override
        public int hashCode() {
            int result = mFileName != null ? mFileName.hashCode() : 0;
            result = 31 * result + (mConfigName != null ? mConfigName.hashCode() : 0);
            result = 31 * result + (mDirectory != null ? mDirectory.hashCode() : 0);
            return result;
        }

        @Override
        public String toString() {
            return "LayoutFileBundle{" +
                    "mHasVariations=" + mHasVariations +
                    ", mDirectory='" + mDirectory + '\'' +
                    ", mConfigName='" + mConfigName + '\'' +
                    ", mModulePackage='" + mModulePackage + '\'' +
                    ", mFileName='" + mFileName + '\'' +
                    '}';
        }

        public String getModulePackage() {
            return mModulePackage;
        }

        public String getAbsoluteFilePath() {
            return mAbsoluteFilePath;
        }

        @Override
        public String provideScopeFilePath() {
            return mAbsoluteFilePath;
        }

        private static Marshaller sMarshaller;
        private static Unmarshaller sUmarshaller;

        public String toXML() throws JAXBException {
            StringWriter writer = new StringWriter();
            getMarshaller().marshal(this, writer);
            return writer.getBuffer().toString();
        }

        public static LayoutFileBundle fromXML(InputStream inputStream) throws JAXBException {
            return (LayoutFileBundle) getUnmarshaller().unmarshal(inputStream);
        }

        private static Marshaller getMarshaller() throws JAXBException {
            if (sMarshaller == null) {
                JAXBContext context = JAXBContext
                        .newInstance(ResourceBundle.LayoutFileBundle.class);
                sMarshaller = context.createMarshaller();
            }
            return sMarshaller;
        }

        private static Unmarshaller getUnmarshaller() throws JAXBException {
            if (sUmarshaller == null) {
                JAXBContext context = JAXBContext
                        .newInstance(ResourceBundle.LayoutFileBundle.class);
                sUmarshaller = context.createUnmarshaller();
            }
            return sUmarshaller;
        }
    }

    @XmlAccessorType(XmlAccessType.NONE)
    public static class NameTypeLocation {
        @XmlAttribute(name="type", required = true)
        public String type;

        @XmlAttribute(name="name", required = true)
        public String name;

        @XmlElement(name="location", required = false)
        public Location location;

        public NameTypeLocation() {
        }

        public NameTypeLocation(String name, String type, Location location) {
            this.type = type;
            this.name = name;
            this.location = location;
        }

        @Override
        public String toString() {
            return "{" +
                    "type='" + type + '\'' +
                    ", name='" + name + '\'' +
                    ", location=" + location +
                    '}';
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            NameTypeLocation that = (NameTypeLocation) o;

            if (location != null ? !location.equals(that.location) : that.location != null) {
                return false;
            }
            if (!name.equals(that.name)) {
                return false;
            }
            return type.equals(that.type);

        }

        @Override
        public int hashCode() {
            int result = type.hashCode();
            result = 31 * result + name.hashCode();
            result = 31 * result + (location != null ? location.hashCode() : 0);
            return result;
        }

        public static boolean contains(List list, String name) {
            for (NameTypeLocation ntl : list) {
                if (name.equals(ntl.name)) {
                    return true;
                }
            }
            return false;
        }
    }

    @XmlAccessorType(XmlAccessType.NONE)
    public static class VariableDeclaration extends NameTypeLocation {
        @XmlAttribute(name="declared", required = false)
        public boolean declared;

        public VariableDeclaration() {

        }

        public VariableDeclaration(String name, String type, Location location, boolean declared) {
            super(name, type, location);
            this.declared = declared;
        }
    }

    public static class MarshalledMapType {
        public List entries;
    }

    @XmlAccessorType(XmlAccessType.NONE)
    public static class BindingTargetBundle implements Serializable, LocationScopeProvider {
        // public for XML serialization

        @XmlAttribute(name="id")
        public String mId;
        @XmlAttribute(name="tag", required = true)
        public String mTag;
        @XmlAttribute(name="originalTag")
        public String mOriginalTag;
        @XmlAttribute(name="view", required = false)
        public String mViewName;
        private String mFullClassName;
        public boolean mUsed = true;
        @XmlElementWrapper(name="Expressions")
        @XmlElement(name="Expression")
        public List mBindingBundleList = new ArrayList();
        @XmlAttribute(name="include")
        public String mIncludedLayout;
        @XmlElement(name="location")
        public Location mLocation;
        private String mInterfaceType;

        // For XML serialization
        public BindingTargetBundle() {}

        public BindingTargetBundle(String id, String viewName, boolean used,
                String tag, String originalTag, Location location) {
            mId = id;
            mViewName = viewName;
            mUsed = used;
            mTag = tag;
            mOriginalTag = originalTag;
            mLocation = location;
        }

        public void addBinding(String name, String expr, boolean isTwoWay, Location location,
                Location valueLocation) {
            mBindingBundleList.add(new BindingBundle(name, expr, isTwoWay, location, valueLocation));
        }

        public void setIncludedLayout(String includedLayout) {
            mIncludedLayout = includedLayout;
        }

        public String getIncludedLayout() {
            return mIncludedLayout;
        }

        public boolean isBinder() {
            return mIncludedLayout != null;
        }

        public void setInterfaceType(String interfaceType) {
            mInterfaceType = interfaceType;
        }

        public void setLocation(Location location) {
            mLocation = location;
        }

        public Location getLocation() {
            return mLocation;
        }

        public String getId() {
            return mId;
        }

        public String getTag() {
            return mTag;
        }

        public String getOriginalTag() {
            return mOriginalTag;
        }

        public String getFullClassName() {
            if (mFullClassName == null) {
                if (isBinder()) {
                    mFullClassName = mInterfaceType;
                } else if (mViewName.indexOf('.') == -1) {
                    if (Arrays.asList(ANDROID_VIEW_PACKAGE_VIEWS).contains(mViewName)) {
                        mFullClassName = "android.view." + mViewName;
                    } else if("WebView".equals(mViewName)) {
                        mFullClassName = "android.webkit." + mViewName;
                    } else {
                        mFullClassName = "android.widget." + mViewName;
                    }
                } else {
                    mFullClassName = mViewName;
                }
            }
            if (mFullClassName == null) {
                L.e("Unexpected full class name = null. view = %s, interface = %s, layout = %s",
                        mViewName, mInterfaceType, mIncludedLayout);
            }
            return mFullClassName;
        }

        public boolean isUsed() {
            return mUsed;
        }

        public List getBindingBundleList() {
            return mBindingBundleList;
        }

        public String getInterfaceType() {
            return mInterfaceType;
        }

        @Override
        public List provideScopeLocation() {
            return mLocation == null ? null : Arrays.asList(mLocation);
        }

        @XmlAccessorType(XmlAccessType.NONE)
        public static class BindingBundle implements Serializable {

            private String mName;
            private String mExpr;
            private Location mLocation;
            private Location mValueLocation;
            private boolean mIsTwoWay;

            public BindingBundle() {}

            public BindingBundle(String name, String expr, boolean isTwoWay, Location location,
                    Location valueLocation) {
                mName = name;
                mExpr = expr;
                mLocation = location;
                mIsTwoWay = isTwoWay;
                mValueLocation = valueLocation;
            }

            @XmlAttribute(name="attribute", required=true)
            public String getName() {
                return mName;
            }

            @XmlAttribute(name="text", required=true)
            public String getExpr() {
                return mExpr;
            }

            public void setName(String name) {
                mName = name;
            }

            public void setExpr(String expr) {
                mExpr = expr;
            }

            public void setTwoWay(boolean isTwoWay) {
                mIsTwoWay = isTwoWay;
            }

            @XmlElement(name="Location")
            public Location getLocation() {
                return mLocation;
            }

            public void setLocation(Location location) {
                mLocation = location;
            }

            @XmlElement(name="ValueLocation")
            public Location getValueLocation() {
                return mValueLocation;
            }

            @XmlElement(name="TwoWay")
            public boolean isTwoWay() {
                return mIsTwoWay;
            }

            public void setValueLocation(Location valueLocation) {
                mValueLocation = valueLocation;
            }
        }
    }

    /**
     * Just an inner callback class to process imports and variables w/ the same code.
     */
    private interface ValidateAndFilterCallback {
        List get(LayoutFileBundle bundle);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy