summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Mount <mount@google.com>2016-02-18 14:33:37 -0800
committerGeorge Mount <mount@google.com>2016-03-08 15:38:00 -0800
commitbb4a033fcd5cd20e5be46ef8ead442dc7db2454d (patch)
treea6eebe0a848983d8dad5662f674959e7e76b2c80
parentb7eeedbfadec03792551014e9dfa2bd384fc21a3 (diff)
downloaddata-binding-bb4a033fcd5cd20e5be46ef8ead442dc7db2454d.tar.gz
Have two-way binding use localized variables to prevent NPE.
Bug 26962999 Two-way binding was using the inverted expressions directly without localizing variables. That meant that if there was a variable set to null during evaluation, it may get a NullPointerException even though it checked for null on the value previously. This CL localizes the variables so that cannot happen. Change-Id: Ia55955ce0f1cb750e6a678e72e0cda03f0e3c9b6
-rw-r--r--compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java6
-rw-r--r--compiler/src/main/java/android/databinding/tool/Binding.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/BindingTarget.java28
-rw-r--r--compiler/src/main/java/android/databinding/tool/CompilerChef.java38
-rw-r--r--compiler/src/main/java/android/databinding/tool/InverseBinding.java141
-rw-r--r--compiler/src/main/java/android/databinding/tool/LayoutBinder.java14
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java7
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java16
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java41
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java7
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/CallbackArgExpr.java7
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java1
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/CastExpr.java25
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java21
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/Expr.java41
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ExprModel.java10
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java129
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/FieldAssignmentExpr.java107
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java27
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java14
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/LambdaExpr.java7
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java12
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/MathExpr.java85
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java56
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ObservableFieldExpr.java80
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java25
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java10
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java13
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java33
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java7
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java20
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java15
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java217
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java65
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java111
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java20
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java26
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java6
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java10
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/store/SetterStore.java33
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/expr/ExprWriters.kt2
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt42
-rw-r--r--compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java8
-rw-r--r--compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java8
-rw-r--r--compiler/src/test/java/android/databinding/tool/expr/ExecutionPathTest.java2
-rw-r--r--compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java15
-rw-r--r--compiler/src/test/java/android/databinding/tool/expr/ExprTest.java14
-rw-r--r--compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java12
-rw-r--r--compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java5
-rw-r--r--compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java2
-rw-r--r--databinding.properties2
53 files changed, 1276 insertions, 382 deletions
diff --git a/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java b/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
index 3525b3a8..68c34245 100644
--- a/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
+++ b/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
@@ -158,7 +158,7 @@ public class SimpleCompilationTest extends BaseCompilationTest {
expectedErrorFile = "/app/src/main/res/layout/broken.xml";
} else if (errorFile.getCanonicalPath().equals(invalidSetter.getCanonicalPath())) {
message = String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
- String.class.getCanonicalName());
+ String.class.getCanonicalName(), "android.widget.TextView");
expectedErrorFile = "/app/src/main/res/layout/invalid_setter.xml";
} else {
fail("unexpected exception " + exception.getBareMessage());
@@ -208,7 +208,7 @@ public class SimpleCompilationTest extends BaseCompilationTest {
ScopedException ex = singleFileErrorTest("/layout/invalid_setter_binding.xml",
"/app/src/main/res/layout/invalid_setter.xml", "myVariable",
String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
- String.class.getCanonicalName()));
+ String.class.getCanonicalName(), "android.widget.TextView"));
}
@Test
@@ -257,7 +257,7 @@ public class SimpleCompilationTest extends BaseCompilationTest {
prepareProject();
ScopedException ex = singleFileErrorTest("/layout/invalid_variable_type.xml",
"/app/src/main/res/layout/invalid_variable.xml", "myVariable",
- String.format(ErrorMessages.CANNOT_RESOLVE_TYPE, "myVariable~"));
+ String.format(ErrorMessages.CANNOT_RESOLVE_TYPE, "myVariable"));
}
@Test
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java
index 4dc059f3..59e86141 100644
--- a/compiler/src/main/java/android/databinding/tool/Binding.java
+++ b/compiler/src/main/java/android/databinding/tool/Binding.java
@@ -75,7 +75,7 @@ public class Binding implements LocationScopeProvider {
LambdaExpr lambdaExpr = (LambdaExpr) mExpr;
final ModelClass listener = getListenerParameter(mTarget, mName, mExpr.getModel());
Preconditions.checkNotNull(listener, ErrorMessages.CANNOT_FIND_SETTER_CALL, mName,
- "lambda");
+ "lambda", getTarget().getInterfaceType());
//noinspection ConstantConditions
List<ModelMethod> abstractMethods = listener.getAbstractMethods();
int numberOfAbstractMethods = abstractMethods.size();
@@ -106,7 +106,8 @@ public class Binding implements LocationScopeProvider {
Scope.enter(this);
resolveSetterCall();
if (mSetterCall == null) {
- L.e(ErrorMessages.CANNOT_FIND_SETTER_CALL, mName, mExpr.getResolvedType());
+ L.e(ErrorMessages.CANNOT_FIND_SETTER_CALL, mName, mExpr.getResolvedType(),
+ getTarget().getInterfaceType());
}
} finally {
Scope.exit();
diff --git a/compiler/src/main/java/android/databinding/tool/BindingTarget.java b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
index 74ef86cd..8c59ca75 100644
--- a/compiler/src/main/java/android/databinding/tool/BindingTarget.java
+++ b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
@@ -58,28 +58,24 @@ public class BindingTarget implements LocationScopeProvider {
L.e(ErrorMessages.TWO_WAY_EVENT_ATTRIBUTE, name);
}
mBindings.add(new Binding(this, name, expr));
- if (expr.isTwoWay()) {
- try {
- Scope.enter(expr);
- expr.assertIsInvertible();
- final InverseBinding inverseBinding = new InverseBinding(this, name, expr);
- mInverseBindings.add(inverseBinding);
- mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
- mModel.twoWayListenerExpr(inverseBinding),
- inverseBinding.getEventSetter()));
- } finally {
- Scope.exit();
- }
- }
}
public String getInterfaceType() {
return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
}
+ public InverseBinding addInverseBinding(String name, Expr expr, String bindingClass) {
+ expr.assertIsInvertible();
+ final InverseBinding inverseBinding = new InverseBinding(this, name, expr, bindingClass);
+ mInverseBindings.add(inverseBinding);
+ mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
+ mModel.twoWayListenerExpr(inverseBinding),
+ inverseBinding.getEventSetter()));
+ return inverseBinding;
+ }
+
public InverseBinding addInverseBinding(String name, BindingGetterCall call) {
- final InverseBinding inverseBinding = new InverseBinding(this, name, null);
- inverseBinding.setGetterCall(call);
+ final InverseBinding inverseBinding = new InverseBinding(this, name, call);
mInverseBindings.add(inverseBinding);
mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
mModel.twoWayListenerExpr(inverseBinding)));
@@ -111,7 +107,7 @@ public class BindingTarget implements LocationScopeProvider {
if (mResolvedClass == null) {
if (mBundle.isBinder()) {
mResolvedClass = ModelAnalyzer.getInstance().
- findClass(ModelAnalyzer.VIEW_DATA_BINDING, mModel.getImports());
+ findClass(mBundle.getInterfaceType(), mModel.getImports());
} else {
mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
mModel.getImports());
diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
index b7456da0..278492ad 100644
--- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java
+++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
@@ -23,6 +23,7 @@ import android.databinding.tool.writer.DataBinderWriter;
import android.databinding.tool.writer.DynamicUtilWriter;
import android.databinding.tool.writer.JavaFileWriter;
+import java.util.HashMap;
import java.util.Set;
/**
@@ -69,6 +70,7 @@ public class CompilerChef {
chef.mResourceBundle = bundle;
chef.mFileWriter = fileWriter;
chef.mResourceBundle.validateMultiResLayouts();
+ chef.pushClassesToAnalyzer();
return chef;
}
@@ -89,6 +91,42 @@ public class CompilerChef {
return mResourceBundle != null && mResourceBundle.getLayoutBundles().size() > 0;
}
+ /**
+ * Injects ViewDataBinding subclasses to the ModelAnalyzer so that they can be
+ * analyzed prior to creation. This is useful for resolving variable setters and
+ * View fields during compilation.
+ */
+ private void pushClassesToAnalyzer() {
+ ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
+ for (String layoutName : mResourceBundle.getLayoutBundles().keySet()) {
+ ResourceBundle.LayoutFileBundle layoutFileBundle =
+ mResourceBundle.getLayoutBundles().get(layoutName).get(0);
+ final HashMap<String, String> imports = new HashMap<String, String>();
+ for (ResourceBundle.NameTypeLocation imp : layoutFileBundle.getImports()) {
+ imports.put(imp.name, imp.type);
+ }
+ final HashMap<String, String> variables = new HashMap<String, String>();
+ for (ResourceBundle.VariableDeclaration variable : layoutFileBundle.getVariables()) {
+ final String variableName = variable.name;
+ String type = variable.type;
+ if (imports.containsKey(type)) {
+ type = imports.get(type);
+ }
+ variables.put(variableName, type);
+ }
+ final HashMap<String, String> fields = new HashMap<String, String>();
+ for (ResourceBundle.BindingTargetBundle bindingTargetBundle :
+ layoutFileBundle.getBindingTargetBundles()) {
+ if (bindingTargetBundle.getId() != null) {
+ fields.put(bindingTargetBundle.getId(), bindingTargetBundle.getInterfaceType());
+ }
+ }
+ final String className = layoutFileBundle.getBindingClassPackage() + "." +
+ layoutFileBundle.getBindingClassName();
+ analyzer.injectViewDataBinding(className, variables, fields);
+ }
+ }
+
public void writeDataBinderMapper(int minSdk, BRWriter brWriter) {
ensureDataBinder();
final String pkg = "android.databinding";
diff --git a/compiler/src/main/java/android/databinding/tool/InverseBinding.java b/compiler/src/main/java/android/databinding/tool/InverseBinding.java
index e04be283..13dd8875 100644
--- a/compiler/src/main/java/android/databinding/tool/InverseBinding.java
+++ b/compiler/src/main/java/android/databinding/tool/InverseBinding.java
@@ -16,25 +16,23 @@
package android.databinding.tool;
+import android.databinding.tool.expr.CallbackArgExpr;
+import android.databinding.tool.expr.CallbackExprModel;
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
import android.databinding.tool.expr.FieldAccessExpr;
+import android.databinding.tool.expr.IdentifierExpr;
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.scopes.LocationScopeProvider;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.solver.ExecutionPath;
import android.databinding.tool.store.Location;
import android.databinding.tool.store.SetterStore;
import android.databinding.tool.store.SetterStore.BindingGetterCall;
import android.databinding.tool.store.SetterStore.BindingSetterCall;
import android.databinding.tool.util.L;
-import android.databinding.tool.util.Preconditions;
-import android.databinding.tool.writer.FlagSet;
-import android.databinding.tool.writer.KCode;
-import android.databinding.tool.writer.LayoutBinderWriterKt;
-
-import kotlin.jvm.functions.Function2;
import java.util.ArrayList;
import java.util.List;
@@ -46,11 +44,38 @@ public class InverseBinding implements LocationScopeProvider {
private final BindingTarget mTarget;
private BindingGetterCall mGetterCall;
private final ArrayList<FieldAccessExpr> mChainedExpressions = new ArrayList<FieldAccessExpr>();
+ private final CallbackExprModel mCallbackExprModel;
+ private final Expr mInverseExpr;
+ private final CallbackArgExpr mVariableExpr;
+ private final ExecutionPath mExecutionPath;
+
+ public InverseBinding(BindingTarget target, String name, Expr expr, String bindingClassName) {
+ mTarget = target;
+ mName = name;
+ mCallbackExprModel = new CallbackExprModel(expr.getModel());
+ mExpr = expr.cloneToModel(mCallbackExprModel);
+ setGetterCall(mExpr);
+ mVariableExpr = mCallbackExprModel.callbackArg("callbackArg_0");
+ ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+ ModelClass type = modelAnalyzer.findClass(getGetterCall().getGetterType(), null);
+ mVariableExpr.setClassFromCallback(type);
+ mVariableExpr.setUserDefinedType(getGetterCall().getGetterType());
+ mInverseExpr =
+ mExpr.generateInverse(mCallbackExprModel, mVariableExpr, bindingClassName);
+ mExecutionPath = ExecutionPath.createRoot();
+ mInverseExpr.toExecutionPath(mExecutionPath);
+ mCallbackExprModel.seal();
+ }
- public InverseBinding(BindingTarget target, String name, Expr expr) {
+ public InverseBinding(BindingTarget target, String name, BindingGetterCall getterCall) {
mTarget = target;
mName = name;
- mExpr = expr;
+ mExpr = null;
+ mCallbackExprModel = null;
+ mInverseExpr = null;
+ mVariableExpr = null;
+ mExecutionPath = null;
+ setGetterCall(getterCall);
}
@Override
@@ -62,7 +87,7 @@ public class InverseBinding implements LocationScopeProvider {
}
}
- void setGetterCall(BindingGetterCall getterCall) {
+ private void setGetterCall(BindingGetterCall getterCall) {
mGetterCall = getterCall;
}
@@ -74,74 +99,56 @@ public class InverseBinding implements LocationScopeProvider {
return mTarget.getResolvedType().isViewDataBinding();
}
- private SetterStore.BindingGetterCall getGetterCall() {
- if (mGetterCall == null) {
- if (mExpr != null) {
- mExpr.getResolvedType(); // force resolve of ObservableFields
- }
- try {
- Scope.enter(mTarget);
- Scope.enter(this);
- resolveGetterCall();
- if (mGetterCall == null) {
- L.e(ErrorMessages.CANNOT_FIND_GETTER_CALL, mName,
- mExpr == null ? "Unknown" : mExpr.getResolvedType(),
- mTarget.getResolvedType());
- }
- } finally {
- Scope.exit();
- Scope.exit();
+ private void setGetterCall(Expr expr) {
+ try {
+ Scope.enter(mTarget);
+ Scope.enter(this);
+ ModelClass viewType = mTarget.getResolvedType();
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ final ModelClass resolvedType = expr == null ? null : expr.getResolvedType();
+ mGetterCall = setterStore.getGetterCall(mName, viewType, resolvedType,
+ expr.getModel().getImports());
+ if (mGetterCall == null) {
+ L.e(ErrorMessages.CANNOT_FIND_GETTER_CALL, mName,
+ expr == null ? "Unknown" : mExpr.getResolvedType(),
+ mTarget.getResolvedType());
}
+ } finally {
+ Scope.exit();
+ Scope.exit();
}
- return mGetterCall;
}
- private void resolveGetterCall() {
- ModelClass viewType = mTarget.getResolvedType();
- final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
- final ModelClass resolvedType = mExpr == null ? null : mExpr.getResolvedType();
- mGetterCall = setterStore.getGetterCall(mName, viewType, resolvedType,
- getModel().getImports());
+ public SetterStore.BindingGetterCall getGetterCall() {
+ return mGetterCall;
}
public BindingTarget getTarget() {
return mTarget;
}
- public KCode toJavaCode(String bindingComponent, final FlagSet flagField) {
- final String targetViewName = LayoutBinderWriterKt.getFieldName(getTarget());
- KCode code = new KCode();
- // A chained expression will have substituted its chained value for the expression
- // unless the attribute has no expression. Therefore, chaining and expressions are
- // mutually exclusive.
- Preconditions.check((mExpr == null) != mChainedExpressions.isEmpty(),
- "Chained expressions are only against unbound attributes.");
- if (mExpr != null) {
- code.app("", mExpr.toInverseCode(new KCode(getGetterCall().toJava(bindingComponent,
- targetViewName))));
- } else { // !mChainedExpressions.isEmpty())
- final String fieldName = flagField.getLocalName();
- FlagSet flagSet = new FlagSet();
- for (FieldAccessExpr expr : mChainedExpressions) {
- flagSet = flagSet.or(new FlagSet(expr.getId()));
- }
- final FlagSet allFlags = flagSet;
- code.nl(new KCode("synchronized(this) {"));
- code.tab(LayoutBinderWriterKt
- .mapOr(flagField, flagSet, new Function2<String, Integer, KCode>() {
- @Override
- public KCode invoke(String suffix, Integer index) {
- return new KCode(fieldName)
- .app(suffix)
- .app(" |= ")
- .app(LayoutBinderWriterKt.binaryCode(allFlags, index))
- .app(";");
- }
- }));
- code.nl(new KCode("}"));
- code.nl(new KCode("requestRebind()"));
- }
- return code;
+ public Expr getExpr() {
+ return mExpr;
+ }
+
+ public Expr getInverseExpr() {
+ return mInverseExpr;
+ }
+
+ public IdentifierExpr getVariableExpr() {
+ return mVariableExpr;
+ }
+
+ public ExecutionPath getExecutionPath() {
+ return mExecutionPath;
+ }
+
+ public CallbackExprModel getCallbackExprModel() {
+ return mCallbackExprModel;
+ }
+
+ public List<FieldAccessExpr> getChainedExpressions() {
+ return mChainedExpressions;
}
public String getBindingAdapterInstanceClass() {
diff --git a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
index 7348bd75..7969854a 100644
--- a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
+++ b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
@@ -216,13 +216,18 @@ public class LayoutBinder implements FileScopeProvider {
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());
- bindingTarget.addBinding(bindingBundle.getName(),
- parse(bindingBundle.getExpr(), bindingBundle.isTwoWay(),
- 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();
}
@@ -290,10 +295,9 @@ public class LayoutBinder implements FileScopeProvider {
return target;
}
- public Expr parse(String input, boolean isTwoWay, @Nullable Location locationInFile) {
+ public Expr parse(String input, @Nullable Location locationInFile) {
final Expr parsed = mExpressionParser.parse(input, locationInFile);
parsed.setBindingExpression(true);
- parsed.setTwoWay(isTwoWay);
return parsed;
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
index c8f6e2cf..cdb0d14a 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
@@ -42,12 +42,17 @@ public class ArgListExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
throw new IllegalStateException("should never try to convert an argument expressions"
+ " into code");
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.argListExpr(cloneToModel(model, getChildren()));
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
return modelAnalyzer.findClass(Void.class);
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
index cbc895bb..f4da3ec0 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
@@ -57,15 +57,25 @@ public class BitShiftExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode()
- .app("", getLeft().toCode(expand))
+ .app("", getLeft().toCode())
.app(getOp())
- .app("", getRight().toCode(expand));
+ .app("", getRight().toCode());
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.bitshift(getLeft().cloneToModel(model), mOp, getRight().cloneToModel(model));
}
@Override
public String getInvertibleError() {
return "Bit shift operators cannot be inverted in two-way binding";
}
+
+ @Override
+ public String toString() {
+ return getLeft().toString() + ' ' + mOp + ' ' + getRight().toString();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
index 0829ad04..a9a61556 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
@@ -21,6 +21,8 @@ import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.solver.ExecutionPath;
import android.databinding.tool.writer.KCode;
+import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.List;
@@ -90,7 +92,7 @@ public class BracketExpr extends Expr {
protected String computeUniqueKey() {
final String targetKey = getTarget().computeUniqueKey();
- return addTwoWay(join(targetKey, "$", getArg().computeUniqueKey(), "$"));
+ return join(targetKey, "$", getArg().computeUniqueKey(), "$");
}
@Override
@@ -115,7 +117,7 @@ public class BracketExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
String cast = argCastsInteger() ? "(Integer) " : "";
switch (getAccessor()) {
case ARRAY: {
@@ -152,12 +154,33 @@ public class BracketExpr extends Expr {
}
@Override
- public KCode toInverseCode(KCode value) {
- String cast = argCastsInteger() ? "(Integer) " : "";
- return new KCode().
- app("setTo(", getTarget().toCode(true)).
- app(", ").
- app(cast, getArg().toCode(true)).
- app(", ", value).app(");");
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ Expr arg = getArg().cloneToModel(model);
+ arg = argCastsInteger()
+ ? model.castExpr("int", model.castExpr("Integer", arg))
+ : arg;
+ StaticIdentifierExpr viewDataBinding =
+ model.staticIdentifier(ModelAnalyzer.VIEW_DATA_BINDING);
+ viewDataBinding.setUserDefinedType(ModelAnalyzer.VIEW_DATA_BINDING);
+ ModelClass targetType = getTarget().getResolvedType();
+ if ((targetType.isList() || targetType.isMap()) &&
+ value.getResolvedType().isPrimitive()) {
+ ModelClass boxed = value.getResolvedType().box();
+ value = model.castExpr(boxed.toJavaCode(), value);
+ }
+ List<Expr> args = Lists.newArrayList(getTarget().cloneToModel(model), arg, value);
+ MethodCallExpr setter = model.methodCall(viewDataBinding, "setTo", args);
+ setter.setAllowProtected();
+ return setter;
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.bracketExpr(getTarget().cloneToModel(model), getArg().cloneToModel(model));
+ }
+
+ @Override
+ public String toString() {
+ return getTarget().toString() + '[' + getArg() + ']';
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java
index d2fdea13..ff1d1adf 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java
@@ -49,7 +49,7 @@ public class BuiltInVariableExpr extends IdentifierExpr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
if (mAccessCode == null) {
return new KCode().app(mName);
} else {
@@ -65,4 +65,9 @@ public class BuiltInVariableExpr extends IdentifierExpr {
public String getInvertibleError() {
return "Built-in variables may not be the target of two-way binding";
}
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.builtInVariable(mName, mUserDefinedType, mAccessCode);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/CallbackArgExpr.java b/compiler/src/main/java/android/databinding/tool/expr/CallbackArgExpr.java
index 2f77f831..34841c93 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/CallbackArgExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/CallbackArgExpr.java
@@ -68,7 +68,7 @@ public class CallbackArgExpr extends IdentifierExpr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode(CallbackWrapper.ARG_PREFIX + mArgIndex);
}
@@ -85,4 +85,9 @@ public class CallbackArgExpr extends IdentifierExpr {
public String getName() {
return mName;
}
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return new CallbackArgExpr(mArgIndex, mName);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java b/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java
index 1e0fc8ac..501125c0 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java
@@ -18,7 +18,6 @@ package android.databinding.tool.expr;
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
-import android.databinding.tool.processing.ScopedException;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
diff --git a/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java b/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
index 9a4a36ae..9a66cd98 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
@@ -46,7 +46,7 @@ public class CastExpr extends Expr {
}
protected String computeUniqueKey() {
- return addTwoWay(join(mType, getCastExpr().computeUniqueKey()));
+ return join(mType, getCastExpr().computeUniqueKey());
}
public Expr getCastExpr() {
@@ -58,11 +58,12 @@ public class CastExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode()
.app("(")
.app(getCastType())
- .app(") ", getCastExpr().toCode(expand));
+ .app(") (", getCastExpr().toCode())
+ .app(")");
}
@Override
@@ -71,8 +72,20 @@ public class CastExpr extends Expr {
}
@Override
- public KCode toInverseCode(KCode value) {
- // assume no need to cast in reverse
- return getCastExpr().toInverseCode(value);
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ Expr castExpr = getCastExpr();
+ ModelClass exprType = castExpr.getResolvedType();
+ Expr castValue = model.castExpr(exprType.toJavaCode(), value);
+ return castExpr.generateInverse(model, castValue, bindingClassName);
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.castExpr(mType, getCastExpr().cloneToModel(model));
+ }
+
+ @Override
+ public String toString() {
+ return "(" + mType + ") " + getCastExpr();
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
index 172ea219..6b36bca9 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
@@ -62,14 +62,27 @@ public class ComparisonExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
- return new KCode().app("", getLeft().toCode(expand))
- .app(" ").app(getOp()).app(" ")
- .app("", getRight().toCode(expand));
+ protected KCode generateCode() {
+ return new KCode()
+ .app("(", getLeft().toCode())
+ .app(") ")
+ .app(getOp())
+ .app(" (", getRight().toCode())
+ .app(")");
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.comparison(mOp, getLeft().cloneToModel(model), getRight().cloneToModel(model));
}
@Override
public String getInvertibleError() {
return "Comparison operators are not valid as targets of two-way binding";
}
+
+ @Override
+ public String toString() {
+ return getLeft().toString() + ' ' + mOp + ' ' + getRight();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/Expr.java b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
index 59cc916d..768a59a5 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/Expr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
@@ -100,7 +100,6 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
private boolean mRead;
private boolean mIsUsed = false;
private boolean mIsUsedInCallback = false;
- private boolean mIsTwoWay = false;
Expr(Iterable<Expr> children) {
for (Expr expr : children) {
@@ -212,22 +211,6 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
mModel = model;
}
- public void setTwoWay(boolean isTwoWay) {
- mIsTwoWay = isTwoWay;
- }
-
- public boolean isTwoWay() {
- return mIsTwoWay;
- }
-
- protected String addTwoWay(String uniqueKey) {
- if (mIsTwoWay) {
- return "twoWay(" + uniqueKey + ")";
- } else {
- return "oneWay(" + uniqueKey + ")";
- }
- }
-
private BitSet resolveShouldReadWithConditionals() {
// ensure we have invalid flags
BitSet bitSet = new BitSet();
@@ -751,26 +734,32 @@ abstract public class Expr implements VersionProvider, LocationScopeProvider {
}
public KCode toCode() {
- return toCode(false);
- }
-
- public KCode toCode(boolean expand) {
- if (!expand && isDynamic()) {
+ if (isDynamic()) {
return new KCode(LayoutBinderWriterKt.scopedName(this));
}
- return generateCode(expand);
+ return generateCode();
}
public KCode toFullCode() {
- return generateCode(false);
+ return generateCode();
}
- protected abstract KCode generateCode(boolean expand);
+ protected abstract KCode generateCode();
- public KCode toInverseCode(KCode value) {
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
throw new IllegalStateException("expression does not support two-way binding");
}
+ public abstract Expr cloneToModel(ExprModel model);
+
+ protected static List<Expr> cloneToModel(ExprModel model, List<Expr> exprs) {
+ ArrayList<Expr> clones = new ArrayList<Expr>();
+ for (Expr expr : exprs) {
+ clones.add(expr.cloneToModel(model));
+ }
+ return clones;
+ }
+
public void assertIsInvertible() {
final String errorMessage = getInvertibleError();
if (errorMessage != null) {
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
index f9f2c654..ba32042e 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
@@ -170,7 +170,7 @@ public class ExprModel {
}
public FieldAccessExpr observableField(Expr parent, String name) {
- return register(new FieldAccessExpr(parent, name, true));
+ return register(new ObservableFieldExpr(parent, name));
}
public SymbolExpr symbol(String text, Class type) {
@@ -402,7 +402,7 @@ public class ExprModel {
for (Expr expr : mExprMap.values()) {
if (expr instanceof FieldAccessExpr) {
FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) expr;
- if (fieldAccessExpr.getChild() instanceof ViewFieldExpr) {
+ if (fieldAccessExpr.getTarget() instanceof ViewFieldExpr) {
flagMapping.add(fieldAccessExpr.getUniqueKey());
fieldAccessExpr.setId(counter++);
}
@@ -682,6 +682,10 @@ public class ExprModel {
return register(new ListenerExpr(expression, name, listenerType, listenerMethod));
}
+ public FieldAssignmentExpr assignment(Expr target, String name, Expr value) {
+ return register(new FieldAssignmentExpr(target, name, value));
+ }
+
public Map<String, CallbackWrapper> getCallbackWrappers() {
return mCallbackWrappers;
}
@@ -696,7 +700,7 @@ public class ExprModel {
return wrapper;
}
- public Expr lambdaExpr(Expr expr, CallbackExprModel callbackExprModel) {
+ public LambdaExpr lambdaExpr(Expr expr, CallbackExprModel callbackExprModel) {
return register(new LambdaExpr(expr, callbackExprModel));
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java b/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
index 5d850b5d..bdfc9f84 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
@@ -35,6 +35,8 @@ import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.KCode;
+import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.List;
@@ -43,23 +45,15 @@ public class FieldAccessExpr extends Expr {
// notification name for the field. Important when we map this to a method w/ different name
String mBrName;
Callable mGetter;
- final boolean mIsObservableField;
boolean mIsListener;
boolean mIsViewAttributeAccess;
FieldAccessExpr(Expr parent, String name) {
super(parent);
mName = name;
- mIsObservableField = false;
- }
-
- FieldAccessExpr(Expr parent, String name, boolean isObservableField) {
- super(parent);
- mName = name;
- mIsObservableField = isObservableField;
}
- public Expr getChild() {
+ public Expr getTarget() {
return getChildren().get(0);
}
@@ -72,15 +66,15 @@ public class FieldAccessExpr extends Expr {
@Override
public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
- final List<ExecutionPath> targetPaths = getChild().toExecutionPath(paths);
+ final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths);
// after this, we need a null check.
List<ExecutionPath> result = new ArrayList<ExecutionPath>();
- if (getChild() instanceof StaticIdentifierExpr) {
- result.addAll(toExecutionPathInOrder(paths, getChild()));
+ if (getTarget() instanceof StaticIdentifierExpr) {
+ result.addAll(toExecutionPathInOrder(paths, getTarget()));
} else {
for (ExecutionPath path : targetPaths) {
final ComparisonExpr cmp = getModel()
- .comparison("!=", getChild(), getModel().symbol("null", Object.class));
+ .comparison("!=", getTarget(), getModel().symbol("null", Object.class));
path.addPath(cmp);
final ExecutionPath subPath = path.addBranch(cmp, true);
if (subPath != null) {
@@ -118,7 +112,7 @@ public class FieldAccessExpr extends Expr {
return true;
}
// if it is static final, gone
- if (getChild().isDynamic()) {
+ if (getTarget().isDynamic()) {
// if owner is dynamic, then we can be dynamic unless we are static final
return !mGetter.isStatic() || mGetter.isDynamic();
}
@@ -137,17 +131,14 @@ public class FieldAccessExpr extends Expr {
@Override
public Expr resolveListeners(ModelClass listener, Expr parent) {
- if (mName == null || mName.isEmpty()) {
- return this; // ObservableFields aren't listeners
- }
- final ModelClass childType = getChild().getResolvedType();
+ final ModelClass childType = getTarget().getResolvedType();
if (getGetter() == null) {
if (listener == null || !mIsListener) {
L.e("Could not resolve %s.%s as an accessor or listener on the attribute.",
childType.getCanonicalName(), mName);
return this;
}
- getChild().getParents().remove(this);
+ getTarget().getParents().remove(this);
} else if (listener == null) {
return this; // Not a listener, but we have a getter.
}
@@ -166,14 +157,14 @@ public class FieldAccessExpr extends Expr {
// Look for a signature matching the abstract method
final ModelMethod listenerMethod = abstractMethods.get(0);
final ModelClass[] listenerParameters = listenerMethod.getParameterTypes();
- boolean isStatic = getChild() instanceof StaticIdentifierExpr;
+ boolean isStatic = getTarget() instanceof StaticIdentifierExpr;
List<ModelMethod> methods = childType.findMethods(mName, isStatic);
for (ModelMethod method : methods) {
if (acceptsParameters(method, listenerParameters) &&
method.getReturnType(null).equals(listenerMethod.getReturnType(null))) {
resetResolvedType();
// replace this with ListenerExpr in parent
- Expr listenerExpr = getModel().listenerExpr(getChild(), mName, listener,
+ Expr listenerExpr = getModel().listenerExpr(getTarget(), mName, listener,
listenerMethod);
if (parent != null) {
int index;
@@ -217,7 +208,7 @@ public class FieldAccessExpr extends Expr {
protected List<Dependency> constructDependencies() {
final List<Dependency> dependencies = constructDynamicChildrenDependencies();
for (Dependency dependency : dependencies) {
- if (dependency.getOther() == getChild()) {
+ if (dependency.getOther() == getTarget()) {
dependency.setMandatory(true);
}
}
@@ -226,10 +217,7 @@ public class FieldAccessExpr extends Expr {
@Override
protected String computeUniqueKey() {
- if (mIsObservableField) {
- return addTwoWay(join(mName, "..", super.computeUniqueKey()));
- }
- return addTwoWay(join(mName, ".", super.computeUniqueKey()));
+ return join(mName, ".", super.computeUniqueKey());
}
public String getName() {
@@ -266,10 +254,10 @@ public class FieldAccessExpr extends Expr {
return modelAnalyzer.findClass(Object.class);
}
if (mGetter == null) {
- Expr child = getChild();
- child.getResolvedType();
- boolean isStatic = child instanceof StaticIdentifierExpr;
- ModelClass resolvedType = child.getResolvedType();
+ Expr target = getTarget();
+ target.getResolvedType();
+ boolean isStatic = target instanceof StaticIdentifierExpr;
+ ModelClass resolvedType = target.getResolvedType();
L.d("resolving %s. Resolved class type: %s", this, resolvedType);
mGetter = resolvedType.findGetterOrField(mName, isStatic);
@@ -284,25 +272,16 @@ public class FieldAccessExpr extends Expr {
if (mGetter.isStatic() && !isStatic) {
// found a static method on an instance. register a new one
- child.getParents().remove(this);
- getChildren().remove(child);
- StaticIdentifierExpr staticId = getModel().staticIdentifierFor(resolvedType);
- getChildren().add(staticId);
- staticId.getParents().add(this);
- child = getChild(); // replace the child for the next if stmt
+ replaceStaticIdentifier(resolvedType);
+ target = getTarget();
}
if (mGetter.resolvedType.isObservableField()) {
// Make this the ".get()" and add an extra field access for the observable field
- child.getParents().remove(this);
- getChildren().remove(child);
-
- FieldAccessExpr observableField = getModel().observableField(child, mName);
- observableField.mGetter = mGetter;
- if (hasBindableAnnotations()) {
- observableField.mBrName = ExtKt.br(BrNameUtil.brKey(mGetter));
- }
+ target.getParents().remove(this);
+ getChildren().remove(target);
+ FieldAccessExpr observableField = getModel().observableField(target, mName);
getChildren().add(observableField);
observableField.getParents().add(this);
mGetter = mGetter.resolvedType.findGetterOrField("", false);
@@ -315,9 +294,17 @@ public class FieldAccessExpr extends Expr {
return mGetter.resolvedType;
}
+ protected void replaceStaticIdentifier(ModelClass staticIdentifierType) {
+ getTarget().getParents().remove(this);
+ getChildren().remove(getTarget());
+ StaticIdentifierExpr staticId = getModel().staticIdentifierFor(staticIdentifierType);
+ getChildren().add(staticId);
+ staticId.getParents().add(this);
+ }
+
@Override
public Expr resolveTwoWayExpressions(Expr parent) {
- final Expr child = getChild();
+ final Expr child = getTarget();
if (!(child instanceof ViewFieldExpr)) {
return this;
}
@@ -390,25 +377,17 @@ public class FieldAccessExpr extends Expr {
@Override
protected String asPackage() {
- String parentPackage = getChild().asPackage();
+ String parentPackage = getTarget().asPackage();
return parentPackage == null ? null : parentPackage + "." + mName;
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
// once we can deprecate using Field.access for callbacks, we can get rid of this since
// it will be detected when resolve type is run.
Preconditions.checkNotNull(getGetter(), ErrorMessages.CANNOT_RESOLVE_TYPE, this);
- KCode code = new KCode();
- if (expand) {
- String defaultValue = ModelAnalyzer.getInstance().getDefaultValue(
- getResolvedType().toJavaCode());
- code.app("(", getChild().toCode(true))
- .app(" == null) ? ")
- .app(defaultValue)
- .app(" : ");
- }
- code.app("", getChild().toCode(expand)).app(".");
+ KCode code = new KCode()
+ .app("", getTarget().toCode()).app(".");
if (getGetter().type == Callable.Type.FIELD) {
return code.app(getGetter().name);
} else {
@@ -417,25 +396,27 @@ public class FieldAccessExpr extends Expr {
}
@Override
- public KCode toInverseCode(KCode value) {
- if (mGetter.setterName == null) {
- throw new IllegalStateException("There is no inverse for " + toCode().generate());
- }
- KCode castValue = new KCode("(").app(getResolvedType().toJavaCode() + ")(", value).app(")");
- String type = getChild().getResolvedType().toJavaCode();
- KCode code = new KCode("targetObj_.");
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ Expr castExpr = model.castExpr(getResolvedType().toJavaCode(), value);
+ Expr target = getTarget().cloneToModel(model);
+ Expr result;
if (getGetter().type == Callable.Type.FIELD) {
- code.app(getGetter().setterName).app(" = ", castValue).app(";");
+ result = model.assignment(target, mName, castExpr);
} else {
- code.app(getGetter().setterName).app("(", castValue).app(")").app(";");
+ result = model.methodCall(target, mGetter.setterName, Lists.newArrayList(castExpr));
}
- return new KCode()
- .app("final ")
- .app(type)
- .app(" targetObj_ = ", getChild().toCode(true))
- .app(";")
- .nl(new KCode("if (targetObj_ != null) {"))
- .tab(code)
- .nl(new KCode("}"));
+ return result;
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ final Expr clonedTarget = getTarget().cloneToModel(model);
+ return model.field(clonedTarget, mName);
+ }
+
+ @Override
+ public String toString() {
+ String name = mName.isEmpty() ? "get()" : mName;
+ return getTarget().toString() + '.' + name;
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/FieldAssignmentExpr.java b/compiler/src/main/java/android/databinding/tool/expr/FieldAssignmentExpr.java
new file mode 100644
index 00000000..a99df620
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/FieldAssignmentExpr.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.solver.ExecutionPath;
+import android.databinding.tool.writer.KCode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is used by inverse field access expressions to assign back to the field.
+ * For example, <code>&commat;={a.b}</code> is inverted to @{code a.b = value;}
+ */
+public class FieldAssignmentExpr extends Expr {
+ final String mName;
+
+ public FieldAssignmentExpr(Expr target, String name, Expr value) {
+ super(target, value);
+ mName = name;
+ }
+
+ @Override
+ protected String computeUniqueKey() {
+ return join(getTarget().getUniqueKey(), mName, "=", getValueExpr().getUniqueKey());
+ }
+
+ public Expr getTarget() {
+ return (FieldAccessExpr) getChildren().get(0);
+ }
+
+ public Expr getValueExpr() {
+ return getChildren().get(1);
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ return modelAnalyzer.findClass(void.class);
+ }
+
+ @Override
+ protected List<Dependency> constructDependencies() {
+ return constructDynamicChildrenDependencies();
+ }
+
+ @Override
+ protected KCode generateCode() {
+ return new KCode()
+ .app("", getTarget().toCode())
+ .app("." + mName + " = ", getValueExpr().toCode());
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.assignment(getTarget().cloneToModel(model), mName, getValueExpr());
+ }
+
+ @Override
+ protected String getInvertibleError() {
+ return "Assignment expressions are inverses of field access expressions.";
+ }
+
+ @Override
+ public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
+ Expr child = getTarget();
+ List<ExecutionPath> targetPaths = child.toExecutionPath(paths);
+
+ // after this, we need a null check.
+ List<ExecutionPath> result = new ArrayList<ExecutionPath>();
+ if (child instanceof StaticIdentifierExpr) {
+ result.addAll(toExecutionPathInOrder(paths, child));
+ } else {
+ for (ExecutionPath path : targetPaths) {
+ final ComparisonExpr cmp = getModel()
+ .comparison("!=", child, getModel().symbol("null", Object.class));
+ path.addPath(cmp);
+ final ExecutionPath subPath = path.addBranch(cmp, true);
+ if (subPath != null) {
+ subPath.addPath(this);
+ result.add(subPath);
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return getTarget().toString() + '.' + mName + " = " + getValueExpr();
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java b/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
index 9ff4f218..b866218a 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
@@ -23,6 +23,8 @@ import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.KCode;
import android.databinding.tool.writer.LayoutBinderWriterKt;
+import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.List;
@@ -78,12 +80,8 @@ public class IdentifierExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
- if (expand) {
- return new KCode(LayoutBinderWriterKt.getFieldName(this));
- } else {
- return new KCode(LayoutBinderWriterKt.scopedName(this));
- }
+ protected KCode generateCode() {
+ return new KCode(LayoutBinderWriterKt.scopedName(this));
}
public void setDeclared() {
@@ -100,7 +98,20 @@ public class IdentifierExpr extends Expr {
}
@Override
- public KCode toInverseCode(KCode value) {
- return new KCode().app(LayoutBinderWriterKt.getSetterName(this)).app("(", value).app(");");
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ String thisType = bindingClassName + ".this";
+ Expr target = model.builtInVariable(thisType, bindingClassName, thisType);
+ return model.methodCall(target, LayoutBinderWriterKt.getSetterName(this),
+ Lists.newArrayList(value));
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.identifier(mName);
+ }
+
+ @Override
+ public String toString() {
+ return mName;
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java b/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
index 980d6356..8783d0e7 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
@@ -37,14 +37,19 @@ public class InstanceOfExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode()
- .app("", getExpr().toCode(expand))
+ .app("", getExpr().toCode())
.app(" instanceof ")
.app(getType().toJavaCode());
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.instanceOfOp(getExpr().cloneToModel(model), mTypeStr);
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
mType = modelAnalyzer.findClass(mTypeStr, getModel().getImports());
return modelAnalyzer.loadPrimitive("boolean");
@@ -67,4 +72,9 @@ public class InstanceOfExpr extends Expr {
public String getInvertibleError() {
return "two-way binding can't target a value with the 'instanceof' operator";
}
+
+ @Override
+ public String toString() {
+ return getExpr().toString() + " instanceof " + mTypeStr;
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/LambdaExpr.java b/compiler/src/main/java/android/databinding/tool/expr/LambdaExpr.java
index e210f277..13c6cb72 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/LambdaExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/LambdaExpr.java
@@ -83,7 +83,7 @@ public class LambdaExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
Preconditions
.checkNotNull(mCallbackWrapper, "Cannot find the callback method for %s", this);
KCode code = new KCode("");
@@ -97,6 +97,11 @@ public class LambdaExpr extends Expr {
return code;
}
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.lambdaExpr(getExpr().cloneToModel(model), (CallbackExprModel) model);
+ }
+
public String generateConstructor() {
return getCallbackWrapper().constructForIdentifier(mCallbackId);
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java
index 6adf997c..3966f7f6 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java
@@ -81,7 +81,7 @@ public class ListenerExpr extends Expr {
}
@Override
- public KCode generateCode(boolean expand) {
+ public KCode generateCode() {
KCode code = new KCode("(");
final int minApi = Math.max(mListenerType.getMinApi(), mMethod.getMinApi());
if (minApi > 1) {
@@ -108,7 +108,17 @@ public class ListenerExpr extends Expr {
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.listenerExpr(getChild().cloneToModel(model), mName, mListenerType, mMethod);
+ }
+
+ @Override
public String getInvertibleError() {
return "Listeners cannot be the target of a two-way binding";
}
+
+ @Override
+ public String toString() {
+ return getChild().toString() + "::" + mName;
+ }
}
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 99e3257d..63222de3 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
@@ -18,12 +18,14 @@ package android.databinding.tool.expr;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.KCode;
import java.util.List;
public class MathExpr extends Expr {
final String mOp;
+
MathExpr(Expr left, String op, Expr right) {
super(left, right);
mOp = op;
@@ -31,7 +33,7 @@ public class MathExpr extends Expr {
@Override
protected String computeUniqueKey() {
- return addTwoWay(join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey()));
+ return join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey());
}
@Override
@@ -61,8 +63,12 @@ public class MathExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
- return new KCode().app("", getLeft().toCode(expand)).app(mOp, getRight().toCode(expand));
+ protected KCode generateCode() {
+ return new KCode().app("(", getLeft().toCode())
+ .app(") ")
+ .app(mOp)
+ .app(" (", getRight().toCode())
+ .app(")");
}
@Override
@@ -81,61 +87,50 @@ public class MathExpr extends Expr {
}
}
- private String inverseCast() {
- if (!getLeft().isDynamic()) {
- return inverseCast(getRight());
- } else {
- return inverseCast(getLeft());
- }
- }
-
- private String inverseCast(Expr expr) {
- if (!expr.getResolvedType().isAssignableFrom(getResolvedType())) {
- return "(" + getResolvedType() + ")";
- }
- return null;
- }
-
@Override
- public KCode toInverseCode(KCode value) {
- if (!isDynamic()) {
- return toCode();
- }
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
final Expr left = getLeft();
final Expr right = getRight();
- final Expr constExpr = left.isDynamic() ? right : left;
- final Expr varExpr = left.isDynamic() ? left : right;
- final String cast = inverseCast();
- if (cast != null) {
- value = new KCode(cast).app("(", value).app(")");
- }
+ Preconditions.check(left.isDynamic() ^ right.isDynamic(), "Two-way binding of a math " +
+ "operations requires A signle 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 newValue;
switch (mOp.charAt(0)) {
case '+': // const + x = value => x = value - const
- return varExpr.toInverseCode(value.app(" - (", constExpr.toCode()).app(")"));
+ newValue = model.math(value, "-", constExpr);
+ break;
case '*': // const * x = value => x = value / const
- return varExpr.toInverseCode(value.app(" / (", constExpr.toCode()).app(")"));
+ newValue = model.math(value, "/", constExpr);
+ break;
case '-':
- if (!left.isDynamic()) { // const - x = value => x = const - value)
- return varExpr.toInverseCode(new KCode()
- .app("(", constExpr.toCode())
- .app(") - (", value)
- .app(")"));
+ if (!left.isDynamic()) { // const - x = value => x = const - (value)
+ newValue = model.math(constExpr, "-", value);
} else { // x - const = value => x = value + const)
- return varExpr.toInverseCode(value.app(" + ", constExpr.toCode()));
+ newValue = model.math(value, "+", constExpr);
}
+ break;
case '/':
if (!left.isDynamic()) { // const / x = value => x = const / value
- return varExpr.toInverseCode(new KCode("(")
- .app("", constExpr.toCode())
- .app(") / (", value)
- .app(")"));
+ newValue = model.math(constExpr, "/", value);
} else { // x / const = value => x = value * const
- return varExpr.toInverseCode(new KCode("(")
- .app("", value)
- .app(") * (", constExpr.toCode())
- .app(")"));
+ newValue = model.math(value, "*", constExpr);
}
+ break;
+ default:
+ throw new IllegalStateException("Invalid math operation is not invertible: " + mOp);
}
- throw new IllegalStateException("Invalid math operation is not invertible: " + mOp);
+ final Expr varExpr = left.isDynamic() ? left : right;
+ return varExpr.generateInverse(model, newValue, bindingClassName);
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.math(getLeft().cloneToModel(model), mOp, getRight().cloneToModel(model));
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeft() + ") " + mOp + " (" + getRight() + ")";
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
index 71822fb5..6acbfa2e 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
@@ -36,8 +36,9 @@ import static android.databinding.tool.reflection.Callable.STATIC;
public class MethodCallExpr extends Expr {
final String mName;
-
Callable mGetter;
+ // Allow protected calls -- only used for ViewDataBinding methods.
+ private boolean mAllowProtected;
static List<Expr> concat(Expr e, List<Expr> list) {
List<Expr> merged = new ArrayList<Expr>();
@@ -64,18 +65,24 @@ public class MethodCallExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
KCode code = new KCode()
- .app("", getTarget().toCode(expand))
+ .app("", getTarget().toCode())
.app(".")
.app(getGetter().name)
.app("(");
- appendArgs(code, expand);
+ appendArgs(code);
code.app(")");
return code;
}
- private void appendArgs(KCode code, boolean expand) {
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.methodCall(getTarget().cloneToModel(model), mName,
+ cloneToModel(model, getArgs()));
+ }
+
+ private void appendArgs(KCode code) {
boolean first = true;
for (Expr arg : getArgs()) {
if (first) {
@@ -83,7 +90,7 @@ public class MethodCallExpr extends Expr {
} else {
code.app(", ");
}
- code.app("", arg.toCode(expand));
+ code.app("", arg.toCode());
}
}
@@ -122,12 +129,20 @@ public class MethodCallExpr extends Expr {
Expr target = getTarget();
boolean isStatic = target instanceof StaticIdentifierExpr;
- ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic);
+ ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic,
+ mAllowProtected);
if (method == null) {
- String message = "cannot find method '" + mName + "' in class " +
+ StringBuilder argTypes = new StringBuilder();
+ for (ModelClass arg : args) {
+ if (argTypes.length() != 0) {
+ argTypes.append(", ");
+ }
+ argTypes.append(arg.toJavaCode());
+ }
+ String message = "cannot find method '" + mName + "(" + argTypes + ")' in class " +
target.getResolvedType().toJavaCode();
IllegalArgumentException e = new IllegalArgumentException(message);
- L.e(e, "cannot find method %s in class %s", mName,
+ L.e(e, "cannot find method %s(%s) in class %s", mName, argTypes,
target.getResolvedType().toJavaCode());
throw e;
}
@@ -185,8 +200,31 @@ public class MethodCallExpr extends Expr {
return mGetter;
}
+ public void setAllowProtected() {
+ mAllowProtected = true;
+ }
+
@Override
public String getInvertibleError() {
return "Method calls may not be used in two-way expressions";
}
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(getTarget())
+ .append('.')
+ .append(mName)
+ .append('(');
+ final List<Expr> args = getArgs();
+ for (int i = 0; i < args.size(); i++) {
+ Expr arg = args.get(i);
+ if (i != 0) {
+ buf.append(", ");
+ }
+ buf.append(arg);
+ }
+ buf.append(')');
+ return buf.toString();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ObservableFieldExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ObservableFieldExpr.java
new file mode 100644
index 00000000..59b4712a
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/ObservableFieldExpr.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.expr;
+
+import android.databinding.tool.ext.ExtKt;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.util.BrNameUtil;
+import android.databinding.tool.util.L;
+
+public class ObservableFieldExpr extends FieldAccessExpr {
+
+ ObservableFieldExpr(Expr parent, String name) {
+ super(parent, name);
+ }
+
+ @Override
+ public Expr resolveListeners(ModelClass listener, Expr parent) {
+ return this; // ObservableFields aren't listeners
+ }
+
+ @Override
+ protected String computeUniqueKey() {
+ return join(mName, "..", super.computeUniqueKey());
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ if (mGetter == null) {
+ Expr target = getTarget();
+ target.getResolvedType();
+ boolean isStatic = target instanceof StaticIdentifierExpr;
+ ModelClass resolvedType = target.getResolvedType();
+ L.d("resolving %s. Resolved class type: %s", this, resolvedType);
+
+ mGetter = resolvedType.findGetterOrField(mName, isStatic);
+
+ if (mGetter == null) {
+ L.e("Could not find accessor %s.%s", resolvedType.getCanonicalName(), mName);
+ return null;
+ }
+
+ if (mGetter.isStatic() && !isStatic) {
+ // found a static method on an instance. register a new one
+ replaceStaticIdentifier(resolvedType);
+ }
+ if (hasBindableAnnotations()) {
+ mBrName = ExtKt.br(BrNameUtil.brKey(getGetter()));
+ } else {
+ mBrName = ExtKt.br(mName);
+ }
+ }
+ return mGetter.resolvedType;
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ final Expr clonedTarget = getTarget().cloneToModel(model);
+ return model.observableField(clonedTarget, mName);
+ }
+
+ @Override
+ public String toString() {
+ return getTarget().toString() + '.' + mName;
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
index 752cb9f8..2ecf5fb0 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
@@ -120,20 +120,22 @@ public class ResourceExpr extends Expr {
@Override
protected String computeUniqueKey() {
- String base;
- if (mPackage == null) {
- base = "@" + mResourceType + "/" + mResourceId;
- } else {
- base = "@" + "android:" + mResourceType + "/" + mResourceId;
- }
+ String base = toString();
return join(base, computeChildrenKey());
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode(toJava());
}
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ String pkg = mPackage.isEmpty() ? "" : "android";
+ return model.resourceExpr(pkg, mResourceType, mResourceId,
+ cloneToModel(model, getChildren()));
+ }
+
public String getResourceId() {
return mPackage + "R." + getResourceObject() + "." + mResourceId;
}
@@ -211,4 +213,13 @@ public class ResourceExpr extends Expr {
}
return rFileObject;
}
+
+ @Override
+ public String toString() {
+ if (mPackage == null) {
+ return "@" + mResourceType + "/" + mResourceId;
+ } else {
+ return "@" + "android:" + mResourceType + "/" + mResourceId;
+ }
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java b/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
index 7618e946..0d36f2a1 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
@@ -41,11 +41,17 @@ public class StaticIdentifierExpr extends IdentifierExpr {
}
@Override
- public KCode toInverseCode(KCode value) {
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
throw new IllegalStateException("StaticIdentifierExpr is not invertible.");
}
+
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode(getResolvedType().toJavaCode());
}
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.staticIdentifier(mName);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java b/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
index ee3a1677..388d2240 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
@@ -20,7 +20,6 @@ import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.solver.ExecutionPath;
import android.databinding.tool.writer.KCode;
-import android.databinding.tool.writer.LayoutBinderWriterKt;
import java.util.ArrayList;
import java.util.List;
@@ -55,11 +54,16 @@ public class SymbolExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode(getText());
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.symbol(mText, mType);
+ }
+
+ @Override
protected List<Dependency> constructDependencies() {
return new ArrayList<Dependency>();
}
@@ -76,4 +80,9 @@ public class SymbolExpr extends Expr {
}
return super.toExecutionPath(paths);
}
+
+ @Override
+ public String toString() {
+ return mText;
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
index a23e6c22..856cab04 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
@@ -124,26 +124,35 @@ public class TernaryExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
return new KCode()
- .app("", getPred().toCode(expand))
- .app(" ? ", getIfTrue().toCode(expand))
- .app(" : ", getIfFalse().toCode(expand));
+ .app("(", getPred().toCode())
+ .app(") ? (", getIfTrue().toCode())
+ .app(") : (", getIfFalse().toCode())
+ .app(")");
}
@Override
- public KCode toInverseCode(KCode variable) {
- return new KCode()
- .app("if (", getPred().toCode(true))
- .app(") {")
- .tab(getIfTrue().toInverseCode(variable))
- .nl(new KCode("} else {"))
- .tab(getIfFalse().toInverseCode(variable))
- .nl(new KCode("}"));
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ final Expr pred = getPred().cloneToModel(model);
+ final Expr ifTrue = getIfTrue().generateInverse(model, value, bindingClassName);
+ final Expr ifFalse = getIfFalse().generateInverse(model, value, bindingClassName);
+ return model.ternary(pred, ifTrue, ifFalse);
+ }
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.ternary(getPred().cloneToModel(model), getIfTrue().cloneToModel(model),
+ getIfFalse().cloneToModel(model));
}
@Override
public boolean isConditional() {
return true;
}
+
+ @Override
+ public String toString() {
+ return getPred().toString() + " ? " + getIfTrue() + " : " + getIfFalse();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java
index 1a656732..c14cdd6c 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java
@@ -45,12 +45,17 @@ public class TwoWayListenerExpr extends Expr {
}
@Override
- protected KCode generateCode(boolean expand) {
+ protected KCode generateCode() {
final String fieldName = LayoutBinderWriterKt.getFieldName(mInverseBinding);
return new KCode(fieldName);
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.twoWayListenerExpr(mInverseBinding);
+ }
+
+ @Override
protected String computeUniqueKey() {
return "event(" + mInverseBinding.getEventAttribute() + ", " +
System.identityHashCode(mInverseBinding) + ")";
diff --git a/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java b/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
index 881a352c..18e5985f 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
@@ -36,17 +36,22 @@ public class UnaryExpr extends Expr {
@Override
protected String computeUniqueKey() {
- return addTwoWay(join(getOpStr(), getExpr().getUniqueKey()));
+ return join(getOpStr(), getExpr().getUniqueKey());
}
@Override
- public KCode toInverseCode(KCode value) {
- return getExpr().toInverseCode(new KCode().app(mOp, value));
+ public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
+ return model.unary(mOp, getExpr().generateInverse(model, value, bindingClassName));
}
@Override
- protected KCode generateCode(boolean expand) {
- return new KCode().app(getOp(), getExpr().toCode(expand));
+ public Expr cloneToModel(ExprModel model) {
+ return model.unary(mOp, getExpr().cloneToModel(model));
+ }
+
+ @Override
+ protected KCode generateCode() {
+ return new KCode().app(getOp(), getExpr().toCode());
}
@Override
@@ -76,4 +81,9 @@ public class UnaryExpr extends Expr {
public Expr getExpr() {
return getChildren().get(0);
}
+
+ @Override
+ public String toString() {
+ return mOp + getExpr();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java
index 0a6b15b1..59c0dbeb 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java
@@ -25,7 +25,7 @@ public class ViewFieldExpr extends BuiltInVariableExpr {
private final BindingTarget mBindingTarget;
ViewFieldExpr(BindingTarget bindingTarget) {
- super(LayoutBinderWriterKt.getFieldName(bindingTarget), initialType(bindingTarget),
+ super(LayoutBinderWriterKt.getFieldName(bindingTarget), bindingTarget.getInterfaceType(),
LayoutBinderWriterKt.getFieldName(bindingTarget));
mBindingTarget = bindingTarget;
}
@@ -35,12 +35,6 @@ public class ViewFieldExpr extends BuiltInVariableExpr {
return "View fields may not be the target of two-way binding";
}
- private static String initialType(BindingTarget bindingTarget) {
- return bindingTarget.isBinder()
- ? "android.databinding.ViewDataBinding"
- : bindingTarget.getInterfaceType();
- }
-
public BindingTarget getBindingTarget() {
return mBindingTarget;
}
@@ -49,8 +43,13 @@ public class ViewFieldExpr extends BuiltInVariableExpr {
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
final ModelClass type = modelAnalyzer.findClass(mBindingTarget.getInterfaceType(), null);
if (type == null) {
- return modelAnalyzer.findClass("android.databinding.ViewDataBinding", null);
+ return modelAnalyzer.findClass(ModelAnalyzer.VIEW_DATA_BINDING, null);
}
return type;
}
+
+ @Override
+ public Expr cloneToModel(ExprModel model) {
+ return model.viewFieldExpr(mBindingTarget);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java
new file mode 100644
index 00000000..4759a9a6
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 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.reflection;
+
+import android.databinding.tool.util.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class that can be used by ModelAnalyzer without any backing model. This is used
+ * for ViewDataBinding subclasses that haven't been generated yet, but we still want
+ * to resolve methods and fields for them.
+ *
+ * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
+ */
+public class InjectedBindingClass extends ModelClass {
+ private final String mClassName;
+ private final String mSuperClass;
+ private final Map<String, String> mVariables;
+ private final Map<String, String> mFields;
+
+ public InjectedBindingClass(String className, String superClass, Map<String, String> variables,
+ Map<String, String> fields) {
+ mClassName = className;
+ mSuperClass = superClass;
+ mVariables = variables;
+ mFields = fields;
+ }
+
+ @Override
+ public String toJavaCode() {
+ return mClassName;
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public ModelClass getComponentType() {
+ return null;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return true;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public boolean isBoolean() {
+ return false;
+ }
+
+ @Override
+ public boolean isChar() {
+ return false;
+ }
+
+ @Override
+ public boolean isByte() {
+ return false;
+ }
+
+ @Override
+ public boolean isShort() {
+ return false;
+ }
+
+ @Override
+ public boolean isInt() {
+ return false;
+ }
+
+ @Override
+ public boolean isLong() {
+ return false;
+ }
+
+ @Override
+ public boolean isFloat() {
+ return false;
+ }
+
+ @Override
+ public boolean isDouble() {
+ return false;
+ }
+
+ @Override
+ public boolean isGeneric() {
+ return false;
+ }
+
+ @Override
+ public List<ModelClass> getTypeArguments() {
+ return null;
+ }
+
+ @Override
+ public boolean isTypeVar() {
+ return false;
+ }
+
+ @Override
+ public boolean isWildcard() {
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public boolean isVoid() {
+ return false;
+ }
+
+ @Override
+ public ModelClass unbox() {
+ return this;
+ }
+
+ @Override
+ public ModelClass box() {
+ return this;
+ }
+
+ @Override
+ public boolean isObservable() {
+ return getSuperclass().isObservable();
+ }
+
+ @Override
+ public boolean isAssignableFrom(ModelClass that) {
+ ModelClass superClass = that;
+ while (superClass != null && !superClass.isObject()) {
+ if (superClass.toJavaCode().equals(mClassName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public ModelClass getSuperclass() {
+ return ModelAnalyzer.getInstance().findClass(mSuperClass, null);
+ }
+
+ @Override
+ public ModelClass erasure() {
+ return this;
+ }
+
+ @Override
+ public String getJniDescription() {
+ return TypeUtil.getInstance().getDescription(this);
+ }
+
+ @Override
+ protected ModelField[] getDeclaredFields() {
+ ModelClass superClass = getSuperclass();
+ final ModelField[] superFields = superClass.getDeclaredFields();
+ final int fieldCount = superFields.length + 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);
+ }
+ return fields;
+ }
+
+ @Override
+ protected ModelMethod[] getDeclaredMethods() {
+ ModelClass superClass = getSuperclass();
+ final ModelMethod[] superMethods = superClass.getDeclaredMethods();
+ final int methodCount = superMethods.length + (mVariables.size() * 2);
+ 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);
+ }
+ return methods;
+ }
+
+ @Override
+ public String toString() {
+ return "Injected Class: " + mClassName;
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java
new file mode 100644
index 00000000..d15cc908
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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.reflection;
+
+import java.util.Map;
+
+/**
+ * A class that can be used by ModelAnalyzer without any backing model. This is used
+ * for fields on ViewDataBinding subclasses that haven't been generated yet.
+ *
+ * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
+ */
+public class InjectedBindingClassField extends ModelField {
+ private final String mType;
+ private final String mName;
+
+ public InjectedBindingClassField(String name, String type) {
+ mName = name;
+ mType = type;
+ }
+
+ @Override
+ public boolean isBindable() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public boolean isPublic() {
+ return true;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return true;
+ }
+
+ @Override
+ public ModelClass getFieldType() {
+ return ModelAnalyzer.getInstance().findClass(mType, null);
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java
new file mode 100644
index 00000000..20e24824
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 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.reflection;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class that can be used by ModelAnalyzer without any backing model. This is used
+ * for methods on ViewDataBinding subclasses that haven't been generated yet.
+ *
+ * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map)
+ */
+public class InjectedBindingClassMethod extends ModelMethod {
+ private final InjectedBindingClass mContainingClass;
+ private final String mName;
+ private final String mReturnType;
+ private final String mParameter;
+
+ public InjectedBindingClassMethod(InjectedBindingClass containingClass, String name, String returnType,
+ String parameter) {
+ mContainingClass = containingClass;
+ mName = name;
+ mReturnType = returnType;
+ mParameter = parameter;
+ }
+
+ @Override
+ public ModelClass getDeclaringClass() {
+ return mContainingClass;
+ }
+
+ @Override
+ public ModelClass[] getParameterTypes() {
+ if (mParameter != null) {
+ ModelClass parameterType = ModelAnalyzer.getInstance().findClass(mParameter, null);
+ return new ModelClass[] { parameterType };
+ }
+ return new ModelClass[0];
+ }
+
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public ModelClass getReturnType(List<ModelClass> args) {
+ ModelClass returnType = ModelAnalyzer.getInstance().findClass(mReturnType, null);
+ return returnType;
+ }
+
+ @Override
+ public boolean isVoid() {
+ return getReturnType().isVoid();
+ }
+
+ @Override
+ public boolean isPublic() {
+ return true;
+ }
+
+ @Override
+ public boolean isProtected() {
+ return false;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return true;
+ }
+
+ @Override
+ public boolean isBindable() {
+ return false;
+ }
+
+ @Override
+ public int getMinApi() {
+ return 0;
+ }
+
+ @Override
+ public String getJniDescription() {
+ return TypeUtil.getInstance().getDescription(this);
+ }
+
+ @Override
+ public boolean isVarArgs() {
+ return false;
+ }
+}
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 6b4bf876..8943200b 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -19,6 +19,7 @@ import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
+import java.util.HashMap;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
@@ -82,6 +83,8 @@ public abstract class ModelAnalyzer {
private ModelClass mViewStubType;
private static ModelAnalyzer sAnalyzer;
+ private final Map<String, InjectedBindingClass> mInjectedClasses =
+ new HashMap<String, InjectedBindingClass>();
protected void setInstance(ModelAnalyzer analyzer) {
sAnalyzer = analyzer;
@@ -218,12 +221,27 @@ public abstract class ModelAnalyzer {
return "null";
}
- public abstract ModelClass findClass(String className, Map<String, String> imports);
+ public final ModelClass findClass(String className, Map<String, String> imports) {
+ if (mInjectedClasses.containsKey(className)) {
+ return mInjectedClasses.get(className);
+ }
+ return findClassInternal(className, imports);
+ }
+
+ public abstract ModelClass findClassInternal(String className, Map<String, String> imports);
public abstract ModelClass findClass(Class classType);
public abstract TypeUtil createTypeUtil();
+ public ModelClass injectViewDataBinding(String className, Map<String, String> variables,
+ Map<String, String> fields) {
+ InjectedBindingClass injectedClass = new InjectedBindingClass(className,
+ ModelAnalyzer.VIEW_DATA_BINDING, variables, fields);
+ mInjectedClasses.put(className, injectedClass);
+ return injectedClass;
+ }
+
ModelClass[] getListTypes() {
if (mListTypes == null) {
mListTypes = new ModelClass[LIST_CLASS_NAMES.length];
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
index 35eaf6fa..243fcdee 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
@@ -235,23 +235,27 @@ public abstract class ModelClass {
public abstract boolean isAssignableFrom(ModelClass that);
/**
- * Returns an array containing all public methods on the type represented by this ModelClass
- * with the name <code>name</code> and can take the passed-in types as arguments. This will
- * also work if the arguments match VarArgs parameter.
+ * Returns an array containing all public methods (or protected if allowProtected is true)
+ * on the type represented by this ModelClass with the name <code>name</code> and can
+ * take the passed-in types as arguments. This will also work if the arguments match
+ * VarArgs parameter.
*
* @param name The name of the method to find.
* @param args The types that the method should accept.
* @param staticOnly Whether only static methods should be returned or both instance methods
* and static methods are valid.
+ * @param allowProtected true if the method can be protected as well as public.
*
* @return An array containing all public methods with the name <code>name</code> and taking
* <code>args</code> parameters.
*/
- public ModelMethod[] getMethods(String name, List<ModelClass> args, boolean staticOnly) {
+ public ModelMethod[] getMethods(String name, List<ModelClass> args, boolean staticOnly,
+ boolean allowProtected) {
ModelMethod[] methods = getDeclaredMethods();
ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
for (ModelMethod method : methods) {
- if (method.isPublic() && (!staticOnly || method.isStatic()) &&
+ if ((method.isPublic() || (allowProtected && method.isProtected())) &&
+ (!staticOnly || method.isStatic()) &&
name.equals(method.getName()) && method.acceptsArguments(args)) {
matching.add(method);
}
@@ -288,9 +292,11 @@ public abstract class ModelClass {
* @param args The arguments that the method should accept
* @param staticOnly true if the returned method must be static or false if it does not
* matter.
+ * @param allowProtected true if the method can be protected as well as public.
*/
- public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticOnly) {
- ModelMethod[] methods = getMethods(name, args, staticOnly);
+ public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticOnly,
+ boolean allowProtected) {
+ ModelMethod[] methods = getMethods(name, args, staticOnly, allowProtected);
L.d("looking methods for %s. static only ? %s . method count: %d", name, staticOnly,
methods.length);
for (ModelMethod method : methods) {
@@ -397,7 +403,8 @@ public abstract class ModelClass {
name
};
for (String methodName : methodNames) {
- ModelMethod[] methods = getMethods(methodName, new ArrayList<ModelClass>(), staticOnly);
+ ModelMethod[] methods =
+ getMethods(methodName, new ArrayList<ModelClass>(), staticOnly, false);
for (ModelMethod method : methods) {
if (method.isPublic() && (!staticOnly || method.isStatic()) &&
!method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
@@ -465,7 +472,8 @@ public abstract class ModelClass {
name
};
for (String methodName : methodNames) {
- ModelMethod[] methods = getMethods(methodName, new ArrayList<ModelClass>(), false);
+ ModelMethod[] methods =
+ getMethods(methodName, new ArrayList<ModelClass>(), false, false);
for (ModelMethod method : methods) {
if (method.isPublic() && !method.isStatic() &&
!method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
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 87ae28d4..5bd214e3 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
@@ -32,6 +32,8 @@ public abstract class ModelMethod {
public abstract boolean isPublic();
+ public abstract boolean isProtected();
+
public abstract boolean isStatic();
public abstract boolean isAbstract();
@@ -76,6 +78,9 @@ public abstract class ModelMethod {
for (int i = 0; i < args.size(); i++) {
ModelClass parameterType = getParameter(i, parameterTypes);
ModelClass arg = args.get(i);
+ if (parameterType.isIncomplete()) {
+ parameterType = parameterType.erasure();
+ }
if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) {
parametersMatch = false;
break;
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
index ef52880b..80664cda 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
@@ -26,7 +26,6 @@ import java.util.Map;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
@@ -85,7 +84,7 @@ public class AnnotationAnalyzer extends ModelAnalyzer {
}
@Override
- public AnnotationClass findClass(String className, Map<String, String> imports) {
+ public ModelClass findClassInternal(String className, Map<String, String> imports) {
className = className.trim();
int numDimensions = 0;
while (className.endsWith("[]")) {
@@ -121,7 +120,8 @@ public class AnnotationAnalyzer extends ModelAnalyzer {
ArrayList<String> templateParameters = splitTemplateParameters(paramStr);
TypeMirror[] typeArgs = new TypeMirror[templateParameters.size()];
for (int i = 0; i < typeArgs.length; i++) {
- final AnnotationClass clazz = findClass(templateParameters.get(i), imports);
+ final AnnotationClass clazz = (AnnotationClass)
+ findClass(templateParameters.get(i), imports);
if (clazz == null) {
L.e("cannot find type argument for %s in %s", templateParameters.get(i),
baseClassName);
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
index ce17c4b0..feae09db 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
@@ -286,13 +286,17 @@ class AnnotationClass extends ModelClass {
@Override
public boolean isAssignableFrom(ModelClass that) {
- if (that == null) {
+ ModelClass other = that;
+ while (other != null && !(other instanceof AnnotationClass)) {
+ other = other.getSuperclass();
+ }
+ if (other == null) {
return false;
}
- if (equals(that)) {
+ if (equals(other)) {
return true;
}
- AnnotationClass thatAnnotationClass = (AnnotationClass) that;
+ AnnotationClass thatAnnotationClass = (AnnotationClass) other;
return getTypeUtils().isAssignable(thatAnnotationClass.mTypeMirror, this.mTypeMirror);
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
index d7caa452..66c1dbc5 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
@@ -127,6 +127,11 @@ class AnnotationMethod extends ModelMethod {
}
@Override
+ public boolean isProtected() {
+ return mExecutableElement.getModifiers().contains(Modifier.PROTECTED);
+ }
+
+ @Override
public boolean isStatic() {
return mExecutableElement.getModifiers().contains(Modifier.STATIC);
}
diff --git a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
index 95688100..90f75e2f 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -714,7 +714,7 @@ public class SetterStore {
if (viewType == null) {
return null;
} else if (viewType.isViewDataBinding()) {
- return new ViewDataBindingGetterCall(attribute);
+ return new ViewDataBindingGetterCall(viewType, attribute);
}
attribute = stripNamespace(attribute);
@@ -758,11 +758,11 @@ public class SetterStore {
viewType.getCanonicalName());
} else {
bestMethod.call = new AdapterGetter(inverseDescription,
- setters.get(0));
+ setters.get(0), key.valueType);
}
} else {
bestMethod.call = new AdapterGetter(inverseDescription,
- eventCall);
+ eventCall, key.valueType);
}
}
@@ -1271,6 +1271,7 @@ public class SetterStore {
}
private static class IntermediateV2 extends IntermediateV1 {
+ private static final long serialVersionUID = 0xA45C2EB637E35C07L;
public final HashMap<String, HashMap<AccessorKey, InverseDescription>> inverseAdapters =
new HashMap<String, HashMap<AccessorKey, InverseDescription>>();
public final HashMap<String, HashMap<String, InverseDescription>> inverseMethods =
@@ -1635,6 +1636,8 @@ public class SetterStore {
public interface BindingGetterCall {
String toJava(String componentExpression, String viewExpression);
+ String getGetterType();
+
int getMinApi();
String getBindingAdapterInstanceClass();
@@ -1650,12 +1653,14 @@ public class SetterStore {
private final String mGetter;
private final BindingSetterCall mEventSetter;
private final String mAttribute;
+ private final ModelClass mBindingClass;
- public ViewDataBindingGetterCall(String attribute) {
+ public ViewDataBindingGetterCall(ModelClass bindingClass, String attribute) {
final int colonIndex = attribute.indexOf(':');
mAttribute = attribute.substring(colonIndex + 1);
mGetter = "get" + StringUtils.capitalize(mAttribute);
mEventSetter = new ViewDataBindingEventSetter();
+ mBindingClass = bindingClass;
}
@Override
@@ -1664,6 +1669,11 @@ public class SetterStore {
}
@Override
+ public String getGetterType() {
+ return mBindingClass.findInstanceGetter(mGetter).getReturnType().toJavaCode();
+ }
+
+ @Override
public int getMinApi() {
return 0;
}
@@ -1716,6 +1726,11 @@ public class SetterStore {
}
@Override
+ public String getGetterType() {
+ return mMethod.getReturnType().toJavaCode();
+ }
+
+ @Override
public int getMinApi() {
return mMethod.getMinApi();
}
@@ -1734,10 +1749,18 @@ public class SetterStore {
private final InverseDescription mInverseDescription;
private String mBindingAdapterCall;
private final BindingSetterCall mEventCall;
+ private final String mGetterType;
- public AdapterGetter(InverseDescription description, BindingSetterCall eventCall) {
+ public AdapterGetter(InverseDescription description, BindingSetterCall eventCall,
+ String getterType) {
mInverseDescription = description;
mEventCall = eventCall;
+ mGetterType = getterType;
+ }
+
+ @Override
+ public String getGetterType() {
+ return mGetterType;
}
@Override
diff --git a/compiler/src/main/kotlin/android/databinding/tool/expr/ExprWriters.kt b/compiler/src/main/kotlin/android/databinding/tool/expr/ExprWriters.kt
index 618c1413..84884516 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/expr/ExprWriters.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/expr/ExprWriters.kt
@@ -30,7 +30,7 @@ fun Expr.shouldLocalizeInCallbacks() = canBeEvaluatedToAVariable() && !resolvedT
fun CallbackExprModel.localizeGlobalVariables(vararg ignore: Expr): KCode = kcode("// localize variables for thread safety") {
// puts all variables in this model to local values.
mExprMap.values.filter { it.shouldLocalizeInCallbacks() && !ignore.contains(it) }.forEach {
- nl("// ${it.uniqueKey}")
+ nl("// ${it.toString()}")
nl("${it.resolvedType.toJavaCode()} ${it.scopedName()} = ${if (it.isVariable()) it.fieldName else it.defaultValue};")
}
}
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 51c1cc95..7809f5b6 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
@@ -206,7 +206,6 @@ val Expr.callbackLocalName by lazyProp { expr : Expr ->
else expr.toCode().generate()
}
-
val Expr.executePendingLocalName by lazyProp { expr : Expr ->
if(expr.needsLocalField) "${expr.model.ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD, false)}"
else expr.toCode().generate()
@@ -366,7 +365,12 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
nl(declareVariables())
nl(declareBoundValues())
nl(declareListeners())
- nl(declareInverseBindingImpls());
+ try {
+ Scope.enter(Scope.GLOBAL)
+ nl(declareInverseBindingImpls());
+ } finally {
+ Scope.exit()
+ }
nl(declareConstructor(minSdk))
nl(declareInvalidateAll())
nl(declareHasPendingBindings())
@@ -853,14 +857,30 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
className = "android.databinding.InverseBindingListener"
param = ""
}
- nl("private $className ${inverseBinding.fieldName} = new $className($param) {") {
- tab("@Override")
- tab("public void onChange() {") {
- tab(inverseBinding.toJavaCode("mBindingComponent", mDirtyFlags)).app(";");
+ block("private $className ${inverseBinding.fieldName} = new $className($param)") {
+ nl("@Override")
+ block("public void onChange()") {
+ if (inverseBinding.inverseExpr != null) {
+ val valueExpr = inverseBinding.variableExpr
+ val getterCall = inverseBinding.getterCall
+ nl("// Inverse of ${inverseBinding.expr}")
+ nl("// is ${inverseBinding.inverseExpr}")
+ nl("${valueExpr.resolvedType.toJavaCode()} ${valueExpr.name} = ${getterCall.toJava("mBindingComponent", target.fieldName)};")
+ nl(inverseBinding.callbackExprModel.localizeGlobalVariables(valueExpr))
+ nl(inverseBinding.executionPath.toCode())
+ } else {
+ block("synchronized(this)") {
+ val flagSet = inverseBinding.chainedExpressions.fold(FlagSet(), { initial, expr ->
+ initial.or(FlagSet(expr.id))
+ })
+ mDirtyFlags.mapOr(flagSet) { suffix, index ->
+ tab("${mDirtyFlags.localValue(index)} |= ${flagSet.binaryCode(index)};")
+ }
+ }
+ nl("requestRebind();")
+ }
}
- tab("}")
- }
- nl("};")
+ }.app(";")
}
}
}
@@ -999,7 +1019,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
if (!assignedValues.isEmpty()) {
val assignment = kcode("") {
assignedValues.forEach { expr: Expr ->
- tab("// read ${expr.uniqueKey}")
+ tab("// read ${expr}")
tab("${expr.executePendingLocalName}").app(" = ", expr.toFullCode()).app(";")
}
}
@@ -1018,7 +1038,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
it.value.forEach { expr: Expr ->
justRead.add(expr)
- L.d("%s / readWithDependants %s", className, expr.uniqueKey);
+ L.d("%s / readWithDependants %s", className, expr);
L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper);
// if I am the condition for an expression, set its flag
diff --git a/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java b/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java
index 97331cfa..84b032af 100644
--- a/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java
+++ b/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java
@@ -143,8 +143,8 @@ public class ExpressionVisitorTest {
@Test
public void testInheritedFieldResolution() {
final FieldAccessExpr parsed = parse("myStr.length", FieldAccessExpr.class);
- assertTrue(parsed.getChild() instanceof IdentifierExpr);
- final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
+ assertTrue(parsed.getTarget() instanceof IdentifierExpr);
+ final IdentifierExpr id = (IdentifierExpr) parsed.getTarget();
id.setUserDefinedType("java.lang.String");
assertEquals(new JavaClass(int.class), parsed.getResolvedType());
Callable getter = parsed.getGetter();
@@ -159,8 +159,8 @@ public class ExpressionVisitorTest {
@Test
public void testGetterResolution() {
final FieldAccessExpr parsed = parse("myStr.bytes", FieldAccessExpr.class);
- assertTrue(parsed.getChild() instanceof IdentifierExpr);
- final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
+ assertTrue(parsed.getTarget() instanceof IdentifierExpr);
+ final IdentifierExpr id = (IdentifierExpr) parsed.getTarget();
id.setUserDefinedType("java.lang.String");
assertEquals(new JavaClass(byte[].class), parsed.getResolvedType());
Callable getter = parsed.getGetter();
diff --git a/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java b/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
index 8b1f820d..c7f81ab1 100644
--- a/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
+++ b/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
@@ -77,8 +77,8 @@ public class LayoutBinderTest {
int originalSize = mExprModel.size();
mLayoutBinder.addVariable("user", "android.databinding.tool2.LayoutBinderTest.TestUser",
null);
- mLayoutBinder.parse("user.name", false, null);
- mLayoutBinder.parse("user.lastName", false, null);
+ mLayoutBinder.parse("user.name", null);
+ mLayoutBinder.parse("user.lastName", null);
assertEquals(originalSize + 3, mExprModel.size());
final List<Expr> bindingExprs = mExprModel.getBindingExpressions();
assertEquals(2, bindingExprs.size());
@@ -94,7 +94,7 @@ public class LayoutBinderTest {
public void testParseWithMethods() {
mLayoutBinder.addVariable("user", "android.databinding.tool.LayoutBinderTest.TestUser",
null);
- mLayoutBinder.parse("user.fullName", false, null);
+ mLayoutBinder.parse("user.fullName", null);
Expr item = mExprModel.getBindingExpressions().get(0);
assertTrue(item instanceof FieldAccessExpr);
IdentifierExpr id = mExprModel.identifier("user");
@@ -102,7 +102,7 @@ public class LayoutBinderTest {
fa.getResolvedType();
final Callable getter = fa.getGetter();
assertTrue(getter.type == Callable.Type.METHOD);
- assertSame(id, fa.getChild());
+ assertSame(id, fa.getTarget());
assertTrue(fa.isDynamic());
}
diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExecutionPathTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExecutionPathTest.java
index 4f31ecaa..2bb8832f 100644
--- a/compiler/src/test/java/android/databinding/tool/expr/ExecutionPathTest.java
+++ b/compiler/src/test/java/android/databinding/tool/expr/ExecutionPathTest.java
@@ -57,7 +57,7 @@ public class ExecutionPathTest {
public void simpleExpr() {
MockLayoutBinder lb = new MockLayoutBinder();
ExprModel model = lb.getModel();
- Expr parsed = lb.parse(mExpression, false, null);
+ Expr parsed = lb.parse(mExpression, null);
List<ExecutionPath> paths = new ArrayList<ExecutionPath>();
ExecutionPath root = ExecutionPath.createRoot();
paths.add(root);
diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
index 6a89cb6a..20d523f6 100644
--- a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
+++ b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
@@ -75,11 +75,16 @@ public class ExprModelTest {
}
@Override
- protected KCode generateCode(boolean full) {
+ protected KCode generateCode() {
return new KCode();
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return this;
+ }
+
+ @Override
protected String getInvertibleError() {
return "DummyExpr cannot be 2-way.";
}
@@ -149,7 +154,7 @@ public class ExprModelTest {
IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
- lb.parse("a == null ? b : c", false, null);
+ lb.parse("a == null ? b : c", null);
mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class));
lb.getModel().seal();
List<Expr> shouldRead = getShouldRead();
@@ -298,7 +303,7 @@ public class ExprModelTest {
IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
IdentifierExpr d = lb.addVariable("d", "java.lang.String", null);
IdentifierExpr e = lb.addVariable("e", "java.lang.String", null);
- final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", false, null);
+ final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", null);
assertTrue(aTernary instanceof TernaryExpr);
final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue();
assertTrue(bTernary instanceof TernaryExpr);
@@ -796,7 +801,7 @@ public class ExprModelTest {
assertFalse(fieldAccess.isDynamic());
mExprModel.seal();
assertEquals(0, getShouldRead().size());
- final Expr child = fieldAccess.getChild();
+ final Expr child = fieldAccess.getTarget();
assertTrue(child instanceof StaticIdentifierExpr);
StaticIdentifierExpr id = (StaticIdentifierExpr) child;
assertEquals(id.getResolvedType().getCanonicalName(), "android.view.View");
@@ -1058,7 +1063,7 @@ public class ExprModelTest {
}
private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) {
- final Expr parsed = binder.parse(input, false, null);
+ final Expr parsed = binder.parse(input, null);
assertTrue(klass.isAssignableFrom(parsed.getClass()));
return (T) parsed;
}
diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
index 61d04cb6..6966dd49 100644
--- a/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
+++ b/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
@@ -56,11 +56,16 @@ public class ExprTest{
}
@Override
- protected KCode generateCode(boolean full) {
+ protected KCode generateCode() {
return new KCode();
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return this;
+ }
+
+ @Override
protected String getInvertibleError() {
return null;
}
@@ -90,11 +95,16 @@ public class ExprTest{
}
@Override
- protected KCode generateCode(boolean full) {
+ protected KCode generateCode() {
return new KCode();
}
@Override
+ public Expr cloneToModel(ExprModel model) {
+ return this;
+ }
+
+ @Override
protected String getInvertibleError() {
return null;
}
diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
index 1b97cd9f..695e04b8 100644
--- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
+++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
@@ -13,17 +13,17 @@
package android.databinding.tool.reflection.java;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-
-import org.apache.commons.io.FileUtils;
-
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.reflection.SdkUtil;
import android.databinding.tool.reflection.TypeUtil;
import android.databinding.tool.util.L;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+
+import org.apache.commons.io.FileUtils;
+
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
@@ -72,7 +72,7 @@ public class JavaAnalyzer extends ModelAnalyzer {
}
@Override
- public ModelClass findClass(String className, Map<String, String> imports) {
+ public ModelClass findClassInternal(String className, Map<String, String> imports) {
// TODO handle imports
JavaClass loaded = mClassCache.get(className);
if (loaded != null) {
diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java
index 0d00c574..b7b626c1 100644
--- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java
+++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java
@@ -68,6 +68,11 @@ public class JavaMethod extends ModelMethod {
}
@Override
+ public boolean isProtected() {
+ return Modifier.isProtected(mMethod.getModifiers());
+ }
+
+ @Override
public boolean isStatic() {
return Modifier.isStatic(mMethod.getModifiers());
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java b/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
index fa773745..746729ec 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
@@ -22,7 +22,7 @@ public class ErrorMessages {
public static final String UNDEFINED_VARIABLE =
"Identifiers must have user defined types from the XML file. %s is missing it";
public static final String CANNOT_FIND_SETTER_CALL =
- "Cannot find the setter for attribute '%s' with parameter type %s.";
+ "Cannot find the setter for attribute '%s' with parameter type %s on %s.";
public static final String CANNOT_RESOLVE_TYPE =
"Cannot resolve type for %s";
public static final String MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH =
diff --git a/databinding.properties b/databinding.properties
index 95277f2f..77cf3068 100644
--- a/databinding.properties
+++ b/databinding.properties
@@ -1,6 +1,6 @@
# global settings for projects
kotlinVersion = 1.0.0
-extensionsVersion = 1.0-rc5
+extensionsVersion = 1.1
# we use a public plugin so that it does not need data binding while compiling library
androidPublicPluginVersion= 1.5.0
javaTargetCompatibility = 1.6