summaryrefslogtreecommitdiff
path: root/compiler/src/main/java/android/databinding
diff options
context:
space:
mode:
authorGeorge Mount <mount@google.com>2016-03-15 16:35:50 -0700
committerGeorge Mount <mount@google.com>2016-03-17 15:49:29 -0700
commit11df39c91611b9ff2d7c87a9a9829251a015bccf (patch)
tree6239f0e399c08457fcabd397adf4024af57cb5a3 /compiler/src/main/java/android/databinding
parent3b54f63a65ad1ac66ed424efc595598ace99ce9f (diff)
downloaddata-binding-11df39c91611b9ff2d7c87a9a9829251a015bccf.tar.gz
Added simple inverted String conversion.
When binding a primitive to an EditText, a common pattern is to use '@{"" + value}'. This, however, doesn't allow for a two-way data binding expressions. To mitigate the need for conversion functions, a simple inversion for this expression wsa implemented that just converts value from a String when possible. This CL also fixes a bug in which a method matching the first parameter was always chosen, reguardless of the second and further parameters. Change-Id: I36828d9f54d2073965358fceb140b2d5e6328919
Diffstat (limited to 'compiler/src/main/java/android/databinding')
-rw-r--r--compiler/src/main/java/android/databinding/tool/CompilerChef.java27
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/MathExpr.java50
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java (renamed from compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java)41
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java (renamed from compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java)4
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java (renamed from compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java)67
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java32
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java3
7 files changed, 171 insertions, 53 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
index 278492ad..611f3b8f 100644
--- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java
+++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
@@ -13,6 +13,8 @@
package android.databinding.tool;
+import android.databinding.tool.reflection.InjectedClass;
+import android.databinding.tool.reflection.InjectedMethod;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.ResourceBundle;
@@ -71,6 +73,7 @@ public class CompilerChef {
chef.mFileWriter = fileWriter;
chef.mResourceBundle.validateMultiResLayouts();
chef.pushClassesToAnalyzer();
+ chef.pushDynamicUtilToAnalyzer();
return chef;
}
@@ -127,6 +130,30 @@ public class CompilerChef {
}
}
+ public static InjectedClass pushDynamicUtilToAnalyzer() {
+ InjectedClass injectedClass = new InjectedClass("android.databinding.DynamicUtil",
+ "java.lang.Object");
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "getColorFromResource",
+ "int", "android.view.View", "int"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "boolean", "java.lang.String", "boolean"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "short", "java.lang.String", "short"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "int", "java.lang.String", "int"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "long", "java.lang.String", "long"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "float", "java.lang.String", "float"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "double", "java.lang.String", "double"));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse",
+ "char", "java.lang.String", "char"));
+ ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
+ analyzer.injectClass(injectedClass);
+ return injectedClass;
+ }
+
public void writeDataBinderMapper(int minSdk, BRWriter brWriter) {
ensureDataBinder();
final String pkg = "android.databinding";
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
index 63222de3..14b82c47 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
@@ -21,9 +21,12 @@ import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.KCode;
+import com.google.common.collect.Lists;
+
import java.util.List;
public class MathExpr extends Expr {
+ static final String DYNAMIC_UTIL = "android.databinding.DynamicUtil";
final String mOp;
MathExpr(Expr left, String op, Expr right) {
@@ -75,16 +78,29 @@ public class MathExpr extends Expr {
public String getInvertibleError() {
if (mOp.equals("%")) {
return "The modulus operator (%) is not supported in two-way binding.";
- } else if (getResolvedType().isString()) {
- return "String concatenation operator (+) is not supported in two-way binding.";
}
- if (!getLeft().isDynamic()) {
- return getRight().getInvertibleError();
- } else if (!getRight().isDynamic()) {
- return getLeft().getInvertibleError();
- } else {
- return "Arithmetic operator " + mOp + " is not supported with two dynamic expressions.";
+
+ final Expr left = getLeft();
+ final Expr right = getRight();
+ if (left.isDynamic() == right.isDynamic()) {
+ return "Two way binding with operator " + mOp +
+ " supports only a single dynamic expressions.";
+ }
+ Expr dyn = left.isDynamic() ? left : right;
+ if (getResolvedType().isString()) {
+ Expr constExpr = left.isDynamic() ? right : left;
+
+ if (!(constExpr instanceof SymbolExpr) ||
+ !"\"\"".equals(((SymbolExpr) constExpr).getText())) {
+ return "Two-way binding with string concatenation operator (+) only supports the" +
+ " empty string constant (`` or \"\")";
+ }
+ if (!dyn.getResolvedType().unbox().isPrimitive()) {
+ return "Two-way binding with string concatenation operator (+) only supports " +
+ "primitives";
+ }
}
+ return dyn.getInvertibleError();
}
@Override
@@ -92,13 +108,19 @@ public class MathExpr extends Expr {
final Expr left = getLeft();
final Expr right = getRight();
Preconditions.check(left.isDynamic() ^ right.isDynamic(), "Two-way binding of a math " +
- "operations requires A signle dynamic expression. Neither or both sides are " +
+ "operations requires A single dynamic expression. Neither or both sides are " +
"dynamic: (%s) %s (%s)", left, mOp, right);
final Expr constExpr = (left.isDynamic() ? right : left).cloneToModel(model);
+ final Expr varExpr = left.isDynamic() ? left : right;
final Expr newValue;
switch (mOp.charAt(0)) {
case '+': // const + x = value => x = value - const
- newValue = model.math(value, "-", constExpr);
+ if (getResolvedType().isString()) {
+ // just convert back to the primitive type
+ newValue = parseInverse(model, value, varExpr);
+ } else {
+ newValue = model.math(value, "-", constExpr);
+ }
break;
case '*': // const * x = value => x = value / const
newValue = model.math(value, "/", constExpr);
@@ -120,10 +142,16 @@ public class MathExpr extends Expr {
default:
throw new IllegalStateException("Invalid math operation is not invertible: " + mOp);
}
- final Expr varExpr = left.isDynamic() ? left : right;
return varExpr.generateInverse(model, newValue, bindingClassName);
}
+ private Expr parseInverse(ExprModel model, Expr value, Expr prev) {
+ IdentifierExpr dynamicUtil = model.staticIdentifier(DYNAMIC_UTIL);
+ dynamicUtil.setUserDefinedType(DYNAMIC_UTIL);
+
+ return model.methodCall(dynamicUtil, "parse", Lists.newArrayList(value, prev));
+ }
+
@Override
public Expr cloneToModel(ExprModel model) {
return model.math(getLeft().cloneToModel(model), mOp, getRight().cloneToModel(model));
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java
index 4759a9a6..45857991 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java
@@ -18,6 +18,7 @@ package android.databinding.tool.reflection;
import android.databinding.tool.util.StringUtils;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -29,18 +30,23 @@ import java.util.Map;
*
* @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
*/
-public class InjectedBindingClass extends ModelClass {
+public class InjectedClass extends ModelClass {
private final String mClassName;
private final String mSuperClass;
- private final Map<String, String> mVariables;
- private final Map<String, String> mFields;
+ private final List<InjectedMethod> mMethods = new ArrayList<InjectedMethod>();
+ private final List<InjectedField> mFields = new ArrayList<InjectedField>();
- public InjectedBindingClass(String className, String superClass, Map<String, String> variables,
- Map<String, String> fields) {
+ public InjectedClass(String className, String superClass) {
mClassName = className;
mSuperClass = superClass;
- mVariables = variables;
- mFields = fields;
+ }
+
+ public void addField(InjectedField field) {
+ mFields.add(field);
+ }
+
+ public void addMethod(InjectedMethod method) {
+ mMethods.add(method);
}
@Override
@@ -183,12 +189,11 @@ public class InjectedBindingClass extends ModelClass {
protected ModelField[] getDeclaredFields() {
ModelClass superClass = getSuperclass();
final ModelField[] superFields = superClass.getDeclaredFields();
- final int fieldCount = superFields.length + mFields.size();
+ final int initialCount = superFields.length;
+ final int fieldCount = initialCount + mFields.size();
final ModelField[] fields = Arrays.copyOf(superFields, fieldCount);
- int index = superFields.length;
- for (String fieldName : mFields.keySet()) {
- final String fieldType = mFields.get(fieldName);
- fields[index++] = new InjectedBindingClassField(fieldName, fieldType);
+ for (int i = 0; i < mFields.size(); i++) {
+ fields[i + initialCount] = mFields.get(i);
}
return fields;
}
@@ -197,15 +202,11 @@ public class InjectedBindingClass extends ModelClass {
protected ModelMethod[] getDeclaredMethods() {
ModelClass superClass = getSuperclass();
final ModelMethod[] superMethods = superClass.getDeclaredMethods();
- final int methodCount = superMethods.length + (mVariables.size() * 2);
+ final int initialCount = superMethods.length;
+ final int methodCount = initialCount + mMethods.size();
final ModelMethod[] methods = Arrays.copyOf(superMethods, methodCount);
- int index = superMethods.length;
- for (String variableName : mVariables.keySet()) {
- final String variableType = mVariables.get(variableName);
- final String getterName = "get" + StringUtils.capitalize(variableName);
- methods[index++] = new InjectedBindingClassMethod(this, getterName, variableType, null);
- final String setterName = "set" + StringUtils.capitalize(variableName);
- methods[index++] = new InjectedBindingClassMethod(this, setterName, "void", variableType);
+ for (int i = 0; i < mMethods.size(); i++) {
+ methods[i + initialCount] = mMethods.get(i);
}
return methods;
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java
index d15cc908..85719f1c 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java
@@ -24,11 +24,11 @@ import java.util.Map;
*
* @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
*/
-public class InjectedBindingClassField extends ModelField {
+public class InjectedField extends ModelField {
private final String mType;
private final String mName;
- public InjectedBindingClassField(String name, String type) {
+ public InjectedField(String name, String type) {
mName = name;
mType = type;
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java
index 20e24824..f47442b7 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java
@@ -25,18 +25,22 @@ import java.util.Map;
*
* @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
*/
-public class InjectedBindingClassMethod extends ModelMethod {
- private final InjectedBindingClass mContainingClass;
+public class InjectedMethod extends ModelMethod {
+ private final InjectedClass mContainingClass;
private final String mName;
- private final String mReturnType;
- private final String mParameter;
-
- public InjectedBindingClassMethod(InjectedBindingClass containingClass, String name, String returnType,
- String parameter) {
+ private final String mReturnTypeName;
+ private final String[] mParameterTypeNames;
+ private ModelClass[] mParameterTypes;
+ private ModelClass mReturnType;
+ private boolean mIsStatic;
+
+ public InjectedMethod(InjectedClass containingClass, boolean isStatic, String name,
+ String returnType, String... parameters) {
mContainingClass = containingClass;
mName = name;
- mReturnType = returnType;
- mParameter = parameter;
+ mIsStatic = isStatic;
+ mReturnTypeName = returnType;
+ mParameterTypeNames = parameters;
}
@Override
@@ -46,11 +50,18 @@ public class InjectedBindingClassMethod extends ModelMethod {
@Override
public ModelClass[] getParameterTypes() {
- if (mParameter != null) {
- ModelClass parameterType = ModelAnalyzer.getInstance().findClass(mParameter, null);
- return new ModelClass[] { parameterType };
+ if (mParameterTypes == null) {
+ if (mParameterTypeNames == null) {
+ mParameterTypes = new ModelClass[0];
+ } else {
+ mParameterTypes = new ModelClass[mParameterTypeNames.length];
+ ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+ for (int i = 0; i < mParameterTypeNames.length; i++) {
+ mParameterTypes[i] = modelAnalyzer.findClass(mParameterTypeNames[i], null);
+ }
+ }
}
- return new ModelClass[0];
+ return mParameterTypes;
}
@Override
@@ -60,8 +71,10 @@ public class InjectedBindingClassMethod extends ModelMethod {
@Override
public ModelClass getReturnType(List<ModelClass> args) {
- ModelClass returnType = ModelAnalyzer.getInstance().findClass(mReturnType, null);
- return returnType;
+ if (mReturnType == null) {
+ mReturnType = ModelAnalyzer.getInstance().findClass(mReturnTypeName, null);
+ }
+ return mReturnType;
}
@Override
@@ -81,7 +94,7 @@ public class InjectedBindingClassMethod extends ModelMethod {
@Override
public boolean isStatic() {
- return false;
+ return mIsStatic;
}
@Override
@@ -108,4 +121,26 @@ public class InjectedBindingClassMethod extends ModelMethod {
public boolean isVarArgs() {
return false;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("public ");
+ if (mIsStatic) {
+ sb.append("static ");
+ }
+ sb.append(mReturnTypeName)
+ .append(' ')
+ .append(mName)
+ .append("(");
+ if (mParameterTypeNames != null) {
+ for (int i = 0; i < mParameterTypeNames.length; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(mParameterTypeNames[i]);
+ }
+ }
+ sb.append(')');
+ return sb.toString();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
index 8943200b..995ae219 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -18,6 +18,7 @@ package android.databinding.tool.reflection;
import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
@@ -83,8 +84,8 @@ public abstract class ModelAnalyzer {
private ModelClass mViewStubType;
private static ModelAnalyzer sAnalyzer;
- private final Map<String, InjectedBindingClass> mInjectedClasses =
- new HashMap<String, InjectedBindingClass>();
+ private final Map<String, InjectedClass> mInjectedClasses =
+ new HashMap<String, InjectedClass>();
protected void setInstance(ModelAnalyzer analyzer) {
sAnalyzer = analyzer;
@@ -234,10 +235,33 @@ public abstract class ModelAnalyzer {
public abstract TypeUtil createTypeUtil();
+ public ModelClass injectClass(InjectedClass injectedClass) {
+ mInjectedClasses.put(injectedClass.getCanonicalName(), injectedClass);
+ return injectedClass;
+ }
+
public ModelClass injectViewDataBinding(String className, Map<String, String> variables,
Map<String, String> fields) {
- InjectedBindingClass injectedClass = new InjectedBindingClass(className,
- ModelAnalyzer.VIEW_DATA_BINDING, variables, fields);
+ InjectedClass injectedClass = new InjectedClass(className,
+ ModelAnalyzer.VIEW_DATA_BINDING);
+
+ if (fields != null) {
+ for (String name : fields.keySet()) {
+ String type = fields.get(name);
+ injectedClass.addField(new InjectedField(name, type));
+ }
+ }
+ if (variables != null) {
+ for (String name : variables.keySet()) {
+ String type = variables.get(name);
+ String capName = StringUtils.capitalize(name);
+ String setName = "set" + capName;
+ String getName = "get" + capName;
+ injectedClass.addMethod(new InjectedMethod(injectedClass, false, getName, type));
+ injectedClass.addMethod(new InjectedMethod(injectedClass, false, setName, "void",
+ type));
+ }
+ }
mInjectedClasses.put(className, injectedClass);
return injectedClass;
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
index 5bd214e3..3ec7ff8d 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
@@ -96,6 +96,9 @@ public abstract class ModelMethod {
final ModelClass arg = args.get(i);
final ModelClass thisParameter = getParameter(i, parameterTypes);
final ModelClass thatParameter = other.getParameter(i, otherParameterTypes);
+ if (thisParameter.equals(thatParameter)) {
+ continue;
+ }
final int diff = compareParameter(arg, thisParameter, thatParameter);
if (diff != 0) {
return diff < 0;