Fix broken static import with alias.

Added tests to ensure they don't break again.
This commit is contained in:
George Mount
2015-02-19 16:43:01 -08:00
parent 0e7ca4055e
commit 980449bc4c
11 changed files with 85 additions and 82 deletions

View File

@@ -71,4 +71,24 @@ public class FindMethodTest
TextView textView = mBinder.getTextView8();
assertEquals("hello world", textView.getText().toString());
}
public void testImportStaticMethod() throws Throwable {
TextView textView = mBinder.getTextView9();
assertEquals("world", textView.getText().toString());
}
public void testImportStaticField() throws Throwable {
TextView textView = mBinder.getTextView10();
assertEquals("hello world", textView.getText().toString());
}
public void testAliasStaticMethod() throws Throwable {
TextView textView = mBinder.getTextView11();
assertEquals("world", textView.getText().toString());
}
public void testAliasStaticField() throws Throwable {
TextView textView = mBinder.getTextView12();
assertEquals("hello world", textView.getText().toString());
}
}

View File

@@ -4,9 +4,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<variable name="obj" type="com.android.databinding.testapp.vo.FindMethodBindingObject"/>
<!--
<import name="FMBO" type=""/>
-->
<import type="com.android.databinding.testapp.vo.FindMethodBindingObject"/>
<import type="com.android.databinding.testapp.vo.FindMethodBindingObject" alias="FMBO"/>
<TextView
android:id="@+id/textView0"
android:layout_width="wrap_content" android:layout_height="wrap_content"
@@ -43,4 +42,29 @@
android:id="@+id/textView8"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{com.android.databinding.testapp.vo.FindMethodBindingObject.foo.bar}"/>
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FindMethodBindingObject.staticMethod()}"/>
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FindMethodBindingObject.foo.bar}"/>
<TextView
android:id="@+id/textView11"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FMBO.staticMethod()}"/>
<TextView
android:id="@+id/textView12"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FMBO.foo.bar}"/>
<!-- The following are just to test duplicate expressions -->
<TextView
android:id="@+id/textView13"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FMBO.staticMethod()}"/>
<TextView
android:id="@+id/textView14"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{FMBO.foo.bar}"/>
</LinearLayout>

View File

@@ -575,17 +575,18 @@ abstract public class Expr {
}
}
protected void replaceStaticAccess(ModelAnalyzer modelAnalyzer) {
protected void replaceStaticIdentifiers(ModelAnalyzer modelAnalyzer) {
for (int i = 0; i < mChildren.size(); i++) {
Expr child = mChildren.get(i);
String packageName = child.asPackage();
if (packageName != null) {
ModelClass modelClass = modelAnalyzer.findClass(packageName);
if (modelClass != null) {
Expr staticAccessExpr = getModel().staticAccessExpr(modelClass);
child.removeParentAndUnregisterIfOrphan(this);
StaticIdentifierExpr staticAccessExpr = getModel().staticIdentifier(packageName);
staticAccessExpr.setUserDefinedType(packageName);
staticAccessExpr.getParents().add(this);
mChildren.set(i, staticAccessExpr);
child.removeParentAndUnregisterIfOrphan(this);
}
}
}
@@ -595,11 +596,11 @@ abstract public class Expr {
while (mParents.remove(parent)) {
}
if (getParents().isEmpty()) {
getModel().unregister(this);
for (Expr expr : mChildren) {
expr.removeParentAndUnregisterIfOrphan(this);
}
mChildren.clear();
getModel().unregister(this);
}
}

View File

@@ -146,10 +146,6 @@ public class ExprModel {
return register(new BracketExpr(variableExpr, argExpr));
}
public Expr staticAccessExpr(ModelClass modelClass) {
return register(new StaticAccessExpr(modelClass));
}
public Expr castExpr(String type, Expr expr) {
return register(new CastExpr(type, expr));
}
@@ -166,7 +162,9 @@ public class ExprModel {
public Expr bindingExpr(Expr bindingExpr) {
Preconditions.checkArgument(mExprMap.containsKey(bindingExpr.getUniqueKey()),
"Main expression should already be registered");
mBindingExpressions.add(bindingExpr);
if (!mBindingExpressions.contains(bindingExpr)) {
mBindingExpressions.add(bindingExpr);
}
return bindingExpr;
}
@@ -257,6 +255,9 @@ public class ExprModel {
}
// non-dynamic binding expressions receive some ids so that they can be invalidated
for (int i = 0; i < mBindingExpressions.size(); i++) {
L.d("[" + i + "] " + mBindingExpressions.get(i));
}
for (Expr expr : mBindingExpressions) {
if (!(expr.isDynamic() || !expr.hasId())) {
L.d("Expr " + expr + " is dynamic? " + expr.isDynamic() + ", has ID? " + expr.hasId());
@@ -348,7 +349,7 @@ public class ExprModel {
public boolean apply(Expr input) {
return input instanceof IdentifierExpr
&& !input.hasId()
&& !modelAnalyzer.isObservable(input.getResolvedType())
&& !input.isObservable()
&& input.isDynamic();
}
});
@@ -358,7 +359,7 @@ public class ExprModel {
return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
@Override
public boolean apply(Expr input) {
return modelAnalyzer.isObservable(input.getResolvedType());
return input.isObservable();
}
});
}

View File

@@ -39,7 +39,7 @@ public class FieldAccessExpr extends Expr {
mIsObservableField = isObservableField;
}
public Expr getParent() {
public Expr getChild() {
return getChildren().get(0);
}
@@ -52,7 +52,7 @@ public class FieldAccessExpr extends Expr {
@Override
public boolean isDynamic() {
if (!getParent().isDynamic()) {
if (!getChild().isDynamic()) {
return false;
}
if (mGetter == null) {
@@ -66,7 +66,7 @@ public class FieldAccessExpr extends Expr {
protected List<Dependency> constructDependencies() {
final List<Dependency> dependencies = constructDynamicChildrenDependencies();
for (Dependency dependency : dependencies) {
if (dependency.getOther() == getParent()) {
if (dependency.getOther() == getChild()) {
dependency.setMandatory(true);
}
}
@@ -94,16 +94,17 @@ public class FieldAccessExpr extends Expr {
@Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
replaceStaticAccess(modelAnalyzer);
Expr parent = getParent();
boolean isStatic = parent instanceof StaticAccessExpr;
mGetter = modelAnalyzer.findMethodOrField(parent.getResolvedType(), mName, isStatic);
replaceStaticIdentifiers(modelAnalyzer);
Expr child = getChild();
child.resolveType(modelAnalyzer);
boolean isStatic = child instanceof StaticIdentifierExpr;
mGetter = modelAnalyzer.findMethodOrField(child.getResolvedType(), mName, isStatic);
if (modelAnalyzer.isObservableField(mGetter.resolvedType)) {
// Make this the ".get()" and add an extra field access for the observable field
parent.getParents().remove(this);
getChildren().remove(parent);
child.getParents().remove(this);
getChildren().remove(child);
FieldAccessExpr observableField = getModel().observableField(parent, mName);
FieldAccessExpr observableField = getModel().observableField(child, mName);
observableField.mGetter = mGetter;
getChildren().add(observableField);
@@ -117,7 +118,7 @@ public class FieldAccessExpr extends Expr {
@Override
protected String asPackage() {
String parentPackage = getParent().asPackage();
String parentPackage = getChild().asPackage();
return parentPackage == null ? null : parentPackage + "." + mName;
}
}

View File

@@ -45,14 +45,14 @@ public class MethodCallExpr extends Expr {
@Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
replaceStaticAccess(modelAnalyzer);
replaceStaticIdentifiers(modelAnalyzer);
List<ModelClass> args = new ArrayList<>();
for (Expr expr : getArgs()) {
args.add(expr.getResolvedType());
}
Expr target = getTarget();
boolean isStatic = target instanceof StaticAccessExpr;
boolean isStatic = target instanceof StaticIdentifierExpr;
mGetter = modelAnalyzer.findMethod(target.getResolvedType(), mName, args, isStatic);
}
return mGetter.resolvedType;

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.databinding.expr;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import java.util.ArrayList;
import java.util.List;
public class StaticAccessExpr extends Expr {
private ModelClass mStaticClass;
public StaticAccessExpr(ModelClass modelClass) {
mStaticClass = modelClass;
}
@Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
return mStaticClass;
}
@Override
protected List<Dependency> constructDependencies() {
return new ArrayList<>();
}
protected String computeUniqueKey() {
return mStaticClass.toJavaCode();
}
}

View File

@@ -22,6 +22,11 @@ public class StaticIdentifierExpr extends IdentifierExpr {
super(name);
}
@Override
public boolean isObservable() {
return false;
}
@Override
public boolean isDynamic() {
return false;

View File

@@ -46,7 +46,6 @@ import com.android.databinding.expr.ResourceExpr
import com.android.databinding.expr.BracketExpr
import com.android.databinding.reflection.Callable
import com.android.databinding.expr.CastExpr
import com.android.databinding.expr.StaticAccessExpr
fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar()
@@ -161,7 +160,7 @@ fun Expr.toCode(full : Boolean = false) : KCode {
app("", it.getRight().toCode())
}
is FieldAccessExpr -> kcode("") {
app("", it.getParent().toCode())
app("", it.getChild().toCode())
if (it.getGetter().type == Callable.Type.FIELD) {
app(".", it.getGetter().name)
} else {
@@ -222,9 +221,6 @@ fun Expr.toCode(full : Boolean = false) : KCode {
app("(", it.getCastType())
app(") ", it.getCastExpr().toCode())
}
is StaticAccessExpr -> kcode("") {
app("", it.getResolvedType().toJavaCode())
}
else -> kcode("//NOT IMPLEMENTED YET")
}

View File

@@ -139,8 +139,8 @@ public class ExpressionVisitorTest {
@Test
public void testInheritedFieldResolution() {
final FieldAccessExpr parsed = parse("myStr.length", FieldAccessExpr.class);
assertTrue(parsed.getParent() instanceof IdentifierExpr);
final IdentifierExpr id = (IdentifierExpr) parsed.getParent();
assertTrue(parsed.getChild() instanceof IdentifierExpr);
final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
id.setUserDefinedType("java.lang.String");
assertSame(int.class, parsed.getResolvedType());
Callable getter = parsed.getGetter();
@@ -155,8 +155,8 @@ public class ExpressionVisitorTest {
@Test
public void testGetterResolution() {
final FieldAccessExpr parsed = parse("myStr.bytes", FieldAccessExpr.class);
assertTrue(parsed.getParent() instanceof IdentifierExpr);
final IdentifierExpr id = (IdentifierExpr) parsed.getParent();
assertTrue(parsed.getChild() instanceof IdentifierExpr);
final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
id.setUserDefinedType("java.lang.String");
assertSame(byte[].class, parsed.getResolvedType());
Callable getter = parsed.getGetter();

View File

@@ -93,7 +93,7 @@ public class LayoutBinderTest {
fa.getResolvedType();
final Callable getter = fa.getGetter();
assertTrue(getter.type == Callable.Type.METHOD);
assertSame(id, fa.getParent());
assertSame(id, fa.getChild());
assertTrue(fa.isDynamic());
}