Add resource support in binding expression.

Change-Id: Iccb8c3a5856c247d8245fe97a3c37cd60bb7e758
diff --git a/compiler/src/main/java/com/android/databinding/ExpressionVisitor.java b/compiler/src/main/java/com/android/databinding/ExpressionVisitor.java
index ce603e5..dbc6f55 100644
--- a/compiler/src/main/java/com/android/databinding/ExpressionVisitor.java
+++ b/compiler/src/main/java/com/android/databinding/ExpressionVisitor.java
@@ -143,6 +143,11 @@
         return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
     }
 
+    @Override
+    public Expr visitResource(@NotNull BindingExpressionParser.ResourceContext ctx) {
+        return mModel.resourceExpr(ctx.getText());
+    }
+
     //    @Override
 //    public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) {
 //        final String identifier = ctx.Identifier().getText();
diff --git a/compiler/src/main/java/com/android/databinding/expr/Expr.java b/compiler/src/main/java/com/android/databinding/expr/Expr.java
index 88eef39..d68124b 100644
--- a/compiler/src/main/java/com/android/databinding/expr/Expr.java
+++ b/compiler/src/main/java/com/android/databinding/expr/Expr.java
@@ -124,7 +124,7 @@
 
     private BitSet resolveInvalidFlags() {
         BitSet bitSet = new BitSet();
-        if (mCanBeInvalidated) {
+        if (mCanBeInvalidated || getDependants().isEmpty()) {
             bitSet.set(getId(), true);
         }
         for (Dependency dependency : getDependencies()) {
diff --git a/compiler/src/main/java/com/android/databinding/expr/ExprModel.java b/compiler/src/main/java/com/android/databinding/expr/ExprModel.java
index ea7bbc7..bfe04e5 100644
--- a/compiler/src/main/java/com/android/databinding/expr/ExprModel.java
+++ b/compiler/src/main/java/com/android/databinding/expr/ExprModel.java
@@ -142,6 +142,10 @@
         return register(new GroupExpr(grouped));
     }
 
+    public Expr resourceExpr(String resourceText) {
+        return register(new ResourceExpr(resourceText));
+    }
+
     public List<Expr> getBindingExpressions() {
         return mBindingExpressions;
     }
diff --git a/compiler/src/main/java/com/android/databinding/expr/ResourceExpr.java b/compiler/src/main/java/com/android/databinding/expr/ResourceExpr.java
new file mode 100644
index 0000000..80cbbf5
--- /dev/null
+++ b/compiler/src/main/java/com/android/databinding/expr/ResourceExpr.java
@@ -0,0 +1,189 @@
+/*
+ * 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.google.common.collect.ImmutableMap;
+
+import com.android.databinding.ClassAnalyzer;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+public class ResourceExpr extends Expr {
+
+    private final static Map<String, String> RESOURCE_TYPE_TO_R_OBJECT =
+            ImmutableMap.<String, String>builder()
+                    .put("colorStateList", "color  ")
+                    .put("dimenOffset", "dimen  ")
+                    .put("dimenSize", "dimen  ")
+                    .put("intArray", "array  ")
+                    .put("stateListAnimator", "animator  ")
+                    .put("stringArray", "array  ")
+                    .put("typedArray", "array")
+                    .build();
+
+    private final String mPackage;
+
+    private final String mResourceType;
+
+    private final String mResourceId;
+
+    public ResourceExpr(String resourceText) {
+        int colonIndex = resourceText.indexOf(':');
+        int slashIndex = resourceText.indexOf('/');
+        mResourceType = resourceText.substring(colonIndex + 1, slashIndex).trim();
+        mResourceId = resourceText.substring(slashIndex + 1).trim();
+        String packageName = "";
+        if (colonIndex > 1) {
+            if ("android".equals(resourceText.substring(1, colonIndex).trim())) {
+                packageName = "android.";
+            } else {
+                packageName = "";
+            }
+        }
+        mPackage = packageName;
+    }
+
+    @Override
+    protected Class resolveType(ClassAnalyzer classAnalyzer) {
+        String type;
+        switch (mResourceType) {
+            case "anim":
+                type = "android.view.animation.Animation";
+                break;
+            case "animator":
+                type = "android.animation.Animator";
+                break;
+            case "bool":
+                return boolean.class;
+            case "color":
+                return int.class;
+            case "colorStateList":
+                type = "android.content.res.ColorStateList";
+                break;
+            case "dimen":
+                return float.class;
+            case "dimenOffset":
+                return int.class;
+            case "dimenSize":
+                return int.class;
+            case "drawable":
+                type = "android.graphics.drawable.Drawable";
+                break;
+            case "fraction":
+                return float.class;
+            case "id":
+                return int.class;
+            case "intArray":
+                return int[].class;
+            case "integer":
+                return int.class;
+            case "interpolator":
+                type = "";
+                break;
+            case "layout":
+                return int.class;
+            case "plurals":
+                return int.class;
+            case "stateListAnimator":
+                type = "android.animation.StateListAnimator";
+                break;
+            case "string":
+                return String.class;
+            case "stringArray":
+                return String[].class;
+            case "transition":
+                type = "android.transition.Transition";
+                break;
+            case "typedArray":
+                type = "android.content.res.TypedArray";
+                break;
+            default:
+                type = mResourceType;
+                break;
+        }
+        return classAnalyzer.findClass(type);
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        if (mPackage == null) {
+            return "@" + mResourceType + "/" + mResourceId;
+        } else {
+            return "@" + "android:" + mResourceType + "/" + mResourceId;
+        }
+    }
+
+    @Override
+    public boolean isDynamic() {
+        return false;
+    }
+
+    @Override
+    public boolean canBeInvalidated() {
+        return false;
+    }
+
+    public String toJava() {
+        final String context = "mRoot.getContext()";
+        final String resources = context + ".getResources()";
+        final String resourceName = mPackage + "R." + getResourceObject() + "." + mResourceId;
+        switch (mResourceType) {
+            case "anim": return "android.view.animation.AnimationUtils.loadAnimation(" + context + ", " + resourceName + ")";
+            case "animator": return "android.animation.AnimatorInflater.loadAnimator(" + context + ", " + resourceName + ")";
+            case "bool": return resources + ".getBoolean(" + resourceName + ")";
+            case "color": return resources + ".getColor(" + resourceName + ")";
+            case "colorStateList": return resources + ".getColorStateList(" + resourceName + ")";
+            case "dimen": return resources + ".getDimension(" + resourceName + ")";
+            case "dimenOffset": return resources + ".getDimensionPixelOffset(" + resourceName + ")";
+            case "dimenSize": return resources + ".getDimensionPixelSize(" + resourceName + ")";
+            case "drawable": return resources + ".getDrawable(" + resourceName + ")";
+            case "fraction": return resources + ".getFraction(" + resourceName + ", 1, 1)";
+            case "id": return resourceName;
+            case "intArray": return resources + ".getIntArray(" + resourceName + ")";
+            case "integer": return resources + ".getInteger(" + resourceName + ")";
+            case "interpolator":  return "android.view.animation.AnimationUtils.loadInterpolator(" + context + ", " + resourceName + ")";
+            case "layout": return resourceName;
+            case "plurals": return resourceName;
+            case "stateListAnimator": return "android.animation.AnimatorInflater.loadStateListAnimator(" + context + ", " + resourceName + ")";
+            case "string": return resources + ".getString(" + resourceName + ")";
+            case "stringArray": return resources + ".getStringArray(" + resourceName + ")";
+            case "transition": return "android.transition.TransitionInflater.from(" + context + ").inflateTransition(" + resourceName + ")";
+            case "typedArray": return resources + ".obtainTypedArray(" + resourceName + ")";
+        }
+        final String property = Character.toUpperCase(mResourceType.charAt(0)) +
+                mResourceType.substring(1);
+        return resources + ".get" + property + "(" + resourceName + ")";
+
+    }
+
+    private String getResourceObject() {
+        String rFileObject = RESOURCE_TYPE_TO_R_OBJECT.get(mResourceType);
+        if (rFileObject == null) {
+            rFileObject = mResourceType;
+        }
+        return rFileObject;
+    }
+}
diff --git a/compiler/src/main/java/com/android/databinding/store/SetterStore.java b/compiler/src/main/java/com/android/databinding/store/SetterStore.java
index 0cab80c..b41e7e1 100644
--- a/compiler/src/main/java/com/android/databinding/store/SetterStore.java
+++ b/compiler/src/main/java/com/android/databinding/store/SetterStore.java
@@ -16,7 +16,6 @@
 package com.android.databinding.store;
 
 import com.android.databinding.ClassAnalyzer;
-import com.android.databinding.util.L;
 
 import java.io.File;
 import java.io.IOException;
@@ -318,8 +317,6 @@
 
     public String getSetterCall(String attribute, Class<?> viewType, Class<?> valueType,
             String viewExpression, String valueExpression) {
-        L.d("getting setter call for %s %s %s %s %s", attribute, viewType, valueType,
-                viewExpression, valueExpression);
         if (!attribute.startsWith("android:")) {
             int colon = attribute.indexOf(':');
             if (colon >= 0) {
@@ -327,11 +324,9 @@
             }
         }
         ArrayList<AdaptedMethod> adapters = mAdaptedMethods.get(attribute);
-        L.d("returned adapter count %d", adapters == null ? -1 : adapters.size());
         AdaptedMethod adapter = null;
         String setterName = null;
         Method bestSetterMethod = getBestSetter(viewType, valueType, attribute);
-        L.d("setter method: %s", bestSetterMethod == null ? "null" : bestSetterMethod.getName());
         Class<?> bestViewType = null;
         Class<?> bestValueType = null;
         if (bestSetterMethod != null) {
@@ -342,7 +337,6 @@
 
         if (adapters != null) {
             for (AdaptedMethod adaptedMethod : adapters) {
-                L.d("checking adapter method %s", adaptedMethod);
                 if (adaptedMethod.viewType.isAssignableFrom(viewType)) {
                     boolean isBetterView = bestViewType == null ||
                             bestValueType.isAssignableFrom(adaptedMethod.valueType);
@@ -351,12 +345,7 @@
                         bestViewType = adaptedMethod.viewType;
                         bestValueType = adaptedMethod.valueType;
                         adapter = adaptedMethod;
-                        L.d("chosen %s", adaptedMethod);
-                    } else {
-                        L.d("not better");
                     }
-                } else {
-                    L.d("not assignable");
                 }
             }
         }
@@ -469,16 +458,12 @@
     }
 
     private ConversionMethod getConversionMethod(Class<?> from, Class<?> to) {
-        System.out.println("Getting conversion from " + from + " to " + to);
         if (from != null && to != null) {
             for (ConversionMethod conversion : mConversionMethods) {
-                System.out.println("Testing " + conversion.fromType + " to " + conversion.toType);
                 if (canUseForConversion(from, conversion.fromType) &&
                         canUseForConversion(conversion.toType, to)) {
-                    System.out.println("Yes!");
                     return conversion;
                 }
-                System.out.println("Nope!");
             }
         }
         return null;
diff --git a/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt b/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
index e72c2ae..4dfc85f 100644
--- a/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
@@ -43,6 +43,7 @@
 import com.android.databinding.ext.br
 import com.android.databinding.ext.toJavaCode
 import com.android.databinding.ext.isObservable
+import com.android.databinding.expr.ResourceExpr
 
 fun Expr.isObservable() = com.android.databinding.ClassAnalyzer.getInstance().isObservable(this.getResolvedType())
 
@@ -190,6 +191,9 @@
             app("?", it.getIfTrue().toCode())
             app(":", it.getIfFalse().toCode())
         }
+        is ResourceExpr -> kcode("") {
+            app("", it.toJava())
+        }
         else -> kcode("//NOT IMPLEMENTED YET")
     }
 
diff --git a/grammerBuilder/BindingExpression.g4 b/grammerBuilder/BindingExpression.g4
index e647895..85d7631 100644
--- a/grammerBuilder/BindingExpression.g4
+++ b/grammerBuilder/BindingExpression.g4
@@ -445,25 +445,25 @@
 ResourceType
     :   'anim'
     |   'animator'
-    |   'array'
-    |   'attr'
     |   'bool'
     |   'color'
+    |   'colorStateList'
     |   'dimen'
+    |   'dimenOffset'
+    |   'dimenSize'
     |   'drawable'
     |   'fraction'
     |   'id'
     |   'integer'
+    |   'intArray'
     |   'interpolator'
     |   'layout'
-    |   'menu'
-    |   'mipmap'
     |   'plurals'
-    |   'raw'
+    |   'stateListAnimator'
     |   'string'
-    |   'style'
+    |   'stringArray'
     |   'transition'
-    |   'xml'
+    |   'typedArray'
     ;
 
 ResourceName
diff --git a/library/src/main/java/android/binding/adapters/ViewBindingAdapter.java b/library/src/main/java/android/binding/adapters/ViewBindingAdapter.java
index 01ec43c..7333abe 100644
--- a/library/src/main/java/android/binding/adapters/ViewBindingAdapter.java
+++ b/library/src/main/java/android/binding/adapters/ViewBindingAdapter.java
@@ -39,11 +39,6 @@
 })
 public class ViewBindingAdapter {
 
-    @BindingAdapter("android:background")
-    public static void setBackground(View view, int color) {
-        view.setBackgroundColor(color);
-    }
-
     @BindingAdapter("android:padding")
     public static void setPadding(View view, int padding) {
         view.setPadding(padding, padding, padding, padding);