diff options
8 files changed, 139 insertions, 66 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/DataBinder.java b/compiler/src/main/java/android/databinding/tool/DataBinder.java index 368b83e5..49acdb8f 100644 --- a/compiler/src/main/java/android/databinding/tool/DataBinder.java +++ b/compiler/src/main/java/android/databinding/tool/DataBinder.java @@ -39,7 +39,7 @@ public class DataBinder { for (Map.Entry<String, List<ResourceBundle.LayoutFileBundle>> entry : resourceBundle.getLayoutBundles().entrySet()) { for (ResourceBundle.LayoutFileBundle bundle : entry.getValue()) { - mLayoutBinders.add(new LayoutBinder(resourceBundle, bundle)); + mLayoutBinders.add(new LayoutBinder(bundle)); } } } diff --git a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java index 8271d6d9..d8669271 100644 --- a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java +++ b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java @@ -55,25 +55,18 @@ public class LayoutBinder { private final ExpressionParser mExpressionParser; private final List<BindingTarget> mBindingTargets; private final List<BindingTarget> mSortedBindingTargets; - private String mPackage; private String mModulePackage; - private String mProjectPackage; - private String mBaseClassName; private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>(); private LayoutBinderWriter mWriter; private ResourceBundle.LayoutFileBundle mBundle; - public LayoutBinder(ResourceBundle resourceBundle, - ResourceBundle.LayoutFileBundle layoutBundle) { + public LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle) { mExprModel = new ExprModel(); mExpressionParser = new ExpressionParser(mExprModel); mBindingTargets = new ArrayList<BindingTarget>(); mBundle = layoutBundle; - mProjectPackage = resourceBundle.getAppPackage(); mModulePackage = layoutBundle.getModulePackage(); - mPackage = layoutBundle.getModulePackage() + ".databinding"; - mBaseClassName = ParserHelper.INSTANCE$.toClassName(layoutBundle.getFileName()) + "Binding"; // copy over data. for (Map.Entry<String, String> variable : mBundle.getVariables().entrySet()) { addVariable(variable.getKey(), variable.getValue()); @@ -170,14 +163,13 @@ public class LayoutBinder { public String writeViewBinder(int minSdk) { mExprModel.seal(); ensureWriter(); - Preconditions.checkNotNull(mPackage, "package cannot be null"); - Preconditions.checkNotNull(mProjectPackage, "project package cannot be null"); - Preconditions.checkNotNull(mBaseClassName, "base class name cannot be null"); + Preconditions.checkNotNull(getPackage(), "package cannot be null"); + Preconditions.checkNotNull(getClassName(), "base class name cannot be null"); return mWriter.write(minSdk); } public String getPackage() { - return mPackage; + return mBundle.getBindingClassPackage(); } public boolean isMerge() { @@ -188,28 +180,20 @@ public class LayoutBinder { return mModulePackage; } - public void setPackage(String aPackage) { - mPackage = aPackage; - } - - public String getProjectPackage() { - return mProjectPackage; - } - public String getLayoutname() { return mBundle.getFileName(); } public String getImplementationName() { if (hasVariations()) { - return mBaseClassName + mBundle.getConfigName() + "Impl"; + return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl"; } else { - return mBaseClassName; + return mBundle.getBindingClassName(); } } public String getClassName() { - return mBaseClassName; + return mBundle.getBindingClassName(); } public String getTag() { diff --git a/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java index 05bcdd73..192ec77e 100644 --- a/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java +++ b/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java @@ -24,7 +24,6 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import android.databinding.tool.store.ResourceBundle.BindingTargetBundle; import android.databinding.tool.util.L; import android.databinding.tool.util.ParserHelper; import android.databinding.tool.util.XmlEditor; @@ -120,7 +119,7 @@ public class LayoutFileParser { for (Node parent : bindingNodes) { NamedNodeMap attributes = parent.getAttributes(); String nodeName = parent.getNodeName(); - String className; + String viewName = null; String includedLayoutName = null; final Node id = attributes.getNamedItem("android:id"); final String tag; @@ -133,25 +132,23 @@ public class LayoutFileParser { // if user is binding something there, there MUST be a layout file to be // generated. String layoutName = includeValue.substring(LAYOUT_PREFIX.length()); - className = pkg + ".databinding." + - ParserHelper.INSTANCE$.toClassName(layoutName) + "Binding"; includedLayoutName = layoutName; tag = nodeTagMap.get(parent.getParentNode()); } else { - className = getFullViewClassName(parent); + viewName = getViewName(parent); if (doc.getDocumentElement() == parent || "merge".equals(parent.getParentNode().getNodeName())) { int index = rootDone ? tagNumber++ : 0; rootDone = true; tag = newTag + "_" + index; } else { - tag = "bindingTag" + tagNumber; + tag = "binding_" + tagNumber; tagNumber++; } } final Node originalTag = attributes.getNamedItem("android:tag"); final ResourceBundle.BindingTargetBundle bindingTargetBundle = bundle.createBindingTarget(id == null ? null : id.getNodeValue(), - className, true, tag, originalTag == null ? null : originalTag.getNodeValue()); + viewName, true, tag, originalTag == null ? null : originalTag.getNodeValue()); nodeTagMap.put(parent, tag); bindingTargetBundle.setIncludedLayout(includedLayoutName); @@ -171,7 +168,7 @@ public class LayoutFileParser { for (Node node : idNodes) { if (!bindingNodes.contains(node) && !"include".equals(node.getNodeName())) { final Node id = node.getAttributes().getNamedItem("android:id"); - final String className = getFullViewClassName(node); + final String className = getViewName(node); bundle.createBindingTarget(id.getNodeValue(), className, true, null, null); } } @@ -217,7 +214,7 @@ public class LayoutFileParser { return result; } - private String getFullViewClassName(Node viewNode) { + private String getViewName(Node viewNode) { String viewName = viewNode.getNodeName(); if ("view".equals(viewName)) { Node classNode = viewNode.getAttributes().getNamedItem("class"); @@ -227,13 +224,6 @@ public class LayoutFileParser { viewName = classNode.getNodeValue(); } } - if (viewName.indexOf('.') == -1) { - if (ObjectUtils.equals(viewName, "View") || ObjectUtils.equals(viewName, "ViewGroup") || - ObjectUtils.equals(viewName, "ViewStub")) { - return "android.view." + viewName; - } - return "android.widget." + viewName; - } return viewName; } diff --git a/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java b/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java index d79d1d90..0de1d53c 100644 --- a/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java +++ b/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java @@ -17,8 +17,6 @@ import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; -import android.databinding.tool.reflection.ModelAnalyzer; -import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.util.L; import android.databinding.tool.util.ParserHelper; @@ -78,6 +76,23 @@ public class ResourceBundle implements Serializable { } public void validateMultiResLayouts() { + for (List<LayoutFileBundle> layoutFileBundles : mLayoutBundles.values()) { + for (LayoutFileBundle layoutFileBundle : layoutFileBundles) { + for (BindingTargetBundle target : layoutFileBundle.getBindingTargetBundles()) { + if (target.isBinder()) { + List<LayoutFileBundle> boundTo = + mLayoutBundles.get(target.getIncludedLayout()); + if (boundTo == null || boundTo.isEmpty()) { + L.e("There is no binding for %s", target.getIncludedLayout()); + } else { + String binding = boundTo.get(0).getFullBindingClass(); + target.setInterfaceType(binding); + } + } + } + } + } + final Iterable<Map.Entry<String, List<LayoutFileBundle>>> multiResLayouts = Iterables .filter(mLayoutBundles.entrySet(), new Predicate<Map.Entry<String, List<LayoutFileBundle>>>() { @@ -92,9 +107,20 @@ public class ResourceBundle implements Serializable { // and all variables have the same name Map<String, String> variableTypes = new HashMap<String, String>(); Map<String, String> importTypes = new HashMap<String, String>(); + String bindingClass = null; for (LayoutFileBundle bundle : bundles.getValue()) { bundle.mHasVariations = true; + if (bindingClass == null) { + bindingClass = bundle.getFullBindingClass(); + } else { + if (!bindingClass.equals(bundle.getFullBindingClass())) { + L.e("Binding class names must match. Layout file for %s have " + + "different binding class names %s and %s", + bundle.getFileName(), + bindingClass, bundle.getFullBindingClass()); + } + } for (Map.Entry<String, String> variable : bundle.mVariables.entrySet()) { String existing = variableTypes.get(variable.getKey()); Preconditions @@ -140,28 +166,28 @@ public class ResourceBundle implements Serializable { L.d("validating ids for %s", bundles.getKey()); for (LayoutFileBundle bundle : bundles.getValue()) { for (BindingTargetBundle target : bundle.mBindingTargetBundles) { - L.d("checking %s %s %s", target.getId(), target.mFullClassName, + L.d("checking %s %s %s", target.getId(), target.getFullClassName(), target.isBinder()); if (target.mId != null) { if (target.isBinder()) { - Preconditions.checkState(!viewBindingIds.contains(target.mFullClassName), + Preconditions.checkState(!viewBindingIds.contains(target.getFullClassName()), "Cannot use the same id for a View and an include tag. Error " + "in file %s / %s", bundle.mFileName, bundle.mConfigName); - includeBindingIds.add(target.mFullClassName); + includeBindingIds.add(target.getFullClassName()); } else { - Preconditions.checkState(!includeBindingIds.contains(target.mFullClassName), + Preconditions.checkState(!includeBindingIds.contains(target.getFullClassName()), "Cannot use the same id for a View and an include tag. Error in " + "file %s / %s", bundle.mFileName, bundle.mConfigName); - viewBindingIds.add(target.mFullClassName); + viewBindingIds.add(target.getFullClassName()); } String existingType = viewTypes.get(target.mId); if (existingType == null) { - L.d("assigning %s as %s", target.getId(), target.mFullClassName); - viewTypes.put(target.mId, target.mFullClassName); + 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.mFullClassName)) { + } else if (!existingType.equals(target.getFullClassName())) { if (target.isBinder()) { L.d("overriding %s as base binder", target.getId()); viewTypes.put(target.mId, @@ -180,10 +206,18 @@ public class ResourceBundle implements Serializable { for (Map.Entry<String, String> viewType : viewTypes.entrySet()) { BindingTargetBundle target = bundle.getBindingTargetById(viewType.getKey()); if (target == null) { - bundle.createBindingTarget(viewType.getKey(), viewType.getValue(), false, - null, null).setIncludedLayout(includes.get(viewType.getKey())); + String include = includes.get(viewType.getKey()); + if (include == null) { + bundle.createBindingTarget(viewType.getKey(), viewType.getValue(), + false, null, null); + } else { + BindingTargetBundle bindingTargetBundle = bundle.createBindingTarget( + viewType.getKey(), null, false, 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.mFullClassName, viewType.getValue()); + L.d("setting interface type on %s (%s) as %s", target.mId, target.getFullClassName(), viewType.getValue()); target.setInterfaceType(viewType.getValue()); } } @@ -219,6 +253,19 @@ public class ResourceBundle implements Serializable { public String mModulePackage; private String mConfigName; + // The binding class as given by the user + @XmlAttribute(name="bindingClass", required = false) + public String mBindingClass; + + // 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; @@ -258,9 +305,9 @@ public class ResourceBundle implements Serializable { mImports.put(alias, type); } - public BindingTargetBundle createBindingTarget(String id, String fullClassName, + public BindingTargetBundle createBindingTarget(String id, String viewName, boolean used, String tag, String originalTag) { - BindingTargetBundle target = new BindingTargetBundle(id, fullClassName, used, tag, + BindingTargetBundle target = new BindingTargetBundle(id, viewName, used, tag, originalTag); mBindingTargetBundles.add(target); return target; @@ -307,6 +354,40 @@ public class ResourceBundle implements Serializable { return mIsMerge; } + public String getBindingClassName() { + if (mBindingClassName == null) { + String fullClass = getFullBindingClass(); + int dotIndex = fullClass.lastIndexOf('.'); + mBindingClassName = fullClass.substring(dotIndex + 1); + } + return mBindingClassName; + } + + 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.INSTANCE$.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<BindingTargetBundle> getBindingTargetBundles() { return mBindingTargetBundles; } @@ -385,8 +466,9 @@ public class ResourceBundle implements Serializable { public String mTag; @XmlAttribute(name="originalTag") public String mOriginalTag; - @XmlAttribute(name="boundClass", required = true) - public String mFullClassName; + @XmlAttribute(name="view", required = false) + public String mViewName; + private String mFullClassName; public boolean mUsed = true; @XmlElementWrapper(name="Expressions") @XmlElement(name="Expression") @@ -398,10 +480,10 @@ public class ResourceBundle implements Serializable { // For XML serialization public BindingTargetBundle() {} - public BindingTargetBundle(String id, String fullClassName, boolean used, + public BindingTargetBundle(String id, String viewName, boolean used, String tag, String originalTag) { mId = id; - mFullClassName = fullClassName; + mViewName = viewName; mUsed = used; mTag = tag; mOriginalTag = originalTag; @@ -440,6 +522,24 @@ public class ResourceBundle implements Serializable { } public String getFullClassName() { + if (mFullClassName == null) { + if (isBinder()) { + mFullClassName = mInterfaceType; + } else if (mViewName.indexOf('.') == -1) { + if ("View".equals(mViewName) || "ViewGroup".equals(mViewName) || + "ViewStub".equals(mViewName)) { + mFullClassName = "android.view." + 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; } diff --git a/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt b/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt index 9b7b3a93..01a25aa9 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt @@ -204,7 +204,7 @@ object XmlEditor { tag = "" } else { val index = bindingIndex++; - tag = "android:tag=\"bindingTag${index}\""; + tag = "android:tag=\"binding_${index}\""; } it.attributes?.forEach { if (!replaced && tagWillFit(it.start, it.end, tag)) { diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt index 4f8183b3..d5fade48 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt @@ -316,8 +316,8 @@ fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) fun indexFromTag(tag : String) : kotlin.Int { val startIndex : kotlin.Int - if (tag.startsWith("bindingTag")) { - startIndex = "bindingTag".length(); + if (tag.startsWith("binding_")) { + startIndex = "binding_".length(); } else { startIndex = tag.lastIndexOf('_') + 1 } @@ -512,7 +512,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { tab("this.${it.fieldName}.setContainingBinding(this);") } if (it.supportsTag() && it.getTag() != null && - (rootTagsSupported || it.getTag().startsWith("bindingTag"))) { + (rootTagsSupported || it.getTag().startsWith("binding_"))) { val originalTag = it.getOriginalTag(); var tagValue = "null" if (originalTag != null) { diff --git a/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java b/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java index a172ca59..36b92fe0 100644 --- a/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java +++ b/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java @@ -18,7 +18,6 @@ import android.databinding.tool.store.ResourceBundle; public class MockLayoutBinder extends LayoutBinder { public MockLayoutBinder() { - super(new ResourceBundle("com.test"), - new ResourceBundle.LayoutFileBundle("blah.xml", "layout", "com.test.submodule", false)); + super(new ResourceBundle.LayoutFileBundle("blah.xml", "layout", "com.test.submodule", false)); } } diff --git a/library/src/main/java/android/databinding/ViewDataBinding.java b/library/src/main/java/android/databinding/ViewDataBinding.java index 8fa5fa98..b2dbd6a8 100644 --- a/library/src/main/java/android/databinding/ViewDataBinding.java +++ b/library/src/main/java/android/databinding/ViewDataBinding.java @@ -43,7 +43,7 @@ public abstract class ViewDataBinding { * Prefix for android:tag on Views with binding. The root View and include tags will not have * android:tag attributes and will use ids instead. */ - public static final String BINDING_TAG_PREFIX = "bindingTag"; + public static final String BINDING_TAG_PREFIX = "binding_"; // The length of BINDING_TAG_PREFIX prevents calling length repeatedly. private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length(); |