OSDN Git Service

Support casting operation in binding expressions.
authorGeorge Mount <mount@google.com>
Wed, 18 Feb 2015 18:19:43 +0000 (10:19 -0800)
committerGeorge Mount <mount@google.com>
Wed, 18 Feb 2015 18:44:22 +0000 (10:44 -0800)
Bug 19272385

Change-Id: I5a992f4eaf6f456d21983481ab4ca01305582f36

tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/CastTest.java [new file with mode: 0644]
tools/data-binding/TestApp/src/main/res/layout/cast_test.xml [new file with mode: 0644]
tools/data-binding/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java
tools/data-binding/compiler/src/main/java/com/android/databinding/ExpressionVisitor.java
tools/data-binding/compiler/src/main/java/com/android/databinding/expr/CastExpr.java [new file with mode: 0644]
tools/data-binding/compiler/src/main/java/com/android/databinding/expr/ExprModel.java
tools/data-binding/compiler/src/main/java/com/android/databinding/reflection/AnnotationAnalyzer.java
tools/data-binding/compiler/src/main/java/com/android/databinding/reflection/AnnotationClass.java
tools/data-binding/compiler/src/main/java/com/android/databinding/store/SetterStore.java
tools/data-binding/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt

diff --git a/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/CastTest.java b/tools/data-binding/TestApp/src/androidTest/java/com/android/databinding/testapp/CastTest.java
new file mode 100644 (file)
index 0000000..0ea053f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.testapp;
+
+import com.android.databinding.testapp.generated.CastTestBinder;
+
+import android.support.v4.util.ArrayMap;
+import android.test.UiThreadTest;
+
+import java.util.ArrayList;
+
+public class CastTest extends BaseDataBinderTest<CastTestBinder> {
+    ArrayList<String> mValues = new ArrayList<>();
+    ArrayMap<String, String> mMap = new ArrayMap<>();
+
+    public CastTest() {
+        super(CastTestBinder.class, R.layout.cast_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mValues.clear();
+                    mValues.add("hello");
+                    mValues.add("world");
+                    mValues.add("not seen");
+                    mMap.clear();
+                    mMap.put("hello", "world");
+                    mMap.put("world", "hello");
+                    mBinder.setList(mValues);
+                    mBinder.setMap(mMap);
+                    mBinder.rebindDirty();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testCast() throws Throwable {
+        assertEquals("hello", mBinder.getTextView0().getText().toString());
+        assertEquals("world", mBinder.getTextView1().getText().toString());
+    }
+}
diff --git a/tools/data-binding/TestApp/src/main/res/layout/cast_test.xml b/tools/data-binding/TestApp/src/main/res/layout/cast_test.xml
new file mode 100644 (file)
index 0000000..4d4cef3
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="list" type="java.util.Collection&lt;java.lang.String&gt;"/>
+    <variable name="map" type="java.lang.Object"/>
+
+    <TextView
+            android:id="@+id/textView0"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{((java.util.ArrayList&lt;java.lang.String&gt;)list)[0]}"/>
+    <TextView
+            android:id="@+id/textView1"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{((java.util.Map&lt;java.lang.String,java.lang.String&gt;)map)[`hello`]}"/>
+</LinearLayout>
\ No newline at end of file
index 4113292..f964d63 100644 (file)
@@ -2,6 +2,8 @@ package com.android.databinding.annotationprocessor;
 
 import com.android.databinding.reflection.ModelAnalyzer;
 
+import org.apache.commons.io.IOUtils;
+
 import android.binding.Bindable;
 
 import java.io.File;
@@ -216,11 +218,13 @@ public class ProcessBindable extends AbstractProcessor {
                 properties = (Intermediate) in.readObject();
             }
         } catch (IOException e) {
-            System.err.println("Could not read Binding properties intermediate file: " +
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                    "Could not read Binding properties intermediate file: " +
                     e.getLocalizedMessage());
         } catch (ClassNotFoundException e) {
-            System.err.println("Could not read Binding properties intermediate file: " +
-                    e.getLocalizedMessage());
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                    "Could not read Binding properties intermediate file: " +
+                            e.getLocalizedMessage());
         } finally {
             try {
                 if (in != null) {
@@ -244,7 +248,8 @@ public class ProcessBindable extends AbstractProcessor {
                     .getResources(resourcePath);
             while (resources.hasMoreElements()) {
                 URL url = resources.nextElement();
-                System.out.println("Merging binding adapters from " + url);
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
+                        "Merging binding adapters from " + url);
                 InputStream inputStream = null;
                 try {
                     inputStream = url.openStream();
@@ -254,23 +259,21 @@ public class ProcessBindable extends AbstractProcessor {
                         properties.captureProperties(intermediateProperties);
                     }
                 } catch (IOException e) {
-                    System.err.println("Could not merge in Bindables from " + url + ": " +
-                            e.getLocalizedMessage());
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                            "Could not merge in Bindables from " + url + ": " +
+                                    e.getLocalizedMessage());
                 } catch (ClassNotFoundException e) {
-                    System.err.println("Could not read Binding properties intermediate file: " +
-                            e.getLocalizedMessage());
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                            "Could not read Binding properties intermediate file: " +
+                                    e.getLocalizedMessage());
                 } finally {
-                    try {
-                        inputStream.close();
-                    } catch (IOException e2) {
-                        System.err.println("Error closing intermediate Bindables store: " +
-                                e2.getLocalizedMessage());
-                    }
+                    IOUtils.closeQuietly(inputStream);
                 }
             }
         } catch (IOException e) {
-            System.err.println("Could not read Binding properties intermediate file: " +
-                    e.getLocalizedMessage());
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                    "Could not read Binding properties intermediate file: " +
+                            e.getLocalizedMessage());
         }
     }
 
index cc0516f..5afae55 100644 (file)
@@ -159,6 +159,11 @@ public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
         return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1)));
     }
 
+    @Override
+    public Expr visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) {
+        return mModel.castExpr(ctx.type().getText(), visit(ctx.expression()));
+    }
+
     //    @Override
 //    public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) {
 //        final String identifier = ctx.Identifier().getText();
diff --git a/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/CastExpr.java b/tools/data-binding/compiler/src/main/java/com/android/databinding/expr/CastExpr.java
new file mode 100644 (file)
index 0000000..f516f56
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.List;
+
+public class CastExpr extends Expr {
+
+    final String mType;
+
+    CastExpr(String type, Expr expr) {
+        super(expr);
+        mType = type;
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        return modelAnalyzer.findClass(mType);
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
+        for (Dependency dependency : dependencies) {
+            dependency.setMandatory(true);
+        }
+        return dependencies;
+    }
+
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(mType, getCastExpr().computeUniqueKey());
+    }
+
+    public Expr getCastExpr() {
+        return getChildren().get(0);
+    }
+
+    public String getCastType() {
+        return mType;
+    }
+}
index 17d0470..f7ad56d 100644 (file)
@@ -141,6 +141,10 @@ public class ExprModel {
         return register(new BracketExpr(variableExpr, argExpr));
     }
 
+    public Expr castExpr(String type, Expr expr) {
+        return register(new CastExpr(type, expr));
+    }
+
     public List<Expr> getBindingExpressions() {
         return mBindingExpressions;
     }
index 02c4fc2..5616661 100644 (file)
@@ -294,7 +294,7 @@ public class AnnotationAnalyzer extends ModelAnalyzer {
             declaredType = (DeclaredType) typeElement.asType();
         } else {
             int templateCloseIndex = className.lastIndexOf('>');
-            String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex - 1);
+            String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex);
 
             Elements elementUtils = getElementUtils();
             String baseClassName = className.substring(0, templateOpenIndex);
@@ -338,6 +338,7 @@ public class AnnotationAnalyzer extends ModelAnalyzer {
                     openCount--;
                 }
             }
+            index++;
         }
         list.add(arg.toString());
         return list;
index e7db2d5..facb4fb 100644 (file)
@@ -80,13 +80,16 @@ public class AnnotationClass implements ModelClass {
         TypeMirror foundInterface = null;
         if (typeUtil.isSameType(interfaceType, typeUtil.erasure(mTypeMirror))) {
             foundInterface = mTypeMirror;
-        } else if (mTypeMirror.getKind() == TypeKind.DECLARED) {
-            DeclaredType declaredType = (DeclaredType) mTypeMirror;
-            TypeElement typeElement = (TypeElement) declaredType.asElement();
-            for (TypeMirror type : typeElement.getInterfaces()) {
-                if (typeUtil.isSameType(interfaceType, typeUtil.erasure(type))) {
-                    foundInterface = type;
+        } else {
+            ArrayList<TypeMirror> toCheck = new ArrayList<>();
+            toCheck.add(mTypeMirror);
+            while (!toCheck.isEmpty()) {
+                TypeMirror typeMirror = toCheck.remove(0);
+                if (typeUtil.isSameType(interfaceType, typeUtil.erasure(typeMirror))) {
+                    foundInterface = typeMirror;
                     break;
+                } else {
+                    toCheck.addAll(typeUtil.directSupertypes(typeMirror));
                 }
             }
             if (foundInterface == null) {
@@ -102,6 +105,7 @@ public class AnnotationClass implements ModelClass {
                             ", but it isn't a declared type: " + foundInterface);
             return null;
         }
+        System.err.println("found interface: " + foundInterface);
         return (DeclaredType) foundInterface;
     }
 
@@ -226,6 +230,9 @@ public class AnnotationClass implements ModelClass {
 
     @Override
     public boolean isAssignableFrom(ModelClass that) {
+        if (that == null) {
+            return false;
+        }
         TypeMirror thatType = ((AnnotationClass)that).mTypeMirror;
         return getTypeUtils().isAssignable(thatType, mTypeMirror);
     }
index d95d512..d98d6ad 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.android.databinding.store;
 
+import com.android.databinding.reflection.AnnotationAnalyzer;
 import com.android.databinding.reflection.ModelAnalyzer;
 import com.android.databinding.reflection.ModelClass;
 import com.android.databinding.reflection.ModelMethod;
@@ -109,11 +110,11 @@ public class SetterStore {
             }
             return new SetterStore(modelAnalyzer, store);
         } catch (IOException e) {
-            System.err.println("Could not read SetterStore intermediate file: " +
+            printMessage(Diagnostic.Kind.ERROR, "Could not read SetterStore intermediate file: " +
                     e.getLocalizedMessage());
             e.printStackTrace();
         } catch (ClassNotFoundException e) {
-            System.err.println("Could not read SetterStore intermediate file: " +
+            printMessage(Diagnostic.Kind.ERROR, "Could not read SetterStore intermediate file: " +
                     e.getLocalizedMessage());
             e.printStackTrace();
         }
@@ -240,8 +241,8 @@ public class SetterStore {
         Filer filer = processingEnvironment.getFiler();
         FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT,
                 SetterStore.class.getPackage().getName(), "setter_store.bin");
-        processingEnvironment.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                "============= Writing intermediate file: " + resource.getName());
+        printMessage(Diagnostic.Kind.NOTE, "============= Writing intermediate file: " +
+                resource.getName());
         ObjectOutputStream out = null;
         try {
             out = new ObjectOutputStream(resource.openOutputStream());
@@ -291,11 +292,11 @@ public class SetterStore {
                             }
 
                         } catch (Exception e) {
-                            System.out.println("Unknown class: " + key.valueType);
+                            printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + key.valueType);
                         }
                     }
                 } catch (Exception e) {
-                    System.out.println("Unknown class: " + key.viewType);
+                    printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + key.viewType);
                 }
             }
         }
@@ -330,7 +331,7 @@ public class SetterStore {
                         break;
                     }
                 } catch (Exception e) {
-                    //System.out.println("Unknown class: " + className);
+                    //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
                 }
             }
         }
@@ -433,12 +434,12 @@ public class SetterStore {
                                     return conversion.get(toClassName);
                                 }
                             } catch (Exception e) {
-                                System.out.println("Unknown class: " + toClassName);
+                                printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + toClassName);
                             }
                         }
                     }
                 } catch (Exception e) {
-                    System.out.println("Unknown class: " + fromClassName);
+                    printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + fromClassName);
                 }
             }
         }
@@ -518,6 +519,15 @@ public class SetterStore {
         }
     }
 
+    private static void printMessage(Diagnostic.Kind kind, String message) {
+        ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+        if (modelAnalyzer instanceof AnnotationAnalyzer) {
+            ((AnnotationAnalyzer) modelAnalyzer).printMessage(kind, message);
+        } else {
+            System.out.println(message);
+        }
+    }
+
     private static class MethodDescription implements Serializable {
 
         private static final long serialVersionUID = 1;
index 709ec71..f4300b6 100644 (file)
@@ -45,6 +45,7 @@ import com.android.databinding.ext.toJavaCode
 import com.android.databinding.expr.ResourceExpr
 import com.android.databinding.expr.BracketExpr
 import com.android.databinding.reflection.Callable
+import com.android.databinding.expr.CastExpr
 
 fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar()
 
@@ -216,6 +217,10 @@ fun Expr.toCode(full : Boolean = false) : KCode {
                 }
             }
         }
+        is CastExpr -> kcode("") {
+            app("(", it.getCastType())
+            app(") ", it.getCastExpr().toCode())
+        }
         else -> kcode("//NOT IMPLEMENTED YET")
     }