diff options
author | George Mount <mount@google.com> | 2016-09-28 19:21:41 +0000 |
---|---|---|
committer | George Mount <mount@google.com> | 2016-09-28 12:48:34 -0700 |
commit | e468b817509c5c7dd657a0cbc997452e1106aed8 (patch) | |
tree | aa194fa6a5d607d7eda7d4e11dfc2578a7eec05b /compiler | |
parent | 83bcfb874866a2f6f74f7700fe2c67f0af2a3c20 (diff) | |
download | data-binding-e468b817509c5c7dd657a0cbc997452e1106aed8.tar.gz |
Re-adds "Add support for dependent bindable properties."
This reverts commit 729298e98a96730a9fbcf8c1d2575a8f0da7cc70.
This also fixes the build break caused by the added abstract
methods in ModelMethod and ModelField.
Change-Id: I4d7d5085a1a2e02ede0662591d8ccd79770c5fb3
Diffstat (limited to 'compiler')
14 files changed, 130 insertions, 8 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java index 2395f23a..5f193a3e 100644 --- a/compiler/src/main/java/android/databinding/tool/Binding.java +++ b/compiler/src/main/java/android/databinding/tool/Binding.java @@ -156,7 +156,7 @@ public class Binding implements LocationScopeProvider { // Now try with the value object directly mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName, viewType, mExpr.getResolvedType(), mExpr.getModel().getImports()); - if (warn) { + if (warn && mSetterCall != null) { L.w(ErrorMessages.OBSERVABLE_FIELD_USE, mSetterCall.getDescription()); } } 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 be48cd4a..d4a54de0 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java +++ b/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java @@ -16,6 +16,7 @@ package android.databinding.tool.expr; +import android.databinding.Bindable; import android.databinding.tool.Binding; import android.databinding.tool.BindingTarget; import android.databinding.tool.InverseBinding; @@ -26,16 +27,20 @@ import android.databinding.tool.reflection.Callable; import android.databinding.tool.reflection.Callable.Type; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelField; import android.databinding.tool.store.SetterStore; import android.databinding.tool.store.SetterStore.BindingGetterCall; import android.databinding.tool.util.BrNameUtil; import android.databinding.tool.util.L; import android.databinding.tool.util.Preconditions; +import android.databinding.tool.util.StringUtils; import android.databinding.tool.writer.KCode; import com.google.common.collect.Lists; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; public class FieldAccessExpr extends MethodBaseExpr { // notification name for the field. Important when we map this to a method w/ different name @@ -157,6 +162,70 @@ public class FieldAccessExpr extends MethodBaseExpr { } } + /** + * @return The list of all properties that dirty this expression. This will also contain the + * the BR identifier for this property if there is one. If the property is not bindable, + * it will contain an empty list. + */ + public String[] getDirtyingProperties() { + String[] names = null; + if (mGetter != null && mGetter.canBeInvalidated()) { + if (mGetter.type == Type.FIELD) { + if (mGetter.bindableAnnotation != null && + mGetter.bindableAnnotation.value().length != 0) { + L.e("Bindable annotation with property names is only supported on methods. " + + "Field '%s.%s' has @Bindable(\"%s\")", + getTarget().getResolvedType().toJavaCode(), mGetter.name, + StringUtils.join(mGetter.bindableAnnotation.value(), "\", \"")); + } + } else if (mGetter.method != null && mGetter.canBeInvalidated() && + mGetter.bindableAnnotation != null) { + String[] dependencies = mGetter.bindableAnnotation.value(); + validateDependencies(dependencies); + names = new String[dependencies.length + 1]; + for (int i = 0; i < dependencies.length; i++) { + names[i] = "BR." + dependencies[i]; + } + names[dependencies.length] = getBrName(); + } + } + if (names == null) { + String br = getBrName(); + if (br == null) { + names = new String[0]; + } else { + names = new String[] { br }; + } + } + return names; + } + + /** + * Validates the dependent properties -- they must exist and be Bindable. + */ + private void validateDependencies(String[] dependencies) { + try { + Scope.enter(this); + Arrays.stream(dependencies).forEach(field -> { + ModelClass resolvedType = getTarget().getResolvedType(); + Callable getter = resolvedType.findGetterOrField(field, mGetter.isStatic()); + if (getter == null) { + L.e("Could not find dependent property '%s' referenced in " + + "@Bindable annotation on %s.%s", field, + mGetter.method.getDeclaringClass().toJavaCode(), + mGetter.method.getName()); + } else if (!getter.canBeInvalidated()) { + L.e("The dependent property '%s' referenced in @Bindable annotation on " + + "%s.%s must be annotated with @Bindable", field, + mGetter.method.getDeclaringClass().toJavaCode(), + mGetter.method.getName()); + } + }); + } finally { + Scope.exit(); + } + } + @Override protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { if (mIsListener) { 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 dfeec181..dea19925 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java +++ b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java @@ -186,7 +186,7 @@ public class MethodCallExpr extends Expr { flags |= STATIC; } mGetter = new Callable(Type.METHOD, mMethod.getName(), null, mMethod.getReturnType(args), - mMethod.getParameterTypes().length, flags, mMethod); + mMethod.getParameterTypes().length, flags, mMethod, null); } return mGetter.resolvedType; } diff --git a/compiler/src/main/java/android/databinding/tool/reflection/Callable.java b/compiler/src/main/java/android/databinding/tool/reflection/Callable.java index 5088523d..54db94e3 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/Callable.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/Callable.java @@ -15,6 +15,8 @@ */ package android.databinding.tool.reflection; +import android.databinding.Bindable; + import org.jetbrains.annotations.Nullable; public class Callable { @@ -42,8 +44,10 @@ public class Callable { private final int mParameterCount; + public final Bindable bindableAnnotation; + public Callable(Type type, String name, String setterName, ModelClass resolvedType, - int parameterCount, int flags, ModelMethod method) { + int parameterCount, int flags, ModelMethod method, Bindable bindable) { this.type = type; this.name = name; this.resolvedType = resolvedType; @@ -51,6 +55,7 @@ public class Callable { this.setterName = setterName; mFlags = flags; this.method = method; + this.bindableAnnotation = bindable; } public String getTypeCodeName() { diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java index 85719f1c..82db70b1 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java @@ -16,6 +16,8 @@ package android.databinding.tool.reflection; +import android.databinding.Bindable; + import java.util.Map; /** diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java index f47442b7..a91ae109 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java @@ -16,6 +16,8 @@ package android.databinding.tool.reflection; +import android.databinding.Bindable; + import java.util.List; import java.util.Map; 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 813b72e1..d408405e 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java @@ -15,6 +15,7 @@ */ package android.databinding.tool.reflection; +import android.databinding.Bindable; import android.databinding.tool.reflection.Callable.Type; import android.databinding.tool.util.L; import android.databinding.tool.util.StringUtils; @@ -417,7 +418,7 @@ public abstract class ModelClass { public Callable findGetterOrField(String name, boolean staticOnly) { if ("length".equals(name) && isArray()) { return new Callable(Type.FIELD, name, null, - ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0, null); + ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0, null, null); } String capitalized = StringUtils.capitalize(name); String[] methodNames = { @@ -435,8 +436,10 @@ public abstract class ModelClass { if (method.isStatic()) { flags |= STATIC; } + final Bindable bindable; if (method.isBindable()) { flags |= CAN_BE_INVALIDATED; + bindable = method.getBindableAnnotation(); } else { // if method is not bindable, look for a backing field final ModelField backingField = getField(name, true, method.isStatic()); @@ -444,13 +447,16 @@ public abstract class ModelClass { backingField == null ? "NOT FOUND" : backingField.getName()); if (backingField != null && backingField.isBindable()) { flags |= CAN_BE_INVALIDATED; + bindable = backingField.getBindableAnnotation(); + } else { + bindable = null; } } final ModelMethod setterMethod = findSetter(method, name); final String setterName = setterMethod == null ? null : setterMethod.getName(); final Callable result = new Callable(Callable.Type.METHOD, methodName, setterName, method.getReturnType(null), method.getParameterTypes().length, - flags, method); + flags, method, bindable); return result; } } @@ -484,7 +490,8 @@ public abstract class ModelClass { if (publicField.isBindable()) { flags |= CAN_BE_INVALIDATED; } - return new Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags, null); + return new Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags, null, + publicField.getBindableAnnotation()); } public ModelMethod findInstanceGetter(String name) { diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java index 0cde85b0..2baef1e8 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java @@ -15,6 +15,8 @@ */ package android.databinding.tool.reflection; +import android.databinding.Bindable; + public abstract class ModelField { /** @@ -46,4 +48,11 @@ public abstract class ModelField { * @return The declared type of the field variable. */ public abstract ModelClass getFieldType(); + + /** + * @return the Bindable annotation on the field or null if there isn't one. + */ + public Bindable getBindableAnnotation() { + return null; + } } 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 01500706..5605245a 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java @@ -44,6 +44,13 @@ public abstract class ModelMethod { public abstract boolean isBindable(); /** + * @return the Bindable annotation on the method or null if it doesn't exist. + */ + public Bindable getBindableAnnotation() { + return null; + } + + /** * Since when this method is available. Important for Binding expressions so that we don't * call non-existing APIs when setting UI. * diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java index 9373c6c9..be860570 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java @@ -70,6 +70,11 @@ class AnnotationField extends ModelField { } @Override + public Bindable getBindableAnnotation() { + return mField.getAnnotation(Bindable.class); + } + + @Override public boolean equals(Object obj) { if (obj instanceof AnnotationField) { AnnotationField that = (AnnotationField) obj; 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 66c1dbc5..839aa282 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 @@ -147,6 +147,11 @@ class AnnotationMethod extends ModelMethod { } @Override + public Bindable getBindableAnnotation() { + return mExecutableElement.getAnnotation(Bindable.class); + } + + @Override public int getMinApi() { if (mApiLevel == -1) { mApiLevel = SdkUtil.getMinApi(this); 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 3820084d..2da9367d 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt @@ -749,14 +749,15 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) { block("switch (fieldId)", { val accessedFields: List<FieldAccessExpr> = it.parents.filterIsInstance(FieldAccessExpr::class.java) accessedFields.filter { it.isUsed && it.hasBindableAnnotations() } - .groupBy { it.brName } + .flatMap { expr -> expr.dirtyingProperties.map { Pair(it, expr)} } + .groupBy { it.first } .forEach { // If two expressions look different but resolve to the same method, // we are not yet able to merge them. This is why we merge their // flags below. block("case ${it.key}:") { block("synchronized(this)") { - val flagSet = it.value.foldRight(FlagSet()) { l, r -> l.invalidateFlagSet.or(r) } + val flagSet = it.value.foldRight(FlagSet()) { l, r -> l.second.invalidateFlagSet.or(r) } mDirtyFlags.mapOr(flagSet) { suffix, index -> tab("${mDirtyFlags.localValue(index)} |= ${flagSet.localValue(index)};") diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java index 6821f168..41f68488 100644 --- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java +++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java @@ -56,4 +56,9 @@ public class JavaField extends ModelField { public ModelClass getFieldType() { return new JavaClass(mField.getType()); } + + @Override + public Bindable getBindableAnnotation() { + return mField.getAnnotation(Bindable.class); + } } 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 b7b626c1..46f7bf3a 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 @@ -88,6 +88,11 @@ public class JavaMethod extends ModelMethod { } @Override + public Bindable getBindableAnnotation() { + return mMethod.getAnnotation(Bindable.class); + } + + @Override public int getMinApi() { return SdkUtil.getMinApi(this); } |