Merge "TIF: Update the input list even when a single component in a package is updated."
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index a59927d..635b2fa 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -49,14 +49,25 @@
 
         // javax.crypto.KeyGenerator
         put("KeyGenerator.AES", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$AES");
+        put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA1");
+        put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA224");
         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA256");
+        put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA384");
+        put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA512");
 
         // java.security.SecretKeyFactory
-        put("SecretKeyFactory.AES", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
-        put("SecretKeyFactory.HmacSHA256", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+        putSecretKeyFactoryImpl("AES");
+        putSecretKeyFactoryImpl("HmacSHA1");
+        putSecretKeyFactoryImpl("HmacSHA224");
+        putSecretKeyFactoryImpl("HmacSHA256");
+        putSecretKeyFactoryImpl("HmacSHA384");
+        putSecretKeyFactoryImpl("HmacSHA512");
 
         // javax.crypto.Mac
+        putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224");
         putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256");
+        putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384");
+        putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512");
 
         // javax.crypto.Cipher
         putSymmetricCipherImpl("AES/ECB/NoPadding",
@@ -73,6 +84,10 @@
                 PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding");
     }
 
+    private void putSecretKeyFactoryImpl(String algorithm) {
+        put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi");
+    }
+
     private void putMacImpl(String algorithm, String implClass) {
         put("Mac." + algorithm, implClass);
         put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index ec358d6..487eac0 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -111,7 +111,9 @@
     private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode;
     private final @KeyStoreKeyConstraints.PaddingEnum int mPadding;
     private final int mBlockSizeBytes;
-    private final boolean mIvUsed;
+
+    /** Whether this transformation requires an IV. */
+    private final boolean mIvRequired;
 
     // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
     // doFinal finishes.
@@ -119,10 +121,13 @@
     private KeyStoreSecretKey mKey;
     private SecureRandom mRng;
     private boolean mFirstOperationInitiated;
-    byte[] mIv;
+    private byte[] mIv;
+    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+    private boolean mIvHasBeenUsed;
 
-    // Fields below must be reset
+    // Fields below must be reset after doFinal
     private byte[] mAdditionalEntropyForBegin;
+
     /**
      * Token referencing this operation inside keystore service. It is initialized by
      * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some
@@ -143,7 +148,7 @@
         mBlockMode = blockMode;
         mPadding = padding;
         mBlockSizeBytes = blockSizeBytes;
-        mIvUsed = ivUsed;
+        mIvRequired = ivUsed;
     }
 
     @Override
@@ -170,7 +175,7 @@
     }
 
     private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
-        reset();
+        resetAll();
         if (!(key instanceof KeyStoreSecretKey)) {
             throw new InvalidKeyException(
                     "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
@@ -187,7 +192,25 @@
         mEncrypting = opmode == Cipher.ENCRYPT_MODE;
     }
 
-    private void reset() {
+    private void resetAll() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mEncrypting = false;
+        mKey = null;
+        mRng = null;
+        mFirstOperationInitiated = false;
+        mIv = null;
+        mIvHasBeenUsed = false;
+        mAdditionalEntropyForBegin = null;
+        mOperationToken = null;
+        mOperationHandle = null;
+        mMainDataStreamer = null;
+    }
+
+    private void resetWhilePreservingInitState() {
         IBinder operationToken = mOperationToken;
         if (operationToken != null) {
             mOperationToken = null;
@@ -205,6 +228,12 @@
         if (mKey == null) {
             throw new IllegalStateException("Not initialized");
         }
+        if ((mEncrypting) && (mIvRequired) && (mIvHasBeenUsed)) {
+            // IV is being reused for encryption: this violates security best practices.
+            throw new IllegalStateException(
+                    "IV has already been used. Reusing IV in encryption mode violates security best"
+                    + " practices.");
+        }
 
         KeymasterArguments keymasterInputArgs = new KeymasterArguments();
         keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm);
@@ -234,6 +263,7 @@
         mOperationHandle = opResult.operationHandle;
         loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
         mFirstOperationInitiated = true;
+        mIvHasBeenUsed = true;
         mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                         mKeyStore, opResult.token));
@@ -298,7 +328,7 @@
             }
         }
 
-        reset();
+        resetWhilePreservingInitState();
         return output;
     }
 
@@ -376,7 +406,7 @@
      */
     @Override
     protected AlgorithmParameters engineGetParameters() {
-        if (!mIvUsed) {
+        if (!mIvRequired) {
             return null;
         }
         if ((mIv != null) && (mIv.length > 0)) {
@@ -408,7 +438,7 @@
      */
     protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
             throws InvalidAlgorithmParameterException {
-        if (!mIvUsed) {
+        if (!mIvRequired) {
             if (params != null) {
                 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
             }
@@ -447,7 +477,7 @@
      */
     protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
             throws InvalidAlgorithmParameterException {
-        if (!mIvUsed) {
+        if (!mIvRequired) {
             if (params != null) {
                 throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
             }
@@ -492,7 +522,7 @@
      *         and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters.
      */
     protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
-        if (!mIvUsed) {
+        if (!mIvRequired) {
             return;
         }
 
@@ -515,7 +545,7 @@
         if (!mFirstOperationInitiated) {
             // First begin operation -- see if we need to provide additional entropy for IV
             // generation.
-            if (mIvUsed) {
+            if (mIvRequired) {
                 // IV is needed
                 if ((mIv == null) && (mEncrypting)) {
                     // TODO: Switch to keymaster-generated IV code below once keymaster supports
@@ -534,7 +564,7 @@
             }
         }
 
-        if ((mIvUsed) && (mIv != null)) {
+        if ((mIvRequired) && (mIv != null)) {
             keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
         }
     }
@@ -557,7 +587,7 @@
             returnedIv = null;
         }
 
-        if (mIvUsed) {
+        if (mIvRequired) {
             if (mIv == null) {
                 mIv = returnedIv;
             } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index a5864a4..f69e7d1 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -35,9 +35,33 @@
  */
 public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
 
+    public static class HmacSHA1 extends KeyStoreHmacSpi {
+        public HmacSHA1() {
+            super(KeyStoreKeyConstraints.Digest.SHA1);
+        }
+    }
+
+    public static class HmacSHA224 extends KeyStoreHmacSpi {
+        public HmacSHA224() {
+            super(KeyStoreKeyConstraints.Digest.SHA224);
+        }
+    }
+
     public static class HmacSHA256 extends KeyStoreHmacSpi {
         public HmacSHA256() {
-            super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+            super(KeyStoreKeyConstraints.Digest.SHA256);
+        }
+    }
+
+    public static class HmacSHA384 extends KeyStoreHmacSpi {
+        public HmacSHA384() {
+            super(KeyStoreKeyConstraints.Digest.SHA384);
+        }
+    }
+
+    public static class HmacSHA512 extends KeyStoreHmacSpi {
+        public HmacSHA512() {
+            super(KeyStoreKeyConstraints.Digest.SHA512);
         }
     }
 
@@ -52,9 +76,9 @@
     private IBinder mOperationToken;
     private Long mOperationHandle;
 
-    protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+    protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest) {
         mDigest = digest;
-        mMacSizeBytes = macSizeBytes;
+        mMacSizeBytes = KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
     }
 
     @Override
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index 7137a9a..097c20f 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -327,7 +327,15 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
-            value = {Digest.NONE, Digest.SHA256})
+            value = {
+                Digest.NONE,
+                Digest.MD5,
+                Digest.SHA1,
+                Digest.SHA224,
+                Digest.SHA256,
+                Digest.SHA384,
+                Digest.SHA512,
+                })
     public @interface DigestEnum {}
 
     /**
@@ -343,9 +351,34 @@
         public static final int NONE = 1 << 0;
 
         /**
-         * SHA-256 digest.
+         * MD5 digest.
          */
-        public static final int SHA256 = 1 << 1;
+        public static final int MD5 = 1 << 1;
+
+        /**
+         * SHA-1 digest.
+         */
+        public static final int SHA1 = 1 << 2;
+
+        /**
+         * SHA-2 224 (aka SHA-224) digest.
+         */
+        public static final int SHA224 = 1 << 3;
+
+        /**
+         * SHA-2 256 (aka SHA-256) digest.
+         */
+        public static final int SHA256 = 1 << 4;
+
+        /**
+         * SHA-2 384 (aka SHA-384) digest.
+         */
+        public static final int SHA384 = 1 << 5;
+
+        /**
+         * SHA-2 512 (aka SHA-512) digest.
+         */
+        public static final int SHA512 = 1 << 6;
 
         /**
          * @hide
@@ -354,8 +387,18 @@
             switch (digest) {
                 case NONE:
                     return "NONE";
+                case MD5:
+                    return "MD5";
+                case SHA1:
+                    return "SHA-1";
+                case SHA224:
+                    return "SHA-224";
                 case SHA256:
-                    return "SHA256";
+                    return "SHA-256";
+                case SHA384:
+                    return "SHA-384";
+                case SHA512:
+                    return "SHA-512";
                 default:
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
@@ -364,13 +407,19 @@
         /**
          * @hide
          */
-        public static String[] allToString(@DigestEnum int digests) {
-            int[] values = getSetFlags(digests);
-            String[] result = new String[values.length];
-            for (int i = 0; i < values.length; i++) {
-                result[i] = toString(values[i]);
+        public static String allToString(@DigestEnum int digests) {
+            StringBuilder result = new StringBuilder("[");
+            boolean firstValue = true;
+            for (@DigestEnum int digest : getSetFlags(digests)) {
+                if (firstValue) {
+                    firstValue = false;
+                } else {
+                    result.append(", ");
+                }
+                result.append(toString(digest));
             }
-            return result;
+            result.append(']');
+            return result.toString();
         }
 
         /**
@@ -380,8 +429,18 @@
             switch (digest) {
                 case NONE:
                     return KeymasterDefs.KM_DIGEST_NONE;
+                case MD5:
+                    return KeymasterDefs.KM_DIGEST_MD5;
+                case SHA1:
+                    return KeymasterDefs.KM_DIGEST_SHA1;
+                case SHA224:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
                 case SHA256:
                     return KeymasterDefs.KM_DIGEST_SHA_2_256;
+                case SHA384:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
+                case SHA512:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
                 default:
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
@@ -394,8 +453,18 @@
             switch (digest) {
                 case KeymasterDefs.KM_DIGEST_NONE:
                     return NONE;
+                case KeymasterDefs.KM_DIGEST_MD5:
+                    return MD5;
+                case KeymasterDefs.KM_DIGEST_SHA1:
+                    return SHA1;
+                case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                    return SHA224;
                 case KeymasterDefs.KM_DIGEST_SHA_2_256:
                     return SHA256;
+                case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                    return SHA384;
+                case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                    return SHA512;
                 default:
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
@@ -429,11 +498,21 @@
         public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
             String algorithmLower = algorithm.toLowerCase(Locale.US);
             if (algorithmLower.startsWith("hmac")) {
-                if ("hmacsha256".equals(algorithmLower)) {
+                String digestLower = algorithmLower.substring("hmac".length());
+                if ("md5".equals(digestLower)) {
+                    return MD5;
+                } else if ("sha1".equals(digestLower)) {
+                    return SHA1;
+                } else if ("sha224".equals(digestLower)) {
+                    return SHA224;
+                } else if ("sha256".equals(digestLower)) {
                     return SHA256;
+                } else if ("sha384".equals(digestLower)) {
+                    return SHA384;
+                } else if ("sha512".equals(digestLower)) {
+                    return SHA512;
                 } else {
-                    throw new IllegalArgumentException("Unsupported digest: "
-                            + algorithmLower.substring("hmac".length()));
+                    throw new IllegalArgumentException("Unsupported digest: " + digestLower);
                 }
             } else {
                 return null;
@@ -447,8 +526,18 @@
             switch (digest) {
                 case NONE:
                     return "NONE";
+                case MD5:
+                    return "MD5";
+                case SHA1:
+                    return "SHA1";
+                case SHA224:
+                    return "SHA224";
                 case SHA256:
                     return "SHA256";
+                case SHA384:
+                    return "SHA384";
+                case SHA512:
+                    return "SHA512";
                 default:
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
@@ -461,8 +550,18 @@
             switch (digest) {
                 case NONE:
                     return null;
+                case MD5:
+                    return 128 / 8;
+                case SHA1:
+                    return 160 / 8;
+                case SHA224:
+                    return 224 / 8;
                 case SHA256:
                     return 256 / 8;
+                case SHA384:
+                    return 384 / 8;
+                case SHA512:
+                    return 512 / 8;
                 default:
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
@@ -471,7 +570,7 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
-            value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+            value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR, BlockMode.GCM})
     public @interface BlockModeEnum {}
 
     /**
@@ -489,6 +588,9 @@
         /** Counter (CTR) block mode. */
         public static final int CTR = 1 << 2;
 
+        /** Galois/Counter Mode (GCM) block mode. */
+        public static final int GCM = 1 << 3;
+
         /**
          * @hide
          */
@@ -500,6 +602,8 @@
                     return KeymasterDefs.KM_MODE_CBC;
                 case CTR:
                     return KeymasterDefs.KM_MODE_CTR;
+                case GCM:
+                    return KeymasterDefs.KM_MODE_GCM;
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
@@ -516,6 +620,8 @@
                     return CBC;
                 case KeymasterDefs.KM_MODE_CTR:
                     return CTR;
+                case KeymasterDefs.KM_MODE_GCM:
+                    return GCM;
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
@@ -554,6 +660,8 @@
                     return "CBC";
                 case CTR:
                     return "CTR";
+                case GCM:
+                    return "GCM";
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
@@ -570,6 +678,8 @@
                 return CBC;
             } else if ("ctr".equals(modeLower)) {
                 return CTR;
+            } else if ("gcm".equals(modeLower)) {
+                return GCM;
             } else {
                 throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index abce32d..279acd6 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -41,12 +41,41 @@
         }
     }
 
-    public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
-        public HmacSHA256() {
+    protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi {
+        protected HmacBase(@KeyStoreKeyConstraints.DigestEnum int digest) {
             super(KeyStoreKeyConstraints.Algorithm.HMAC,
-                    KeyStoreKeyConstraints.Digest.SHA256,
-                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
-                            KeyStoreKeyConstraints.Digest.SHA256) * 8);
+                    digest,
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest) * 8);
+        }
+    }
+
+    public static class HmacSHA1 extends HmacBase {
+        public HmacSHA1() {
+            super(KeyStoreKeyConstraints.Digest.SHA1);
+        }
+    }
+
+    public static class HmacSHA224 extends HmacBase {
+        public HmacSHA224() {
+            super(KeyStoreKeyConstraints.Digest.SHA224);
+        }
+    }
+
+    public static class HmacSHA256 extends HmacBase {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Digest.SHA256);
+        }
+    }
+
+    public static class HmacSHA384 extends HmacBase {
+        public HmacSHA384() {
+            super(KeyStoreKeyConstraints.Digest.SHA384);
+        }
+    }
+
+    public static class HmacSHA512 extends HmacBase {
+        public HmacSHA512() {
+            super(KeyStoreKeyConstraints.Digest.SHA512);
         }
     }
 
diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java
index 5600aeb..aa4a860 100644
--- a/location/java/android/location/FusedBatchOptions.java
+++ b/location/java/android/location/FusedBatchOptions.java
@@ -30,6 +30,8 @@
 
     // the default value is set to request fixes at no cost
     private volatile double mMaxPowerAllocationInMW = 0;
+    // If non-zero can be used for power savings by throttling location when device hasn't moved.
+    private volatile float mSmallestDisplacementMeters = 0;
 
     /*
      * Getters and setters for properties needed to hold the options.
@@ -50,6 +52,14 @@
         return mPeriodInNS;
     }
 
+    public void setSmallestDisplacementMeters(float value) {
+        mSmallestDisplacementMeters = value;
+    }
+
+    public float getSmallestDisplacementMeters() {
+        return mSmallestDisplacementMeters;
+    }
+
     public void setSourceToUse(int source) {
         mSourcesToUse |= source;
     }
@@ -112,6 +122,7 @@
             options.setPeriodInNS(parcel.readLong());
             options.setSourceToUse(parcel.readInt());
             options.setFlag(parcel.readInt());
+            options.setSmallestDisplacementMeters(parcel.readFloat());
             return options;
         }
 
@@ -132,5 +143,6 @@
         parcel.writeLong(mPeriodInNS);
         parcel.writeInt(mSourcesToUse);
         parcel.writeInt(mFlags);
+        parcel.writeFloat(mSmallestDisplacementMeters);
     }
 }
diff --git a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
index fd3f402..29818ec 100644
--- a/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
+++ b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java
@@ -43,6 +43,14 @@
         return mOptions.getPeriodInNS();
     }
 
+    public void setSmallestDisplacementMeters(float value) {
+        mOptions.setSmallestDisplacementMeters(value);
+    }
+
+    public float getSmallestDisplacementMeters() {
+        return mOptions.getSmallestDisplacementMeters();
+    }
+
     public void setSourceToUse(int source) {
         mOptions.setSourceToUse(source);
     }
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index f717ac7..0e1517f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -128,7 +128,7 @@
         android:id="@+id/clock"
         />
 
-    <Button android:id="@+id/alarm_status"
+    <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e92443c..3dece49 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1519,6 +1519,7 @@
         } catch (DeadObjectException e) {
             Slog.w(TAG, "Application dead when creating service " + r);
             mAm.appDiedLocked(app);
+            throw e;
         } finally {
             if (!created) {
                 // Keep the executeNesting count accurate.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 607e09c..d2f52b4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2211,8 +2211,8 @@
         mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
 
         // User 0 is the first and only user that runs at boot.
-        mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
-        mUserLru.add(Integer.valueOf(0));
+        mStartedUsers.put(UserHandle.USER_OWNER, new UserStartedState(UserHandle.OWNER, true));
+        mUserLru.add(UserHandle.USER_OWNER);
         updateStartedUserArrayLocked();
 
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -8641,7 +8641,7 @@
                     (ProviderInfo)providers.get(i);
                 boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                         cpi.name, cpi.flags);
-                if (singleton && UserHandle.getUserId(app.uid) != 0) {
+                if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
                     // This is a singleton provider, but a user besides the
                     // default user is asking to initialize a process it runs
                     // in...  well, no, it doesn't actually run in this process,
@@ -15603,7 +15603,7 @@
                 }
                 List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                         .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
-                if (user != 0 && newReceivers != null) {
+                if (user != UserHandle.USER_OWNER && newReceivers != null) {
                     // If this is not the primary user, we need to check for
                     // any receivers that should be filtered out.
                     for (int i=0; i<newReceivers.size(); i++) {
@@ -19302,7 +19302,7 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        if (userId <= 0) {
+        if (userId < 0 || userId == UserHandle.USER_OWNER) {
             throw new IllegalArgumentException("Can't stop primary user " + userId);
         }
         enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
@@ -19548,14 +19548,6 @@
         mUserSwitchObservers.unregister(observer);
     }
 
-    private boolean userExists(int userId) {
-        if (userId == 0) {
-            return true;
-        }
-        UserManagerService ums = getUserManagerLocked();
-        return ums != null ? (ums.getUserInfo(userId) != null) : false;
-    }
-
     int[] getUsersLocked() {
         UserManagerService ums = getUserManagerLocked();
         return ums != null ? ums.getUserIds() : new int[] { 0 };
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 34c1c53..5b5ebef 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -50,7 +50,7 @@
  * foreground priority, and one for normal (background-priority) broadcasts.
  */
 public final class BroadcastQueue {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "BroadcastQueue" : TAG_AM;
+    private static final String TAG = "BroadcastQueue";
     private static final String TAG_MU = TAG + POSTFIX_MU;
     private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7fab5a6..e790fb0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4701,7 +4701,8 @@
                 WindowState w = wtoken.allAppWindows.get(i);
                 if (w.mAppFreezing) {
                     w.mAppFreezing = false;
-                    if (w.mHasSurface && !w.mOrientationChanging) {
+                    if (w.mHasSurface && !w.mOrientationChanging
+                            && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
                         if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w);
                         w.mOrientationChanging = true;
                         mInnerFields.mOrientationChangeComplete = false;
@@ -9021,7 +9022,7 @@
         // If the screen is currently frozen or off, then keep
         // it frozen/off until this window draws at its new
         // orientation.
-        if (!okToDisplay()) {
+        if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG, "Changing surface while display frozen: " + w);
             w.mOrientationChanging = true;
             w.mLastFreezeDuration = 0;
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index 049e455..d5508bc 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -297,6 +297,14 @@
       getSourcesToUse
       );
 
+  jmethodID getSmallestDisplacementMeters = env->GetMethodID(
+      batchOptionsClass,
+      "getSmallestDisplacementMeters",
+      "()F"
+      );
+  batchOptions.smallest_displacement_meters
+      = env->CallFloatMethod(batchOptionsObject, getSmallestDisplacementMeters);
+
   jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
   batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
 
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index c5495a5..9956bd7 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -67,7 +67,7 @@
     libziparchive-host
 
 aaptCFlags := -DAAPT_VERSION=\"$(BUILD_NUMBER)\"
-aaptCFLAGS += -Wall -Werror
+aaptCFlags += -Wall -Werror
 
 ifeq ($(HOST_OS),linux)
     aaptHostLdLibs += -lrt -ldl -lpthread
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 063b4e6..e4738f5 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -412,7 +412,6 @@
                              int startX, int startY, int endX, int endY, int dX, int dY,
                              int* out_inset)
 {
-    bool opaque_within_inset = true;
     uint8_t max_opacity = 0;
     int inset = 0;
     *out_inset = 0;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 38d10cf..beb94fd 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -3056,7 +3056,6 @@
         const sp<AaptDir>& d = dirs.itemAt(k);
         const String8& dirName = d->getLeaf();
         Vector<String8> startTags;
-        const char* startTag = NULL;
         const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
             tagAttrPairs = &kLayoutTagAttrPairs;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 24f8168..c5fccbf 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -3167,7 +3167,7 @@
                     if (!validResources[i]) {
                         sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
                         if (c != NULL) {
-                            fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix,
+                            fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix,
                                     String8(typeName).string(), String8(c->getName()).string(),
                                     Res_MAKEID(p->getAssignedId() - 1, ti, i));
                         }
@@ -4526,7 +4526,6 @@
                     const KeyedVector<String16, Item>& bag = e->getBag();
                     const size_t bagCount = bag.size();
                     for (size_t bi = 0; bi < bagCount; bi++) {
-                        const Item& item = bag.valueAt(bi);
                         const uint32_t attrId = getResId(bag.keyAt(bi), &attr16);
                         const int sdkLevel = getPublicAttributeSdkLevel(attrId);
                         if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
diff --git a/tools/data-binding/annotationprocessor/build.gradle b/tools/data-binding/annotationprocessor/build.gradle
new file mode 100644
index 0000000..a639abb
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'maven'
+
+sourceCompatibility = 1.7
+targetCompatibility = 1.7
+
+buildscript {
+    repositories {
+        mavenLocal()
+        mavenCentral()
+    }
+}
+
+repositories {
+    mavenCentral()
+}
+
+sourceSets {
+    main {
+        java {
+            srcDir 'src/main/java'
+        }
+    }
+}
+
+dependencies {
+    compile project(":baseLibrary")
+    compile project(":compiler")
+    compile 'commons-codec:commons-codec:1.10'
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'annotationprocessor'
+        }
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java
new file mode 100644
index 0000000..c82a976
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+
+class AnnotationUtil {
+
+    /**
+     * Returns only the elements that are annotated with the given class. For some reason
+     * RoundEnvironment is returning elements annotated by other annotations.
+     */
+    static List<Element> getElementsAnnotatedWith(RoundEnvironment roundEnv,
+            Class<? extends Annotation> annotationClass) {
+        ArrayList<Element> elements = new ArrayList<>();
+        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
+            if (element.getAnnotation(annotationClass) != null) {
+                elements.add(element);
+            }
+        }
+        return elements;
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java
new file mode 100644
index 0000000..df525c1
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import com.google.common.base.Preconditions;
+
+import android.databinding.BindingBuildInfo;
+
+import java.lang.annotation.Annotation;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+
+public class BuildInfoUtil {
+    private static BindingBuildInfo sCached;
+    public static BindingBuildInfo load(RoundEnvironment roundEnvironment) {
+        if (sCached == null) {
+            sCached = extractNotNull(roundEnvironment, BindingBuildInfo.class);
+        }
+        return sCached;
+    }
+
+    private static <T extends Annotation> T extractNotNull(RoundEnvironment roundEnv,
+            Class<T> annotationClass) {
+        T result = null;
+        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
+            final T info = element.getAnnotation(annotationClass);
+            if (info == null) {
+                continue; // It gets confused between BindingAppInfo and BinderBundle
+            }
+            Preconditions.checkState(result == null, "Should have only one %s",
+                    annotationClass.getCanonicalName());
+            result = info;
+        }
+        return result;
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java
new file mode 100644
index 0000000..d4582cb
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java
@@ -0,0 +1,304 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import com.google.common.base.Preconditions;
+
+import android.databinding.Bindable;
+import android.databinding.BindingBuildInfo;
+import android.databinding.tool.CompilerChef.BindableHolder;
+import android.databinding.tool.util.GenerationalClassUtil;
+import android.databinding.tool.util.L;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+
+// binding app info and library info are necessary to trigger this.
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+public class ProcessBindable extends ProcessDataBinding.ProcessingStep implements BindableHolder {
+    private static final String INTERMEDIATE_FILE_EXT = "-br.bin";
+    Intermediate mProperties;
+    HashMap<String, HashSet<String>> mLayoutVariables = new HashMap<>();
+
+    @Override
+    public boolean onHandleStep(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
+            BindingBuildInfo buildInfo) {
+        if (mProperties == null) {
+            mProperties = new IntermediateV1(buildInfo.modulePackage());
+            mergeLayoutVariables();
+            mLayoutVariables.clear();
+            for (Element element : AnnotationUtil
+                    .getElementsAnnotatedWith(roundEnv, Bindable.class)) {
+                Element enclosingElement = element.getEnclosingElement();
+                ElementKind kind = enclosingElement.getKind();
+                if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
+                    L.e("Bindable must be on a member field or method. The enclosing type is %s",
+                            enclosingElement.getKind());
+                }
+                TypeElement enclosing = (TypeElement) enclosingElement;
+                String name = getPropertyName(element);
+                if (name != null) {
+                    Preconditions
+                            .checkNotNull(mProperties, "Must receive app / library info before "
+                                    + "Bindable fields.");
+                    mProperties.addProperty(enclosing.getQualifiedName().toString(), name);
+                }
+            }
+            if (mProperties.hasValues()) {
+                GenerationalClassUtil.writeIntermediateFile(processingEnv,
+                        mProperties.getPackage(),
+                        createIntermediateFileName(mProperties.getPackage()), mProperties);
+                generateBRClasses(!buildInfo.isLibrary(), mProperties.getPackage());
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void addVariable(String variableName, String containingClassName) {
+        HashSet<String> variableNames = mLayoutVariables.get(containingClassName);
+        if (variableNames == null) {
+            variableNames = new HashSet<>();
+            mLayoutVariables.put(containingClassName, variableNames);
+        }
+        variableNames.add(variableName);
+    }
+
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+    }
+
+    private String createIntermediateFileName(String appPkg) {
+        return appPkg + INTERMEDIATE_FILE_EXT;
+    }
+
+    private void generateBRClasses(boolean useFinalFields, String pkg) {
+        L.d("************* Generating BR file %s. use final: %s", pkg, useFinalFields);
+        HashSet<String> properties = new HashSet<>();
+        mProperties.captureProperties(properties);
+        List<Intermediate> previousIntermediates = loadPreviousBRFiles();
+        for (Intermediate intermediate : previousIntermediates) {
+            intermediate.captureProperties(properties);
+        }
+        writeBRClass(useFinalFields, pkg, properties);
+        if (useFinalFields) {
+            // generate BR for all previous packages
+            for (Intermediate intermediate : previousIntermediates) {
+                writeBRClass(true, intermediate.getPackage(),
+                        properties);
+            }
+        }
+    }
+
+    private void writeBRClass(boolean useFinalFields, String pkg, HashSet<String> properties) {
+        ArrayList<String> sortedProperties = new ArrayList<String>();
+        sortedProperties.addAll(properties);
+        Collections.sort(sortedProperties);
+        StringBuilder out = new StringBuilder();
+        String modifier = "public static " + (useFinalFields ? "final" : "") + " int ";
+        out.append("package " + pkg + ";\n\n" +
+                        "public class BR {\n" +
+                        "    " + modifier + "_all = 0;\n"
+        );
+        int id = 0;
+        for (String property : sortedProperties) {
+            id++;
+            out.append("    " + modifier + property + " = " + id + ";\n");
+        }
+        out.append("    public static int getId(String key) {\n");
+        out.append("        switch(key) {\n");
+        id = 0;
+        for (String property : sortedProperties) {
+            id++;
+            out.append("            case \"" + property + "\": return " + id + ";\n");
+        }
+        out.append("        }\n");
+        out.append("        return -1;\n");
+        out.append("    }");
+        out.append("}\n");
+
+        getWriter().writeToFile(pkg + ".BR", out.toString() );
+    }
+
+    private String getPropertyName(Element element) {
+        switch (element.getKind()) {
+            case FIELD:
+                return stripPrefixFromField((VariableElement) element);
+            case METHOD:
+                return stripPrefixFromMethod((ExecutableElement) element);
+            default:
+                L.e("@Bindable is not allowed on %s", element.getKind());
+                return null;
+        }
+    }
+
+    private static String stripPrefixFromField(VariableElement element) {
+        Name name = element.getSimpleName();
+        if (name.length() >= 2) {
+            char firstChar = name.charAt(0);
+            char secondChar = name.charAt(1);
+            if (name.length() > 2 && firstChar == 'm' && secondChar == '_') {
+                char thirdChar = name.charAt(2);
+                if (Character.isJavaIdentifierStart(thirdChar)) {
+                    return "" + Character.toLowerCase(thirdChar) +
+                            name.subSequence(3, name.length());
+                }
+            } else if ((firstChar == 'm' && Character.isUpperCase(secondChar)) ||
+                    (firstChar == '_' && Character.isJavaIdentifierStart(secondChar))) {
+                return "" + Character.toLowerCase(secondChar) + name.subSequence(2, name.length());
+            }
+        }
+        return name.toString();
+    }
+
+    private String stripPrefixFromMethod(ExecutableElement element) {
+        Name name = element.getSimpleName();
+        CharSequence propertyName;
+        if (isGetter(element) || isSetter(element)) {
+            propertyName = name.subSequence(3, name.length());
+        } else if (isBooleanGetter(element)) {
+            propertyName = name.subSequence(2, name.length());
+        } else {
+            L.e("@Bindable associated with method must follow JavaBeans convention %s", element);
+            return null;
+        }
+        char firstChar = propertyName.charAt(0);
+        return "" + Character.toLowerCase(firstChar) +
+                propertyName.subSequence(1, propertyName.length());
+    }
+
+    private void mergeLayoutVariables() {
+        for (String containingClass : mLayoutVariables.keySet()) {
+            for (String variable : mLayoutVariables.get(containingClass)) {
+                mProperties.addProperty(containingClass, variable);
+            }
+        }
+    }
+
+    private static boolean prefixes(CharSequence sequence, String prefix) {
+        boolean prefixes = false;
+        if (sequence.length() > prefix.length()) {
+            int count = prefix.length();
+            prefixes = true;
+            for (int i = 0; i < count; i++) {
+                if (sequence.charAt(i) != prefix.charAt(i)) {
+                    prefixes = false;
+                    break;
+                }
+            }
+        }
+        return prefixes;
+    }
+
+    private static boolean isGetter(ExecutableElement element) {
+        Name name = element.getSimpleName();
+        return prefixes(name, "get") &&
+                Character.isJavaIdentifierStart(name.charAt(3)) &&
+                element.getParameters().isEmpty() &&
+                element.getReturnType().getKind() != TypeKind.VOID;
+    }
+
+    private static boolean isSetter(ExecutableElement element) {
+        Name name = element.getSimpleName();
+        return prefixes(name, "set") &&
+                Character.isJavaIdentifierStart(name.charAt(3)) &&
+                element.getParameters().size() == 1 &&
+                element.getReturnType().getKind() == TypeKind.VOID;
+    }
+
+    private static boolean isBooleanGetter(ExecutableElement element) {
+        Name name = element.getSimpleName();
+        return prefixes(name, "is") &&
+                Character.isJavaIdentifierStart(name.charAt(2)) &&
+                element.getParameters().isEmpty() &&
+                element.getReturnType().getKind() == TypeKind.BOOLEAN;
+    }
+
+    private List<Intermediate> loadPreviousBRFiles() {
+        return GenerationalClassUtil
+                .loadObjects(getClass().getClassLoader(),
+                        new GenerationalClassUtil.ExtensionFilter(INTERMEDIATE_FILE_EXT));
+    }
+
+    private interface Intermediate extends Serializable {
+
+        void captureProperties(Set<String> properties);
+
+        void addProperty(String className, String propertyName);
+
+        boolean hasValues();
+
+        String getPackage();
+    }
+
+    private static class IntermediateV1 implements Serializable, Intermediate {
+
+        private static final long serialVersionUID = 2L;
+
+        private String mPackage;
+        private final HashMap<String, HashSet<String>> mProperties = new HashMap<>();
+
+        public IntermediateV1(String aPackage) {
+            mPackage = aPackage;
+        }
+
+        @Override
+        public void captureProperties(Set<String> properties) {
+            for (HashSet<String> propertySet : mProperties.values()) {
+                properties.addAll(propertySet);
+            }
+        }
+
+        @Override
+        public void addProperty(String className, String propertyName) {
+            HashSet<String> properties = mProperties.get(className);
+            if (properties == null) {
+                properties = new HashSet<>();
+                mProperties.put(className, properties);
+            }
+            properties.add(propertyName);
+        }
+
+        @Override
+        public boolean hasValues() {
+            return !mProperties.isEmpty();
+        }
+
+        @Override
+        public String getPackage() {
+            return mPackage;
+        }
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
new file mode 100644
index 0000000..944cc20
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
@@ -0,0 +1,128 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import android.databinding.BindingBuildInfo;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.writer.AnnotationJavaFileWriter;
+import android.databinding.tool.writer.JavaFileWriter;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+@SupportedAnnotationTypes({
+        "android.databinding.BindingAdapter",
+        "android.databinding.Untaggable",
+        "android.databinding.BindingMethods",
+        "android.databinding.BindingConversion",
+        "android.databinding.BindingBuildInfo"}
+)
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+/**
+ * Parent annotation processor that dispatches sub steps to ensure execution order.
+ * Use initProcessingSteps to add a new step.
+ */
+public class ProcessDataBinding extends AbstractProcessor {
+    private List<ProcessingStep> mProcessingSteps;
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (mProcessingSteps == null) {
+            initProcessingSteps();
+        }
+        final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
+        if (buildInfo == null) {
+            return false;
+        }
+        boolean done = true;
+        for (ProcessingStep step : mProcessingSteps) {
+            done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+        }
+        if (roundEnv.processingOver()) {
+            for (ProcessingStep step : mProcessingSteps) {
+                step.onProcessingOver(roundEnv, processingEnv, buildInfo);
+            }
+        }
+        return done;
+    }
+
+    private void initProcessingSteps() {
+        ProcessBindable processBindable = new ProcessBindable();
+        mProcessingSteps = Arrays.asList(
+                new ProcessMethodAdapters(),
+                new ProcessExpressions(processBindable),
+                processBindable
+        );
+        AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
+        for (ProcessingStep step : mProcessingSteps) {
+            step.mJavaFileWriter = javaFileWriter;
+        }
+    }
+
+    @Override
+    public synchronized void init(ProcessingEnvironment processingEnv) {
+        super.init(processingEnv);
+        ModelAnalyzer.setProcessingEnvironment(processingEnv);
+    }
+
+    /**
+     * To ensure execution order and binding build information, we use processing steps.
+     */
+    public abstract static class ProcessingStep {
+        private boolean mDone;
+        private JavaFileWriter mJavaFileWriter;
+
+        protected JavaFileWriter getWriter() {
+            return mJavaFileWriter;
+        }
+
+        private boolean runStep(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo) {
+            if (mDone) {
+                return true;
+            }
+            mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo);
+            return mDone;
+        }
+
+        /**
+         * Invoked in each annotation processing step.
+         *
+         * @return True if it is done and should never be invoked again.
+         */
+        abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo);
+
+        /**
+         * Invoked when processing is done. A good place to generate the output if the
+         * processor requires multiple steps.
+         */
+        abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo);
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
new file mode 100644
index 0000000..d62715a
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
@@ -0,0 +1,196 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import android.databinding.BindingBuildInfo;
+import android.databinding.tool.CompilerChef;
+import android.databinding.tool.reflection.SdkUtil;
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.util.GenerationalClassUtil;
+import android.databinding.tool.util.L;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
+
+    private static final String LAYOUT_INFO_FILE_SUFFIX = "-layoutinfo.bin";
+
+    private final ProcessBindable mProcessBindable;
+
+    public ProcessExpressions(ProcessBindable processBindable) {
+        mProcessBindable = processBindable;
+    }
+
+
+    @Override
+    public boolean onHandleStep(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+        ResourceBundle resourceBundle;
+        SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot()));
+        resourceBundle = new ResourceBundle(buildInfo.modulePackage());
+        List<Intermediate> intermediateList =
+                GenerationalClassUtil.loadObjects(getClass().getClassLoader(),
+                        new GenerationalClassUtil.ExtensionFilter(LAYOUT_INFO_FILE_SUFFIX));
+        IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir());
+        if (mine != null) {
+            mine.removeOverridden(intermediateList);
+            intermediateList.add(mine);
+            saveIntermediate(processingEnvironment, buildInfo, mine);
+        }
+        // generate them here so that bindable parser can read
+        try {
+            generateBinders(resourceBundle, buildInfo, intermediateList);
+        } catch (Throwable t) {
+            L.e(t, "cannot generate view binders");
+        }
+        return true;
+    }
+
+    private void saveIntermediate(ProcessingEnvironment processingEnvironment,
+            BindingBuildInfo buildInfo, IntermediateV1 intermediate) {
+        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
+                buildInfo.modulePackage(), buildInfo.modulePackage() + LAYOUT_INFO_FILE_SUFFIX,
+                intermediate);
+    }
+
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+    }
+
+    private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo,
+            List<Intermediate> intermediates)
+            throws Throwable {
+        for (Intermediate intermediate : intermediates) {
+            intermediate.appendTo(resourceBundle);
+        }
+        writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk());
+    }
+
+    private IntermediateV1 createIntermediateFromLayouts(String layoutInfoFolderPath) {
+        final File layoutInfoFolder = new File(layoutInfoFolderPath);
+        if (!layoutInfoFolder.isDirectory()) {
+            L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath);
+            return null;
+        }
+        IntermediateV1 result = new IntermediateV1();
+        for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.endsWith(".xml");
+            }
+        })) {
+            try {
+                result.addEntry(layoutFile.getName(), FileUtils.readFileToString(layoutFile));
+            } catch (IOException e) {
+                L.e(e, "cannot load layout file information. Try a clean build");
+            }
+        }
+        return result;
+    }
+
+    private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule,
+            int minSdk)
+            throws JAXBException {
+        CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter());
+        if (compilerChef.hasAnythingToGenerate()) {
+            compilerChef.addBRVariables(mProcessBindable);
+            compilerChef.writeViewBinderInterfaces(forLibraryModule);
+            if (!forLibraryModule) {
+                compilerChef.writeDbrFile(minSdk);
+                compilerChef.writeViewBinders();
+            }
+        }
+    }
+
+    public static interface Intermediate extends Serializable {
+
+        Intermediate upgrade();
+
+        public void appendTo(ResourceBundle resourceBundle) throws Throwable;
+    }
+
+    public static class IntermediateV1 implements Intermediate {
+
+        transient Unmarshaller mUnmarshaller;
+
+        // name to xml content map
+        Map<String, String> mLayoutInfoMap = new HashMap<>();
+
+        @Override
+        public Intermediate upgrade() {
+            return this;
+        }
+
+        @Override
+        public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
+            if (mUnmarshaller == null) {
+                JAXBContext context = JAXBContext
+                        .newInstance(ResourceBundle.LayoutFileBundle.class);
+                mUnmarshaller = context.createUnmarshaller();
+            }
+            for (String content : mLayoutInfoMap.values()) {
+                final InputStream is = IOUtils.toInputStream(content);
+                try {
+                    final ResourceBundle.LayoutFileBundle bundle
+                            = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is);
+                    resourceBundle.addLayoutBundle(bundle);
+                    L.d("loaded layout info file %s", bundle);
+                } finally {
+                    IOUtils.closeQuietly(is);
+                }
+            }
+        }
+
+        public void addEntry(String name, String contents) {
+            mLayoutInfoMap.put(name, contents);
+        }
+
+        public void removeOverridden(List<Intermediate> existing) {
+            // this is the way we get rid of files that are copied from previous modules
+            // it is important to do this before saving the intermediate file
+            for (Intermediate old : existing) {
+                if (old instanceof IntermediateV1) {
+                    IntermediateV1 other = (IntermediateV1) old;
+                    for (String key : other.mLayoutInfoMap.keySet()) {
+                        // TODO we should consider the original file as the key here
+                        // but aapt probably cannot provide that information
+                        if (mLayoutInfoMap.remove(key) != null) {
+                            L.d("removing %s from bundle because it came from another module", key);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java
new file mode 100644
index 0000000..98da839
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java
@@ -0,0 +1,177 @@
+/*
+ * 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 android.databinding.annotationprocessor;
+
+import com.google.common.base.Preconditions;
+
+import android.databinding.BindingAdapter;
+import android.databinding.BindingBuildInfo;
+import android.databinding.BindingConversion;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.databinding.Untaggable;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.util.L;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.tools.Diagnostic;
+
+public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep {
+    public ProcessMethodAdapters() {
+    }
+
+    @Override
+    public boolean onHandleStep(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+        L.d("processing adapters");
+        final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+        Preconditions.checkNotNull(modelAnalyzer, "Model analyzer should be"
+                + " initialized first");
+        SetterStore store = SetterStore.get(modelAnalyzer);
+        clearIncrementalClasses(roundEnv, store);
+
+        addBindingAdapters(roundEnv, processingEnvironment, store);
+        addRenamed(roundEnv, processingEnvironment, store);
+        addConversions(roundEnv, processingEnvironment, store);
+        addUntaggable(roundEnv, processingEnvironment, store);
+
+        try {
+            store.write(buildInfo.modulePackage(), processingEnvironment);
+        } catch (IOException e) {
+            L.e(e, "Could not write BindingAdapter intermediate file.");
+        }
+        return true;
+    }
+
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+
+    }
+
+    private void addBindingAdapters(RoundEnvironment roundEnv, ProcessingEnvironment
+            processingEnv, SetterStore store) {
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingAdapter.class)) {
+            if (element.getKind() != ElementKind.METHOD ||
+                    !element.getModifiers().contains(Modifier.STATIC) ||
+                    !element.getModifiers().contains(Modifier.PUBLIC)) {
+                L.e("@BindingAdapter on invalid element: %s", element);
+                continue;
+            }
+            BindingAdapter bindingAdapter = element.getAnnotation(BindingAdapter.class);
+
+            ExecutableElement executableElement = (ExecutableElement) element;
+            List<? extends VariableElement> parameters = executableElement.getParameters();
+            if (parameters.size() != 2) {
+                L.e("@BindingAdapter does not take two parameters: %s",element);
+                continue;
+            }
+            try {
+                L.d("------------------ @BindingAdapter for %s", element);
+                store.addBindingAdapter(bindingAdapter.value(), executableElement);
+            } catch (IllegalArgumentException e) {
+                L.e(e, "@BindingAdapter for duplicate View and parameter type: %s", element);
+            }
+        }
+    }
+
+    private void addRenamed(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
+            SetterStore store) {
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingMethods.class)) {
+            BindingMethods bindingMethods = element.getAnnotation(BindingMethods.class);
+            for (BindingMethod bindingMethod : bindingMethods.value()) {
+                store.addRenamedMethod(bindingMethod.attribute(),
+                        bindingMethod.type(), bindingMethod.method(), (TypeElement) element);
+            }
+        }
+    }
+
+    private void addConversions(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnv, SetterStore store) {
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingConversion.class)) {
+            if (element.getKind() != ElementKind.METHOD ||
+                    !element.getModifiers().contains(Modifier.STATIC) ||
+                    !element.getModifiers().contains(Modifier.PUBLIC)) {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "@BindingConversion is only allowed on public static methods: " + element);
+                continue;
+            }
+
+            ExecutableElement executableElement = (ExecutableElement) element;
+            if (executableElement.getParameters().size() != 1) {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "@BindingConversion method should have one parameter: " + element);
+                continue;
+            }
+            if (executableElement.getReturnType().getKind() == TypeKind.VOID) {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "@BindingConversion method must return a value: " + element);
+                continue;
+            }
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
+                    "added conversion: " + element);
+            store.addConversionMethod(executableElement);
+        }
+    }
+
+    private void addUntaggable(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnv, SetterStore store) {
+        for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
+            Untaggable untaggable = element.getAnnotation(Untaggable.class);
+            store.addUntaggableTypes(untaggable.value(), (TypeElement) element);
+        }
+    }
+
+    private void clearIncrementalClasses(RoundEnvironment roundEnv, SetterStore store) {
+        HashSet<String> classes = new HashSet<>();
+
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingAdapter.class)) {
+            TypeElement containingClass = (TypeElement) element.getEnclosingElement();
+            classes.add(containingClass.getQualifiedName().toString());
+        }
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingMethods.class)) {
+            classes.add(((TypeElement) element).getQualifiedName().toString());
+        }
+        for (Element element : AnnotationUtil
+                .getElementsAnnotatedWith(roundEnv, BindingConversion.class)) {
+            classes.add(((TypeElement) element.getEnclosingElement()).getQualifiedName().
+                    toString());
+        }
+        for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
+            classes.add(((TypeElement) element).getQualifiedName().toString());
+        }
+        store.clear(classes);
+    }
+
+}
diff --git a/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..482452e
--- /dev/null
+++ b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.databinding.annotationprocessor.ProcessDataBinding
\ No newline at end of file
diff --git a/tools/data-binding/baseLibrary/build.gradle b/tools/data-binding/baseLibrary/build.gradle
new file mode 100644
index 0000000..ecb7205
--- /dev/null
+++ b/tools/data-binding/baseLibrary/build.gradle
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'application'
+
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+
+buildscript {
+    repositories {
+        mavenLocal()
+        mavenCentral()
+    }
+}
+
+repositories {
+    mavenCentral()
+}
+
+sourceSets {
+    main {
+        java {
+            srcDir 'src/main/java'
+        }
+    }
+    test {
+        java {
+            srcDir 'src/test/java'
+        }
+    }
+}
+
+dependencies {
+    testCompile 'junit:junit:4.11'
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'baseLibrary'
+        }
+    }
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java
new file mode 100644
index 0000000..3dcebdd
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME) // this is necessary for java analyzer to work
+public @interface Bindable {
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java
new file mode 100644
index 0000000..3d50ed3
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+public @interface BindingAdapter {
+    String value();
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java
new file mode 100644
index 0000000..cbe1e99
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface BindingBuildInfo {
+    String buildId();
+    String modulePackage();
+    String sdkRoot();
+    int minSdk();
+
+    /**
+     * The folder that includes xml files which are exported by aapt or gradle plugin from layout files
+     */
+    String layoutInfoDir();
+    boolean isLibrary();
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java
new file mode 100644
index 0000000..9942f5c
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface BindingConversion {
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java
new file mode 100644
index 0000000..6384409
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java
@@ -0,0 +1,22 @@
+/*
+ * 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 android.databinding;
+
+public @interface BindingMethod {
+    String type();
+    String attribute();
+    String method();
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java
new file mode 100644
index 0000000..a3d39f8
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface BindingMethods {
+    BindingMethod[] value();
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java
new file mode 100644
index 0000000..002692f
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks callbacks for the event. This class supports reentrant modification
+ * of the callbacks during notification without adversely disrupting notifications.
+ * A common pattern for callbacks is to receive a notification and then remove
+ * themselves. This class handles this behavior with constant memory under
+ * most circumstances.
+ *
+ * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to
+ * the constructor to define how notifications should be called. That implementation
+ * does the actual notification on the listener.</p>
+ *
+ * <p>This class supports only callbacks with at most two parameters.
+ * Typically, these are the notification originator and a parameter, but these may
+ * be used as required. If more than two parameters are required or primitive types
+ * must be used, <code>A</code> should be some kind of containing structure that
+ * the subclass may reuse between notifications.</p>
+ *
+ * @param <C> The callback type.
+ * @param <T> The notification sender type. Typically this is the containing class.
+ * @param <A> Opaque argument used to pass additional data beyond an int.
+ */
+public class CallbackRegistry<C, T, A> implements Cloneable {
+    private static final String TAG = "CallbackRegistry";
+
+    /** An ordered collection of listeners waiting to be notified. */
+    private List<C> mCallbacks = new ArrayList<C>();
+
+    /**
+     * A bit flag for the first 64 listeners that are removed during notification.
+     * The lowest significant bit corresponds to the 0th index into mCallbacks.
+     * For a small number of callbacks, no additional array of objects needs to
+     * be allocated.
+     */
+    private long mFirst64Removed = 0x0;
+
+    /**
+     * Bit flags for the remaining callbacks that are removed during notification.
+     * When there are more than 64 callbacks and one is marked for removal, a dynamic
+     * array of bits are allocated for the callbacks.
+     */
+    private long[] mRemainderRemoved;
+
+    /** The recursion level of the notification */
+    private int mNotificationLevel;
+
+    /** The notification mechanism for notifying an event. */
+    private final NotifierCallback<C, T, A> mNotifier;
+
+    /**
+     * Creates an EventRegistry that notifies the event with notifier.
+     * @param notifier The class to use to notify events.
+     */
+    public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
+        mNotifier = notifier;
+    }
+
+    /**
+     * Notify all callbacks.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg2 An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     */
+    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
+        mNotificationLevel++;
+        notifyRecurse(sender, arg, arg2);
+        mNotificationLevel--;
+        if (mNotificationLevel == 0) {
+            if (mRemainderRemoved != null) {
+                for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
+                    final long removedBits = mRemainderRemoved[i];
+                    if (removedBits != 0) {
+                        removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
+                        mRemainderRemoved[i] = 0;
+                    }
+                }
+            }
+            if (mFirst64Removed != 0) {
+                removeRemovedCallbacks(0, mFirst64Removed);
+                mFirst64Removed = 0;
+            }
+        }
+    }
+
+    /**
+     * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg2 An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     */
+    private void notifyFirst64(T sender, int arg, A arg2) {
+        final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
+        notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
+    }
+
+    /**
+     * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
+     * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
+     * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}.
+     *
+     * <p>Recursion is used to avoid allocating temporary state on the heap.</p>
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg2 An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     */
+    private void notifyRecurse(T sender, int arg, A arg2) {
+        final int callbackCount = mCallbacks.size();
+        final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
+
+        // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the
+        // others.
+        notifyRemainder(sender, arg, arg2, remainderIndex);
+
+        // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1
+        // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
+        final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
+
+        // The remaining have no bit set
+        notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
+    }
+
+    /**
+     * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
+     * remainderIndex is -1, the first 64 will be notified instead.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg2 An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param remainderIndex The index into mRemainderRemoved that should be notified.
+     */
+    private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
+        if (remainderIndex < 0) {
+            notifyFirst64(sender, arg, arg2);
+        } else {
+            final long bits = mRemainderRemoved[remainderIndex];
+            final int startIndex = (remainderIndex + 1) * Long.SIZE;
+            final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
+            notifyRemainder(sender, arg, arg2, remainderIndex - 1);
+            notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
+        }
+    }
+
+    /**
+     * Notify callbacks from startIndex to endIndex, using bits as the bit status
+     * for whether they have been removed or not. bits should be from mRemainderRemoved or
+     * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
+     * endIndex should be notified.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param arg2 An opaque parameter passed to
+     *      {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+     * @param startIndex The index into the mCallbacks to start notifying.
+     * @param endIndex One past the last index into mCallbacks to notify.
+     * @param bits A bit field indicating which callbacks have been removed and shouldn't
+     *             be notified.
+     */
+    private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
+            final int endIndex, final long bits) {
+        long bitMask = 1;
+        for (int i = startIndex; i < endIndex; i++) {
+            if ((bits & bitMask) == 0) {
+                mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
+            }
+            bitMask <<= 1;
+        }
+    }
+
+    /**
+     * Add a callback to be notified. If the callback is already in the list, another won't
+     * be added. This does not affect current notifications.
+     * @param callback The callback to add.
+     */
+    public synchronized void add(C callback) {
+        int index = mCallbacks.lastIndexOf(callback);
+        if (index < 0 || isRemoved(index)) {
+            mCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Returns true if the callback at index has been marked for removal.
+     *
+     * @param index The index into mCallbacks to check.
+     * @return true if the callback at index has been marked for removal.
+     */
+    private boolean isRemoved(int index) {
+        if (index < Long.SIZE) {
+            // It is in the first 64 callbacks, just check the bit.
+            final long bitMask = 1L << index;
+            return (mFirst64Removed & bitMask) != 0;
+        } else if (mRemainderRemoved == null) {
+            // It is after the first 64 callbacks, but nothing else was marked for removal.
+            return false;
+        } else {
+            final int maskIndex = (index / Long.SIZE) - 1;
+            if (maskIndex >= mRemainderRemoved.length) {
+                // There are some items in mRemainderRemoved, but nothing at the given index.
+                return false;
+            } else {
+                // There is something marked for removal, so we have to check the bit.
+                final long bits = mRemainderRemoved[maskIndex];
+                final long bitMask = 1L << (index % Long.SIZE);
+                return (bits & bitMask) != 0;
+            }
+        }
+    }
+
+    /**
+     * Removes callbacks from startIndex to startIndex + Long.SIZE, based
+     * on the bits set in removed.
+     * @param startIndex The index into the mCallbacks to start removing callbacks.
+     * @param removed The bits indicating removal, where each bit is set for one callback
+     *                to be removed.
+     */
+    private void removeRemovedCallbacks(int startIndex, long removed) {
+        // The naive approach should be fine. There may be a better bit-twiddling approach.
+        final int endIndex = startIndex + Long.SIZE;
+
+        long bitMask = 1L << (Long.SIZE - 1);
+        for (int i = endIndex - 1; i >= startIndex; i--) {
+            if ((removed & bitMask) != 0) {
+                mCallbacks.remove(i);
+            }
+            bitMask >>>= 1;
+        }
+    }
+
+    /**
+     * Remove a callback. This callback won't be notified after this call completes.
+     * @param callback The callback to remove.
+     */
+    public synchronized void remove(C callback) {
+        if (mNotificationLevel == 0) {
+            mCallbacks.remove(callback);
+        } else {
+            int index = mCallbacks.lastIndexOf(callback);
+            if (index >= 0) {
+                setRemovalBit(index);
+            }
+        }
+    }
+
+    private void setRemovalBit(int index) {
+        if (index < Long.SIZE) {
+            // It is in the first 64 callbacks, just check the bit.
+            final long bitMask = 1L << index;
+            mFirst64Removed |= bitMask;
+        } else {
+            final int remainderIndex = (index / Long.SIZE) - 1;
+            if (mRemainderRemoved == null) {
+                mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
+            } else if (mRemainderRemoved.length < remainderIndex) {
+                // need to make it bigger
+                long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
+                System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
+                mRemainderRemoved = newRemainders;
+            }
+            final long bitMask = 1L << (index % Long.SIZE);
+            mRemainderRemoved[remainderIndex] |= bitMask;
+        }
+    }
+
+    /*
+    private void clearRemovalBit(int index) {
+        if (index < Long.SIZE) {
+            // It is in the first 64 callbacks, just check the bit.
+            final long bitMask = 1L << index;
+            mFirst64Removed &= ~bitMask;
+        } else if (mRemainderRemoved != null) {
+            final int maskIndex = (index / Long.SIZE) - 1;
+            if (maskIndex < mRemainderRemoved.length) {
+                // There is something marked for removal, so we have to check the bit.
+                final long bitMask = 1L << (index % Long.SIZE);
+                mRemainderRemoved[maskIndex] &= ~bitMask;
+            }
+        }
+    }
+    */
+
+    /**
+     * Makes a copy of the registered callbacks and returns it.
+     *
+     * @return a copy of the registered callbacks.
+     */
+    public synchronized ArrayList<C> copyListeners() {
+        ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
+        int numListeners = mCallbacks.size();
+        for (int i = 0; i < numListeners; i++) {
+            if (!isRemoved(i)) {
+                callbacks.add(mCallbacks.get(i));
+            }
+        }
+        return callbacks;
+    }
+
+    /**
+     * Returns true if there are no registered callbacks or false otherwise.
+     *
+     * @return true if there are no registered callbacks or false otherwise.
+     */
+    public synchronized boolean isEmpty() {
+        if (mCallbacks.isEmpty()) {
+            return true;
+        } else if (mNotificationLevel == 0) {
+            return false;
+        } else {
+            int numListeners = mCallbacks.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (!isRemoved(i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Removes all callbacks from the list.
+     */
+    public synchronized void clear() {
+        if (mNotificationLevel == 0) {
+            mCallbacks.clear();
+        } else if (!mCallbacks.isEmpty()) {
+            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                setRemovalBit(i);
+            }
+        }
+    }
+
+    public synchronized CallbackRegistry<C, T, A> clone() {
+        CallbackRegistry<C, T, A> clone = null;
+        try {
+            clone = (CallbackRegistry<C, T, A>) super.clone();
+            clone.mFirst64Removed = 0;
+            clone.mRemainderRemoved = null;
+            clone.mNotificationLevel = 0;
+            clone.mCallbacks = new ArrayList<C>();
+            final int numListeners = mCallbacks.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (!isRemoved(i)) {
+                    clone.mCallbacks.add(mCallbacks.get(i));
+                }
+            }
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+        }
+        return clone;
+    }
+
+    /**
+     * Class used to notify events from CallbackRegistry.
+     *
+     * @param <C> The callback type.
+     * @param <T> The notification sender type. Typically this is the containing class.
+     * @param <A> An opaque argument to pass to the notifier
+     */
+    public abstract static class NotifierCallback<C, T, A> {
+        /**
+         * Used to notify the callback.
+         *
+         * @param callback The callback to notify.
+         * @param sender The opaque sender object.
+         * @param arg The opaque notification parameter.
+         * @param arg2 An opaque argument passed in
+         *        {@link CallbackRegistry#notifyCallbacks}
+         * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
+         */
+        public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
+    }
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java
new file mode 100644
index 0000000..caca167
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 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;
+
+public interface Observable {
+
+    public void addOnPropertyChangedListener(OnPropertyChangedListener listener);
+
+    public void removeOnPropertyChangedListener(OnPropertyChangedListener listener);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java
new file mode 100644
index 0000000..3b82cf1
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java
@@ -0,0 +1,23 @@
+/*
+ * 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 android.databinding;
+
+import java.util.List;
+
+public interface ObservableList<T> extends List<T> {
+    void addOnListChangedListener(OnListChangedListener listener);
+    void removeOnListChangedListener(OnListChangedListener listener);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java
new file mode 100644
index 0000000..9240c48
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java
@@ -0,0 +1,23 @@
+/*
+ * 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 android.databinding;
+
+import java.util.Map;
+
+public interface ObservableMap<K, V> extends Map<K, V> {
+    void addOnMapChangedListener(OnMapChangedListener<? extends ObservableMap<K, V>, K> listener);
+    void removeOnMapChangedListener(OnMapChangedListener<? extends ObservableMap<K, V>, K> listener);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java
new file mode 100644
index 0000000..a76269e
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding;
+
+public interface OnListChangedListener {
+    void onChanged();
+    void onItemRangeChanged(int positionStart, int itemCount);
+    void onItemRangeInserted(int positionStart, int itemCount);
+    void onItemRangeMoved(int fromPosition, int toPosition, int itemCount);
+    void onItemRangeRemoved(int positionStart, int itemCount);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java
new file mode 100644
index 0000000..647b1f7
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java
@@ -0,0 +1,20 @@
+/*
+ * 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 android.databinding;
+
+public interface OnMapChangedListener<T extends ObservableMap<K, ?>, K> {
+    void onMapChanged(T sender, K key);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java
new file mode 100644
index 0000000..4103f07
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 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;
+
+public interface OnPropertyChangedListener {
+    public void onPropertyChanged(Observable sender, int fieldId);
+}
diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java
new file mode 100644
index 0000000..a1ce3ac
--- /dev/null
+++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface Untaggable {
+    String[] value();
+}
diff --git a/tools/data-binding/build.gradle b/tools/data-binding/build.gradle
new file mode 100644
index 0000000..675f196
--- /dev/null
+++ b/tools/data-binding/build.gradle
@@ -0,0 +1,74 @@
+Properties databindingProperties = new Properties()
+databindingProperties.load(new FileInputStream("${projectDir}/databinding.properties"))
+databindingProperties.mavenRepoDir = "${projectDir}/${databindingProperties.mavenRepoName}"
+ext.config = databindingProperties
+
+println "local maven repo is ${ext.config.mavenRepoDir}."
+
+new File(ext.config.mavenRepoDir).mkdir()
+subprojects {
+    apply plugin: 'maven'
+    group = config.group
+    version = config.snapshotVersion
+    repositories {
+        mavenCentral()
+        maven {
+            url "file://${config.mavenRepoDir}"
+        }
+    }
+    uploadArchives {
+        repositories {
+            mavenDeployer {
+                repository(url: "file://${config.mavenRepoDir}")
+            }
+        }
+    }
+}
+
+task deleteRepo(type: Delete) {
+    delete "${config.mavenRepoDir}"
+}
+
+def buildExtensionsTask = project.tasks.create "buildExtensionsTask", Exec
+buildExtensionsTask.workingDir file('extensions').getAbsolutePath()
+//on linux
+buildExtensionsTask.commandLine './gradlew'
+buildExtensionsTask.args 'clean', 'uploadArchives', '--info', '--stacktrace'
+buildExtensionsTask.dependsOn subprojects.uploadArchives
+
+file('integration-tests').listFiles().findAll { it.isDirectory() }.each {
+    println("Creating run test task for  ${it.getAbsolutePath()}.")
+    def testTask = project.tasks.create "runTestsOf${it.getName().capitalize()}", Exec
+    testTask.workingDir it.getAbsolutePath()
+    //on linux
+    testTask.commandLine './gradlew'
+    testTask.args 'clean', 'connectedCheck', '--info', '--stacktrace'
+    testTask.dependsOn subprojects.uploadArchives
+    testTask.dependsOn buildExtensionsTask
+}
+
+task runIntegrationTests {
+    dependsOn tasks.findAll { task -> task.name.startsWith('runTestsOf') }
+}
+
+task runAllTests {
+    dependsOn runIntegrationTests
+}
+
+allprojects {
+    afterEvaluate { project ->
+        runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('test')}
+        runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('connectedCheck')}
+    }
+}
+
+subprojects.uploadArchives.each { it.shouldRunAfter deleteRepo  }
+buildExtensionsTask.shouldRunAfter deleteRepo
+tasks['runTestsOfMultiModuleTestApp'].dependsOn tasks['runTestsOfIndependentLibrary']
+
+
+task rebuildRepo() {
+    dependsOn deleteRepo
+    dependsOn subprojects.uploadArchives
+    dependsOn buildExtensionsTask
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/build.gradle b/tools/data-binding/compiler/build.gradle
new file mode 100644
index 0000000..af0aaf0
--- /dev/null
+++ b/tools/data-binding/compiler/build.gradle
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+apply plugin: "kotlin"
+
+
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}"
+    }
+}
+
+
+dependencies {
+    compile 'junit:junit:4.12'
+    compile 'org.apache.commons:commons-lang3:3.3.2'
+    compile 'org.apache.commons:commons-io:1.3.2'
+    compile 'com.google.guava:guava:18.0'
+    compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}"
+    compile 'commons-codec:commons-codec:1.10'
+    compile project(":baseLibrary")
+    compile project(":grammarBuilder")
+    compile project(":xmlGrammar")
+    testCompile "com.android.databinding:libraryJar:$version@jar"
+}
+
+task fatJar(type: Jar) {
+    baseName = project.name + '-all'
+    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+    with jar
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'compiler'
+        }
+    }
+}
+
+project(':library').afterEvaluate { libProject ->
+    tasks['compileTestKotlin'].dependsOn libProject.tasks['uploadJarArchives']
+}
diff --git a/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3d0dee6
--- /dev/null
+++ b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..29fb85e
--- /dev/null
+++ b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Dec 11 16:05:38 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
diff --git a/tools/data-binding/compiler/gradlew b/tools/data-binding/compiler/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/compiler/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/compiler/gradlew.bat b/tools/data-binding/compiler/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/compiler/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java
new file mode 100644
index 0000000..f2bc96f
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.databinding.tool;
+
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.store.SetterStore.SetterCall;
+
+public class Binding {
+
+    private final String mName;
+    private final Expr mExpr;
+    private final BindingTarget mTarget;
+    private SetterStore.SetterCall mSetterCall;
+
+    public Binding(BindingTarget target, String name, Expr expr) {
+        mTarget = target;
+        mName = name;
+        mExpr = expr;
+    }
+
+    private SetterStore.SetterCall getSetterCall() {
+        if (mSetterCall == null) {
+            ModelClass viewType = mTarget.getResolvedType();
+            if (viewType != null && viewType.extendsViewStub()) {
+                if (isViewStubAttribute()) {
+                    mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr);
+                } else {
+                    mSetterCall = new ViewStubSetterCall(mName);
+                }
+            } else {
+                mSetterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName,
+                        viewType, mExpr.getResolvedType(), mExpr.getModel().getImports());
+            }
+        }
+        return mSetterCall;
+    }
+
+    public BindingTarget getTarget() {
+        return mTarget;
+    }
+
+    public String toJavaCode(String targetViewName, String expressionCode) {
+        return getSetterCall().toJava(targetViewName, expressionCode);
+    }
+
+    /**
+     * The min api level in which this binding should be executed.
+     * <p>
+     * This should be the minimum value among the dependencies of this binding. For now, we only
+     * check the setter.
+     */
+    public int getMinApi() {
+        return getSetterCall().getMinApi();
+    }
+
+//    private String resolveJavaCode(ModelAnalyzer modelAnalyzer) {
+//
+//    }
+////        return modelAnalyzer.findMethod(mTarget.getResolvedType(), mName,
+////                Arrays.asList(mExpr.getResolvedType()));
+//    //}
+//
+
+
+    public String getName() {
+        return mName;
+    }
+
+    public Expr getExpr() {
+        return mExpr;
+    }
+
+    private boolean isViewStubAttribute() {
+        if ("android:inflatedId".equals(mName)) {
+            return true;
+        } else if ("android:layout".equals(mName)) {
+            return true;
+        } else if ("android:visibility".equals(mName)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static class ViewStubSetterCall extends SetterCall {
+        private final String mName;
+
+        public ViewStubSetterCall(String name) {
+            mName = name.substring(name.lastIndexOf(':') + 1);
+        }
+
+        @Override
+        protected String toJavaInternal(String viewExpression, String converted) {
+            return "if (" + viewExpression + ".isInflated()) " + viewExpression +
+                    ".getBinding().setVariable(BR." + mName + ", " + converted + ")";
+        }
+
+        @Override
+        public int getMinApi() {
+            return 0;
+        }
+    }
+
+    private static class ViewStubDirectCall extends SetterCall {
+        private final SetterCall mWrappedCall;
+
+        public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) {
+            mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name,
+                    viewType, expr.getResolvedType(), expr.getModel().getImports());
+        }
+
+        @Override
+        protected String toJavaInternal(String viewExpression, String converted) {
+            return "if (!" + viewExpression + ".isInflated()) " +
+                    mWrappedCall.toJava(viewExpression + ".getViewStub()", converted);
+        }
+
+        @Override
+        public int getMinApi() {
+            return 0;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java
new file mode 100644
index 0000000..79156c0
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java
@@ -0,0 +1,111 @@
+/*
+ * 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 android.databinding.tool;
+
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.store.SetterStore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BindingTarget {
+    List<Binding> mBindings = new ArrayList<Binding>();
+    ExprModel mModel;
+    ModelClass mResolvedClass;
+    String mFieldName;
+
+    // if this target presents itself in multiple layout files with different view types,
+    // it receives an interface type and should use it in the getter instead.
+    private ResourceBundle.BindingTargetBundle mBundle;
+
+    public BindingTarget(ResourceBundle.BindingTargetBundle bundle) {
+        mBundle = bundle;
+    }
+
+    public boolean isUsed() {
+        return mBundle.isUsed();
+    }
+
+    public void addBinding(String name, Expr expr) {
+        mBindings.add(new Binding(this, name, expr));
+    }
+
+    public String getInterfaceType() {
+        return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
+    }
+
+    public String getId() {
+        return mBundle.getId();
+    }
+
+    public String getTag() {
+        return mBundle.getTag();
+    }
+
+    public String getOriginalTag() {
+        return mBundle.getOriginalTag();
+    }
+
+    public String getViewClass() {
+        return mBundle.getFullClassName();
+    }
+
+    public ModelClass getResolvedType() {
+        if (mResolvedClass == null) {
+            mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
+                    mModel.getImports());
+        }
+        return mResolvedClass;
+    }
+
+    public String getIncludedLayout() {
+        return mBundle.getIncludedLayout();
+    }
+
+    public boolean isBinder() {
+        return getIncludedLayout() != null;
+    }
+
+    public boolean supportsTag() {
+        return !SetterStore.get(ModelAnalyzer.getInstance())
+                .isUntaggable(mBundle.getFullClassName());
+    }
+
+    public List<Binding> getBindings() {
+        return mBindings;
+    }
+
+    public ExprModel getModel() {
+        return mModel;
+    }
+
+    public void setModel(ExprModel model) {
+        mModel = model;
+    }
+
+    public void setFieldName(String fieldName) {
+        mFieldName = fieldName;
+    }
+
+    public String getFieldName() {
+        return mFieldName;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java
new file mode 100644
index 0000000..90829c5
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java
@@ -0,0 +1,95 @@
+/*
+ * 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 android.databinding.tool;
+
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.util.L;
+import android.databinding.tool.writer.DataBinderWriter;
+import android.databinding.tool.writer.JavaFileWriter;
+
+/**
+ * Chef class for compiler.
+ *
+ * Different build systems can initiate a version of this to handle their work
+ */
+public class CompilerChef {
+    private JavaFileWriter mFileWriter;
+    private ResourceBundle mResourceBundle;
+    private DataBinder mDataBinder;
+
+    private CompilerChef() {
+    }
+
+    public static CompilerChef createChef(ResourceBundle bundle, JavaFileWriter fileWriter) {
+        CompilerChef chef = new CompilerChef();
+
+        chef.mResourceBundle = bundle;
+        chef.mFileWriter = fileWriter;
+        chef.mResourceBundle.validateMultiResLayouts();
+        return chef;
+    }
+
+    public ResourceBundle getResourceBundle() {
+        return mResourceBundle;
+    }
+
+    public void ensureDataBinder() {
+        if (mDataBinder == null) {
+            mDataBinder = new DataBinder(mResourceBundle);
+            mDataBinder.setFileWriter(mFileWriter);
+        }
+    }
+
+    public boolean hasAnythingToGenerate() {
+        L.d("checking if we have anything to generate. bundle size: %s",
+                mResourceBundle == null ? -1 : mResourceBundle.getLayoutBundles().size());
+        return mResourceBundle != null && mResourceBundle.getLayoutBundles().size() > 0;
+    }
+
+    public void writeDbrFile(int minSdk) {
+        ensureDataBinder();
+        final String pkg = "android.databinding";
+        DataBinderWriter dbr = new DataBinderWriter(pkg, mResourceBundle.getAppPackage(),
+                "DataBinderMapper", mDataBinder.getLayoutBinders(), minSdk);
+        if (dbr.getLayoutBinders().size() > 0) {
+            mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write());
+        }
+    }
+
+    /**
+     * Adds variables to list of Bindables.
+     */
+    public void addBRVariables(BindableHolder bindables) {
+        ensureDataBinder();
+        for (LayoutBinder layoutBinder : mDataBinder.mLayoutBinders) {
+            for (String variableName : layoutBinder.getUserDefinedVariables().keySet()) {
+                bindables.addVariable(variableName, layoutBinder.getClassName());
+            }
+        }
+    }
+    
+    public void writeViewBinderInterfaces(boolean isLibrary) {
+        ensureDataBinder();
+        mDataBinder.writerBaseClasses(isLibrary);
+    }
+
+    public void writeViewBinders() {
+        ensureDataBinder();
+        mDataBinder.writeBinders();
+    }
+
+    public interface BindableHolder {
+        void addVariable(String variableName, String containingClassName);
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java
new file mode 100644
index 0000000..70f8bcb
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java
@@ -0,0 +1,81 @@
+/*
+ * 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 android.databinding.tool;
+
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.util.L;
+import android.databinding.tool.writer.JavaFileWriter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The main class that handles parsing files and generating classes.
+ */
+public class DataBinder {
+    List<LayoutBinder> mLayoutBinders = new ArrayList<LayoutBinder>();
+
+    private JavaFileWriter mFileWriter;
+
+    public DataBinder(ResourceBundle resourceBundle) {
+        L.d("reading resource bundle into data binder");
+        for (Map.Entry<String, List<ResourceBundle.LayoutFileBundle>> entry :
+                resourceBundle.getLayoutBundles().entrySet()) {
+            for (ResourceBundle.LayoutFileBundle bundle : entry.getValue()) {
+                mLayoutBinders.add(new LayoutBinder(resourceBundle, bundle));
+            }
+        }
+    }
+    public List<LayoutBinder> getLayoutBinders() {
+        return mLayoutBinders;
+    }
+    
+    public void writerBaseClasses(boolean isLibrary) {
+        Set<String> writtenFiles = new HashSet<String>();
+        for (LayoutBinder layoutBinder : mLayoutBinders) {
+            if (isLibrary || layoutBinder.hasVariations()) {
+                String className = layoutBinder.getClassName();
+                if (writtenFiles.contains(className)) {
+                    continue;
+                }
+                mFileWriter.writeToFile(layoutBinder.getPackage() + "." + className,
+                        layoutBinder.writeViewBinderBaseClass());
+                writtenFiles.add(className);
+            }
+        }
+    }
+    
+    public void writeBinders() {
+        for (LayoutBinder layoutBinder : mLayoutBinders) {
+            String className = layoutBinder.getImplementationName();
+            L.d("writing data binder %s", className);
+            mFileWriter.writeToFile(layoutBinder.getPackage() + "." + className,
+                    layoutBinder.writeViewBinder());
+        }
+    }
+
+    public void setFileWriter(JavaFileWriter fileWriter) {
+        mFileWriter = fileWriter;
+    }
+
+    public JavaFileWriter getFileWriter() {
+        return mFileWriter;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java
new file mode 100644
index 0000000..03687ff
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.databinding.tool;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+
+import android.databinding.parser.BindingExpressionLexer;
+import android.databinding.parser.BindingExpressionParser;
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.util.L;
+
+public class ExpressionParser {
+    final ExprModel mModel;
+    final ExpressionVisitor visitor;
+
+    public ExpressionParser(ExprModel model) {
+        mModel = model;
+        visitor = new ExpressionVisitor(mModel);
+    }
+
+    public Expr parse(String input) {
+        ANTLRInputStream inputStream = new ANTLRInputStream(input);
+        BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream);
+        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+        BindingExpressionParser parser = new BindingExpressionParser(tokenStream);
+        BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax();
+        L.d("exp tree: %s", root.toStringTree(parser));
+        return root.accept(visitor);
+    }
+
+    public ExprModel getModel() {
+        return mModel;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java
new file mode 100644
index 0000000..a30f59b
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java
@@ -0,0 +1,317 @@
+/*
+ * 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 android.databinding.tool;
+
+import com.google.common.base.Preconditions;
+
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.apache.commons.lang3.ObjectUtils;
+
+import android.databinding.parser.BindingExpressionBaseVisitor;
+import android.databinding.parser.BindingExpressionParser;
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.expr.StaticIdentifierExpr;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
+    private final ExprModel mModel;
+    public ExpressionVisitor(ExprModel model) {
+        mModel = model;
+    }
+
+    @Override
+    public Expr visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) {
+        final String javaString;
+        if (ctx.SingleQuoteString() != null) {
+            String str = ctx.SingleQuoteString().getText();
+            String contents = str.substring(1, str.length() - 1);
+            contents = contents.replace("\"", "\\\"").replace("\\`", "`");
+            javaString = '"' + contents + '"';
+        } else {
+            javaString = ctx.DoubleQuoteString().getText();
+        }
+
+        return mModel.symbol(javaString, String.class);
+    }
+
+    @Override
+    public Expr visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) {
+        Preconditions.checkArgument(ctx.children.size() == 3, "Grouping expression should have"
+                + " 3 children. # of children: %d", ctx.children.size());
+        return mModel.group(ctx.children.get(1).accept(this));
+    }
+
+    @Override
+    public Expr visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) {
+        try {
+            // TODO handle defaults
+            return mModel.bindingExpr(ctx.expression().accept(this));
+        } catch (Exception e) {
+            System.out.println("Error while parsing! " + ctx.getText());
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Expr visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) {
+        ModelAnalyzer analyzer = ModelAnalyzer.getInstance();
+        ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports());
+        if (modelClass == null) {
+            return mModel.field(ctx.expression().accept(this),
+                    ctx.Identifier().getSymbol().getText());
+        } else {
+            String name = modelClass.toJavaCode();
+            StaticIdentifierExpr expr = mModel.staticIdentifier(name);
+            expr.setUserDefinedType(name);
+            return expr;
+        }
+    }
+
+    @Override
+    public Expr visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
+        final Expr left = ctx.left.accept(this);
+        return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
+                ctx.right.accept(this), left);
+    }
+
+    @Override
+    public Expr visitTerminal(@NotNull TerminalNode node) {
+        final int type = node.getSymbol().getType();
+        Class classType;
+        switch (type) {
+            case BindingExpressionParser.IntegerLiteral:
+                classType = int.class;
+                break;
+            case BindingExpressionParser.FloatingPointLiteral:
+                classType = float.class;
+                break;
+            case BindingExpressionParser.BooleanLiteral:
+                classType = boolean.class;
+                break;
+            case BindingExpressionParser.CharacterLiteral:
+                classType = char.class;
+                break;
+            case BindingExpressionParser.SingleQuoteString:
+            case BindingExpressionParser.DoubleQuoteString:
+                classType = String.class;
+                break;
+            case BindingExpressionParser.NullLiteral:
+                classType = Object.class;
+                break;
+            default:
+                throw new RuntimeException("cannot create expression from terminal node " + node.toString());
+        }
+        return mModel.symbol(node.getText(), classType);
+    }
+
+    @Override
+    public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) {
+        return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this));
+    }
+
+    @Override
+    public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) {
+        return mModel.identifier(ctx.getText());
+    }
+
+    @Override
+    public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) {
+        return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this),
+                ctx.iffalse.accept(this));
+    }
+
+    @Override
+    public Expr visitMethodInvocation(
+            @NotNull BindingExpressionParser.MethodInvocationContext ctx) {
+        List<Expr> args = new ArrayList<Expr>();
+        if (ctx.args != null) {
+            for (ParseTree item : ctx.args.children) {
+                if (ObjectUtils.equals(item.getText(), ",")) {
+                    continue;
+                }
+                args.add(item.accept(this));
+            }
+        }
+        return mModel.methodCall(ctx.target.accept(this),
+                ctx.Identifier().getText(), args);
+    }
+
+    @Override
+    public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) {
+        return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+    }
+
+    @Override
+    public Expr visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) {
+        final List<Expr> args = new ArrayList<Expr>();
+        if (ctx.resourceParameters() != null) {
+            for (ParseTree item : ctx.resourceParameters().expressionList().children) {
+                if (ObjectUtils.equals(item.getText(), ",")) {
+                    continue;
+                }
+                args.add(item.accept(this));
+            }
+        }
+        final String resourceReference = ctx.ResourceReference().getText();
+        final int colonIndex = resourceReference.indexOf(':');
+        final int slashIndex = resourceReference.indexOf('/');
+        final String packageName = colonIndex < 0 ? null :
+                resourceReference.substring(1, colonIndex).trim();
+        final int startIndex = Math.max(1, colonIndex + 1);
+        final String resourceType = resourceReference.substring(startIndex, slashIndex).trim();
+        final String resourceName = resourceReference.substring(slashIndex + 1).trim();
+        return mModel.resourceExpr(packageName, resourceType, resourceName, args);
+    }
+
+    @Override
+    public Expr visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) {
+        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();
+//        final VariableRef variableRef = mModel.getOrCreateVariable(identifier, null);
+//        mAccessedVariables.add(variableRef);
+//
+//        return new FieldExpr(variableRef, new ArrayList<VariableRef>(0));
+//    }
+//
+//    @Override
+//    public Expr visit(@NotNull ParseTree tree) {
+//        if (tree == null) {
+//            return null;
+//        }
+//        return super.visit(tree);
+//    }
+//
+//    @Override
+//    public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) {
+//        return new TernaryExpr(ctx.left.accept(this), ctx.iftrue.accept(this), ctx.iffalse.accept(this));
+//    }
+//
+//    @Override
+//    public Expr visitTerminal(@NotNull TerminalNode node) {
+//
+//        final int type = node.getSymbol().getType();
+//        switch (type) {
+//            case IntegerLiteral:
+//                return new SymbolExpr(node.getText(), Integer.class);
+//            case FloatingPointLiteral:
+//                return new SymbolExpr(node.getText(), Float.class);
+//            case BooleanLiteral:
+//                return new SymbolExpr(node.getText(), Boolean.class);
+//            case CharacterLiteral:
+//                return new SymbolExpr(node.getText(), Character.class);
+//            case SingleQuoteString:
+//                return new SymbolExpr(node.getText(), String.class);
+//            case DoubleQuoteString:
+//                return new SymbolExpr(node.getText(), String.class);
+//            case NullLiteral:
+//                return new SymbolExpr(node.getText(), Object.class);
+//            default:
+//                throw new RuntimeException("cannot create expression from terminal node " + node.toString());
+//        }
+//    }
+//
+//    @Override
+//    public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) {
+//        // TODO must support upper cast
+//        return new OpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+//    }
+//
+//    @Override
+//    public Expr visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) {
+//        return new BinaryOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+//    }
+//
+//    @Override
+//    public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) {
+//        return new ComparisonOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+//    }
+//
+//    @Override
+//    public Expr visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) {
+//        return new BinaryOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+//    }
+//
+//    @Override
+//    public Expr visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) {
+//        return new AndOrOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this));
+//    }
+//
+//    @Override
+//    protected Expr aggregateResult(final Expr aggregate, final Expr nextResult) {
+//        if (aggregate == null) {
+//            return nextResult;
+//        } else {
+//            return new Expr() {
+//                @org.jetbrains.annotations.NotNull
+//                @Override
+//                public Class<? extends Object> resolveValueType(
+//                        @org.jetbrains.annotations.NotNull ModelAnalyzer modelAnalyzer) {
+//                    return modelAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass());
+//                }
+//
+//                @org.jetbrains.annotations.NotNull
+//                @Override
+//                public String toReadableString() {
+//                    return aggregate.toReadableString() + ' ' + nextResult.toReadableString();
+//                }
+//
+//                @org.jetbrains.annotations.NotNull
+//                @Override
+//                public String toJava() {
+//                    return aggregate.toJava() + ' ' + nextResult.toJava();
+//                }
+//            };
+//        }
+//    }
+//
+//    @Override
+//    public Expr visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) {
+//        return visit(ctx.constantValue());
+//    }
+//
+//    @Override
+//    public Expr visitMethodInvocation(
+//            @NotNull BindingExpressionParser.MethodInvocationContext ctx) {
+//        final Expr expression = visit(ctx.expression());
+//        final String methodName = ctx.Identifier().getText();
+//        final ArrayList<Expr> parameters = new ArrayList<>();
+//        if (ctx.expressionList() != null) {
+//            for (BindingExpressionParser.ExpressionContext parameter : ctx.expressionList()
+//                    .expression()) {
+//                parameters.add(visit(parameter));
+//            }
+//        }
+//        return new MethodCallExpr(expression, methodName, parameters);
+//    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
new file mode 100644
index 0000000..e0b18d9
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
@@ -0,0 +1,212 @@
+/*
+ * 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 android.databinding.tool;
+
+import com.google.common.base.Preconditions;
+
+import android.databinding.tool.expr.Dependency;
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.expr.IdentifierExpr;
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
+import android.databinding.tool.util.ParserHelper;
+import android.databinding.tool.writer.LayoutBinderWriter;
+import android.databinding.tool.writer.WriterPackage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Keeps all information about the bindings per layout file
+ */
+public class LayoutBinder {
+    private static final Comparator<BindingTarget> COMPARE_FIELD_NAME = new Comparator<BindingTarget>() {
+        @Override
+        public int compare(BindingTarget first, BindingTarget second) {
+            final String fieldName1 = WriterPackage.getFieldName(first);
+            final String fieldName2 = WriterPackage.getFieldName(second);
+            return fieldName1.compareTo(fieldName2);
+        }
+    };
+
+    /*
+    * val pkg: String, val projectPackage: String, val baseClassName: String,
+        val layoutName:String, val lb: LayoutExprBinding*/
+    private final ExprModel mExprModel;
+    private final ExpressionParser mExpressionParser;
+    private final List<BindingTarget> mBindingTargets;
+    private String mPackage;
+    private String mModulePackage;
+    private String mProjectPackage;
+    private String mBaseClassName;
+    private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>();
+
+    private LayoutBinderWriter mWriter;
+    private ResourceBundle.LayoutFileBundle mBundle;
+
+    public LayoutBinder(ResourceBundle resourceBundle,
+            ResourceBundle.LayoutFileBundle layoutBundle) {
+        mExprModel = new ExprModel();
+        mExpressionParser = new ExpressionParser(mExprModel);
+        mBindingTargets = new ArrayList<BindingTarget>();
+        mBundle = layoutBundle;
+        mProjectPackage = resourceBundle.getAppPackage();
+        mModulePackage = layoutBundle.getModulePackage();
+        mPackage = layoutBundle.getModulePackage() + ".databinding";
+        mBaseClassName = ParserHelper.INSTANCE$.toClassName(layoutBundle.getFileName()) + "Binding";
+        // copy over data.
+        for (Map.Entry<String, String> variable : mBundle.getVariables().entrySet()) {
+            addVariable(variable.getKey(), variable.getValue());
+        }
+
+        for (Map.Entry<String, String> userImport : mBundle.getImports().entrySet()) {
+            mExprModel.addImport(userImport.getKey(), userImport.getValue());
+        }
+        for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
+            final BindingTarget bindingTarget = createBindingTarget(targetBundle);
+            for (ResourceBundle.BindingTargetBundle.BindingBundle bindingBundle : targetBundle
+                    .getBindingBundleList()) {
+                bindingTarget.addBinding(bindingBundle.getName(), parse(bindingBundle.getExpr()));
+            }
+        }
+        Collections.sort(mBindingTargets, COMPARE_FIELD_NAME);
+    }
+
+    public void resolveWhichExpressionsAreUsed() {
+        List<Expr> used = new ArrayList<Expr>();
+        for (BindingTarget target : mBindingTargets) {
+            for (Binding binding : target.getBindings()) {
+                binding.getExpr().setIsUsed(true);
+                used.add(binding.getExpr());
+            }
+        }
+        while (!used.isEmpty()) {
+            Expr e = used.remove(used.size() - 1);
+            for (Dependency dep : e.getDependencies()) {
+                if (!dep.getOther().isUsed()) {
+                    used.add(dep.getOther());
+                    dep.getOther().setIsUsed(true);
+                }
+            }
+        }
+    }
+
+    public IdentifierExpr addVariable(String name, String type) {
+        Preconditions.checkState(!mUserDefinedVariables.containsKey(name),
+                "%s has already been defined as %s", name, type);
+        final IdentifierExpr id = mExprModel.identifier(name);
+        id.setUserDefinedType(type);
+        id.enableDirectInvalidation();
+        mUserDefinedVariables.put(name, type);
+        return id;
+    }
+
+    public HashMap<String, String> getUserDefinedVariables() {
+        return mUserDefinedVariables;
+    }
+
+    public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
+        final BindingTarget target = new BindingTarget(targetBundle);
+        mBindingTargets.add(target);
+        target.setModel(mExprModel);
+        return target;
+    }
+
+    public Expr parse(String input) {
+        final Expr parsed = mExpressionParser.parse(input);
+        parsed.setBindingExpression(true);
+        return parsed;
+    }
+
+    public List<BindingTarget> getBindingTargets() {
+        return mBindingTargets;
+    }
+
+    public boolean isEmpty() {
+        return mExprModel.size() == 0;
+    }
+
+    public ExprModel getModel() {
+        return mExprModel;
+    }
+
+    private void ensureWriter() {
+        if (mWriter == null) {
+            mWriter = new LayoutBinderWriter(this);
+        }
+    }
+
+    public String writeViewBinderBaseClass() {
+        ensureWriter();
+        return mWriter.writeBaseClass();
+    }
+
+
+    public String writeViewBinder() {
+        mExprModel.seal();
+        ensureWriter();
+        Preconditions.checkNotNull(mPackage, "package cannot be null");
+        Preconditions.checkNotNull(mProjectPackage, "project package cannot be null");
+        Preconditions.checkNotNull(mBaseClassName, "base class name cannot be null");
+        return mWriter.write();
+    }
+
+    public String getPackage() {
+        return mPackage;
+    }
+
+    public String getModulePackage() {
+        return mModulePackage;
+    }
+
+    public void setPackage(String aPackage) {
+        mPackage = aPackage;
+    }
+
+    public String getProjectPackage() {
+        return mProjectPackage;
+    }
+
+    public String getLayoutname() {
+        return mBundle.getFileName();
+    }
+
+    public String getImplementationName() {
+        if (hasVariations()) {
+            return mBaseClassName + mBundle.getConfigName() + "Impl";
+        } else {
+            return mBaseClassName;
+        }
+    }
+    
+    public String getClassName() {
+        return mBaseClassName;
+    }
+
+    public String getTag() {
+        return mBundle.getDirectory() + "/" + mBundle.getFileName();
+    }
+
+    public boolean hasVariations() {
+        return mBundle.hasVariations();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
new file mode 100644
index 0000000..8cc01d2
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.databinding.tool;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.xml.sax.SAXException;
+
+import android.databinding.BindingBuildInfo;
+import android.databinding.tool.store.LayoutFileParser;
+import android.databinding.tool.store.ResourceBundle;
+import android.databinding.tool.writer.JavaFileWriter;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Processes the layout XML, stripping the binding attributes and elements
+ * and writes the information into an annotated class file for the annotation
+ * processor to work with.
+ */
+public class LayoutXmlProcessor {
+    // hardcoded in baseAdapters
+    public static final String RESOURCE_BUNDLE_PACKAGE = "android.databinding.layouts";
+    public static final String CLASS_NAME = "DataBindingInfo";
+    private final JavaFileWriter mFileWriter;
+    private final ResourceBundle mResourceBundle;
+    private final int mMinSdk;
+
+    private boolean mProcessingComplete;
+    private boolean mWritten;
+    private final boolean mIsLibrary;
+    private final String mBuildId = UUID.randomUUID().toString();
+    // can be a list of xml files or folders that contain XML files
+    private final List<File> mResources;
+
+    public LayoutXmlProcessor(String applicationPackage, List<File> resources,
+            JavaFileWriter fileWriter, int minSdk, boolean isLibrary) {
+        mFileWriter = fileWriter;
+        mResourceBundle = new ResourceBundle(applicationPackage);
+        mResources = resources;
+        mMinSdk = minSdk;
+        mIsLibrary = isLibrary;
+    }
+
+    public static List<File> getLayoutFiles(List<File> resources) {
+        List<File> result = new ArrayList<File>();
+        for (File resource : Iterables.filter(resources, fileExists)) {
+            if (resource.isDirectory()) {
+                for (File layoutFolder : resource.listFiles(layoutFolderFilter)) {
+                    for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) {
+                        result.add(xmlFile);
+                    }
+
+                }
+            } else if (xmlFileFilter.accept(resource.getParentFile(), resource.getName())) {
+                result.add(resource);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * used by the studio plugin
+     */
+    public ResourceBundle getResourceBundle() {
+        return mResourceBundle;
+    }
+
+    public boolean processResources()
+            throws ParserConfigurationException, SAXException, XPathExpressionException,
+            IOException {
+        if (mProcessingComplete) {
+            return false;
+        }
+        LayoutFileParser layoutFileParser = new LayoutFileParser();
+        for (File xmlFile : getLayoutFiles(mResources)) {
+            final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
+                    .parseXml(xmlFile, mResourceBundle.getAppPackage());
+            if (bindingLayout != null && !bindingLayout.isEmpty()) {
+                mResourceBundle.addLayoutBundle(bindingLayout);
+            }
+        }
+        mProcessingComplete = true;
+        return true;
+    }
+
+    public void writeIntermediateFile(File sdkDir, File xmlOutDir) throws JAXBException {
+        if (mWritten) {
+            return;
+        }
+        JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
+        Marshaller marshaller = context.createMarshaller();
+        writeInfoClass(sdkDir, xmlOutDir);
+        for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
+                .values()) {
+            for (ResourceBundle.LayoutFileBundle layout : layouts) {
+                writeXmlFile(xmlOutDir, layout, marshaller);
+            }
+        }
+        mWritten = true;
+    }
+
+    private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout,
+            Marshaller marshaller) throws JAXBException {
+        String filename = generateExportFileName(layout) + ".xml";
+        String xml = toXML(layout, marshaller);
+        mFileWriter.writeToFile(new File(xmlOutDir, filename), xml);
+    }
+
+    public String getInfoClassFullName() {
+        return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME;
+    }
+
+    private String toXML(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller)
+            throws JAXBException {
+        StringWriter writer = new StringWriter();
+        marshaller.marshal(layout, writer);
+        return writer.getBuffer().toString();
+    }
+
+    /**
+     * Generates a string identifier that can uniquely identify the given layout bundle.
+     * This identifier can be used when we need to export data about this layout bundle.
+     */
+    private String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
+        StringBuilder name = new StringBuilder(layout.getFileName());
+        name.append('-').append(layout.getDirectory());
+        for (int i = name.length() - 1; i >= 0; i--) {
+            char c = name.charAt(i);
+            if (c == '-') {
+                name.deleteCharAt(i);
+                c = Character.toUpperCase(name.charAt(i));
+                name.setCharAt(i, c);
+            }
+        }
+        return name.toString();
+    }
+
+    private void writeInfoClass(File sdkDir, File xmlOutDir) {
+        final String sdkPath = StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath());
+        final Class annotation = BindingBuildInfo.class;
+        final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath());
+        String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
+                "import " + annotation.getCanonicalName() + ";\n\n" +
+                "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
+                "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " +
+                "sdkRoot=\"" + sdkPath + "\", " +
+                "layoutInfoDir=\"" + layoutInfoPath + "\"," +
+                "isLibrary=" + mIsLibrary + "," +
+                "minSdk=" + mMinSdk + ")\n" +
+                "public class " + CLASS_NAME + " {}\n";
+        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME, classString);
+    }
+
+    private static final Predicate<File> fileExists = new Predicate<File>() {
+        @Override
+        public boolean apply(File input) {
+            return input.exists() && input.canRead();
+        }
+    };
+
+    private static final FilenameFilter layoutFolderFilter = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.startsWith("layout");
+        }
+    };
+
+    private static final FilenameFilter xmlFileFilter = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().endsWith(".xml");
+        }
+    };
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java
new file mode 100644
index 0000000..ac585d1
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java
@@ -0,0 +1,245 @@
+/*
+ * 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 android.databinding.tool;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.w3c.dom.Document;
+
+import android.databinding.tool.writer.JavaFileWriter;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+/**
+ * This class is used by make to copy resources to an intermediate directory and start processing
+ * them. When aapt takes over, this can be easily extracted to a short script.
+ */
+public class MakeCopy {
+    private static final int MANIFEST_INDEX = 0;
+    private static final int ADK_INDEX = 1;
+    private static final int SRC_INDEX = 2;
+    private static final int XML_INDEX = 3;
+    private static final int RES_OUT_INDEX = 4;
+    private static final int RES_IN_INDEX = 5;
+
+    private static final String APP_SUBPATH = LayoutXmlProcessor.RESOURCE_BUNDLE_PACKAGE
+            .replace('.', File.separatorChar);
+    private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().startsWith("layout");
+        }
+    };
+
+    private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() {
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().endsWith(".xml");
+        }
+    };
+
+    public static void main(String[] args) {
+        if (args.length < 6) {
+            System.out.println("required parameters: manifest adk-dir src-out-dir xml-out-dir " +
+                            "res-out-dir res-in-dir...");
+            System.out.println("Creates an android data binding class and copies resources from");
+            System.out.println("res-source to res-target and modifies binding layout files");
+            System.out.println("in res-target. Binding data is extracted into XML files");
+            System.out.println("and placed in xml-out-dir.");
+            System.out.println("  manifest    path to AndroidManifest.xml file");
+            System.out.println("  adk-dir     path to Android SDK home");
+            System.out.println("  src-out-dir path to where generated source goes");
+            System.out.println("  xml-out-dir path to where generated binding XML goes");
+            System.out.println("  res-out-dir path to the where modified resources should go");
+            System.out.println("  res-in-dir  path to source resources \"res\" directory. One" +
+                    " or more are allowed.");
+            System.exit(1);
+        }
+        final boolean isLibrary;
+        final String applicationPackage;
+        final int minSdk;
+        final Document androidManifest = readAndroidManifest(new File(args[MANIFEST_INDEX]));
+        try {
+            final XPathFactory xPathFactory = XPathFactory.newInstance();
+            final XPath xPath = xPathFactory.newXPath();
+            isLibrary = (Boolean) xPath.evaluate("boolean(/manifest/application)", androidManifest,
+                    XPathConstants.BOOLEAN);
+            applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest);
+            final Double minSdkNumber = (Double) xPath.evaluate(
+                    "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest,
+                    XPathConstants.NUMBER);
+            minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue();
+        } catch (XPathExpressionException e) {
+            e.printStackTrace();
+            System.exit(6);
+            return;
+        }
+        final File srcDir = new File(args[SRC_INDEX], APP_SUBPATH);
+        if (!makeTargetDir(srcDir)) {
+            System.err.println("Could not create source directory " + srcDir);
+            System.exit(2);
+        }
+        final File resTarget = new File(args[RES_OUT_INDEX]);
+        if (!makeTargetDir(resTarget)) {
+            System.err.println("Could not create resource directory: " + resTarget);
+            System.exit(4);
+        }
+        final File xmlDir = new File(args[XML_INDEX]);
+        if (!makeTargetDir(xmlDir)) {
+            System.err.println("Could not create xml output directory: " + xmlDir);
+            System.exit(5);
+        }
+        final File adkDir = new File(args[ADK_INDEX]);
+        if (!adkDir.exists()) {
+            System.err.println("Could not find android SDK directory: " + adkDir);
+            System.exit(6);
+        }
+        System.out.println("Application Package: " + applicationPackage);
+        System.out.println("Minimum SDK: " + minSdk);
+        System.out.println("Target Resources: " + resTarget.getAbsolutePath());
+        System.out.println("Target Source Dir: " + srcDir.getAbsolutePath());
+        System.out.println("Target XML Dir: " + xmlDir.getAbsolutePath());
+
+        boolean foundSomeResources = false;
+        for (int i = RES_IN_INDEX; i < args.length; i++) {
+            final File resDir = new File(args[i]);
+            if (!resDir.exists()) {
+                System.err.println("Could not find resource directory: " + resDir);
+            } else {
+                System.out.println("Source Resources: " + resDir.getAbsolutePath());
+                try {
+                    FileUtils.copyDirectory(resDir, resTarget);
+                    addFromFile(resDir, resTarget);
+                    foundSomeResources = true;
+                } catch (IOException e) {
+                    System.err.println("Could not copy resources from " + resDir + " to " + resTarget +
+                            ": " + e.getLocalizedMessage());
+                    System.exit(3);
+                }
+            }
+        }
+
+        if (!foundSomeResources) {
+            System.err.println("No resource directories were found.");
+            System.exit(7);
+        }
+        processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, adkDir, minSdk,
+                isLibrary);
+    }
+
+    private static Document readAndroidManifest(File manifest) {
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
+            return documentBuilder.parse(manifest);
+        } catch (Exception e) {
+            System.err.println("Could not load Android Manifest from " +
+                    manifest.getAbsolutePath() + ": " + e.getLocalizedMessage());
+            System.exit(8);
+            return null;
+        }
+    }
+
+    private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir,
+            File xmlDir, File adkDir, int minSdk, boolean isLibrary) {
+        ArrayList<File> resourceFolders = new ArrayList<File>();
+        resourceFolders.add(resTarget);
+        MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir);
+        LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage,
+                resourceFolders, makeFileWriter, minSdk, isLibrary);
+        try {
+            xmlProcessor.processResources();
+            xmlProcessor.writeIntermediateFile(adkDir, xmlDir);
+            if (makeFileWriter.getErrorCount() > 0) {
+                System.exit(9);
+            }
+        } catch (Exception e) {
+            System.err.println("Error processing layout files: " + e.getLocalizedMessage());
+            System.exit(10);
+        }
+    }
+
+    private static void addFromFile(File resDir, File resTarget) {
+        for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) {
+            if (layoutDir.isDirectory()) {
+                File targetDir = new File(resTarget, layoutDir.getName());
+                for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) {
+                    File targetFile = new File(targetDir, layoutFile.getName());
+                    FileWriter appender = null;
+                    try {
+                        appender = new FileWriter(targetFile, true);
+                        appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n");
+                    } catch (IOException e) {
+                        System.err.println("Could not update " + layoutFile + ": " +
+                                e.getLocalizedMessage());
+                    } finally {
+                        IOUtils.closeQuietly(appender);
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean makeTargetDir(File dir) {
+        if (dir.exists()) {
+            return dir.isDirectory();
+        }
+
+        return dir.mkdirs();
+    }
+
+    private static class MakeFileWriter extends JavaFileWriter {
+        private final File mSourceRoot;
+        private int mErrorCount;
+
+        public MakeFileWriter(File sourceRoot) {
+            mSourceRoot = sourceRoot;
+        }
+
+        @Override
+        public void writeToFile(String canonicalName, String contents) {
+            String fileName = canonicalName.replace('.', File.separatorChar) + ".java";
+            File sourceFile = new File(mSourceRoot, fileName);
+            FileWriter writer = null;
+            try {
+                sourceFile.getParentFile().mkdirs();
+                writer = new FileWriter(sourceFile);
+                writer.write(contents);
+            } catch (IOException e) {
+                System.err.println("Could not write to " + sourceFile + ": " +
+                        e.getLocalizedMessage());
+                mErrorCount++;
+            } finally {
+                IOUtils.closeQuietly(writer);
+            }
+        }
+
+        public int getErrorCount() {
+            return mErrorCount;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
new file mode 100644
index 0000000..b5cce7d
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.List;
+
+public class BracketExpr extends Expr {
+
+    public static enum BracketAccessor {
+        ARRAY,
+        LIST,
+        MAP,
+    }
+
+    private BracketAccessor mAccessor;
+
+    BracketExpr(Expr target, Expr arg) {
+        super(target, arg);
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        ModelClass targetType = getTarget().resolveType(modelAnalyzer);
+        if (targetType.isArray()) {
+            mAccessor = BracketAccessor.ARRAY;
+        } else if (targetType.isList()) {
+            mAccessor = BracketAccessor.LIST;
+        } else if (targetType.isMap()) {
+            mAccessor = BracketAccessor.MAP;
+        } else {
+            throw new IllegalArgumentException("Cannot determine variable type used in [] " +
+                    "expression. Cast the value to List, Map, " +
+                    "or array. Type detected: " + targetType.toJavaCode());
+        }
+        return targetType.getComponentType();
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return constructDynamicChildrenDependencies();
+    }
+
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(getTarget().computeUniqueKey(), "$", getArg().computeUniqueKey(), "$");
+    }
+
+    public Expr getTarget() {
+        return getChildren().get(0);
+    }
+
+    public Expr getArg() {
+        return getChildren().get(1);
+    }
+
+    public BracketAccessor getAccessor() {
+        return mAccessor;
+    }
+
+    public boolean argCastsInteger() {
+        return Object.class.equals(getArg().getResolvedType());
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
new file mode 100644
index 0000000..b4e41da
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
@@ -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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.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, getModel().getImports());
+    }
+
+    @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 getResolvedType().toJavaCode();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
new file mode 100644
index 0000000..0c237be
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
@@ -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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.List;
+
+public class ComparisonExpr extends Expr {
+    final String mOp;
+    ComparisonExpr(String op, Expr left, Expr right) {
+        super(left, right);
+        mOp = op;
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(mOp, super.computeUniqueKey());
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        return modelAnalyzer.loadPrimitive("boolean");
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return constructDynamicChildrenDependencies();
+    }
+
+    public String getOp() {
+        return mOp;
+    }
+
+    public Expr getLeft() {
+        return getChildren().get(0);
+    }
+
+    public Expr getRight() {
+        return getChildren().get(1);
+    }
+
+    @Override
+    public boolean isEqualityCheck() {
+        return "==".equals(mOp.trim());
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java
new file mode 100644
index 0000000..1678af7
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+public class Dependency {
+    final Expr mDependant;
+    final Expr mOther;
+    final Expr mCondition;
+    final boolean mExpectedOutput;// !
+    // set only if this is conditional. Means it has been resolved so that it can be used in
+    // should get calculations
+    boolean mElevated;
+
+    // this means that trying to calculate the dependant expression w/o
+    // will crash the app unless "Other" has a non-null value
+    boolean mMandatory = false;
+
+    public Dependency(Expr dependant, Expr other) {
+        mDependant = dependant;
+        mOther = other;
+        mCondition = null;
+        mOther.addDependant(this);
+        mExpectedOutput = false;
+    }
+
+    public Dependency(Expr dependant, Expr other, Expr condition, boolean expectedOutput) {
+        mDependant = dependant;
+        mOther = other;
+        mCondition = condition;
+        mOther.addDependant(this);
+        mExpectedOutput = expectedOutput;
+    }
+
+    public void setMandatory(boolean mandatory) {
+        mMandatory = mandatory;
+    }
+
+    public boolean isMandatory() {
+        return mMandatory;
+    }
+
+    public boolean isConditional() {
+        return mCondition != null && !mElevated;
+    }
+
+    public Expr getOther() {
+        return mOther;
+    }
+
+    public Expr getDependant() {
+        return mDependant;
+    }
+
+    public boolean getExpectedOutput() {
+        return mExpectedOutput;
+    }
+
+    public Expr getCondition() {
+        return mCondition;
+    }
+
+    public void elevate() {
+        mElevated = true;
+    }
+
+    public boolean isElevated() {
+        return mElevated;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java
new file mode 100644
index 0000000..09b96d8
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java
@@ -0,0 +1,612 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+
+abstract public class Expr {
+
+    public static final int NO_ID = -1;
+    protected List<Expr> mChildren = new ArrayList<Expr>();
+
+    // any expression that refers to this. Useful if this expr is duplicate and being replaced
+    private List<Expr> mParents = new ArrayList<Expr>();
+
+    private Boolean mIsDynamic;
+
+    private ModelClass mResolvedType;
+
+    private String mUniqueKey;
+
+    private List<Dependency> mDependencies;
+
+    private List<Dependency> mDependants = Lists.newArrayList();
+
+    private int mId = NO_ID;
+
+    private int mRequirementId = NO_ID;
+
+    // means this expression can directly be invalidated by the user
+    private boolean mCanBeInvalidated = false;
+
+    /**
+     * This set denotes the times when this expression is invalid.
+     * If it is an Identifier expression, it is its index
+     * If it is a composite expression, it is the union of invalid flags of its descendants
+     */
+    private BitSet mInvalidFlags;
+
+    /**
+     * Set when this expression is registered to a model
+     */
+    private ExprModel mModel;
+
+    /**
+     * This set denotes the times when this expression must be read.
+     *
+     * It is the union of invalidation flags of all of its non-conditional dependants.
+     */
+    BitSet mShouldReadFlags;
+
+    BitSet mReadSoFar = new BitSet();// i've read this variable for these flags
+
+    /**
+     * calculated on initialization, assuming all conditionals are true
+     */
+    BitSet mShouldReadWithConditionals;
+
+    private boolean mIsBindingExpression;
+
+    /**
+     * Used by generators when this expression is resolved.
+     */
+    private boolean mRead;
+    private boolean mIsUsed = false;
+
+    Expr(Iterable<Expr> children) {
+        for (Expr expr : children) {
+            mChildren.add(expr);
+        }
+        addParents();
+    }
+
+    Expr(Expr... children) {
+        Collections.addAll(mChildren, children);
+        addParents();
+    }
+
+    public int getId() {
+        Preconditions.checkState(mId != NO_ID, "if getId is called on an expression, it should have"
+                + " and id");
+        return mId;
+    }
+
+    public void setId(int id) {
+        Preconditions.checkState(mId == NO_ID, "ID is already set on " + this);
+        mId = id;
+    }
+
+    public ExprModel getModel() {
+        return mModel;
+    }
+
+    public BitSet getInvalidFlags() {
+        if (mInvalidFlags == null) {
+            mInvalidFlags = resolveInvalidFlags();
+        }
+        return mInvalidFlags;
+    }
+
+    private BitSet resolveInvalidFlags() {
+        BitSet bitSet = new BitSet();
+        if (mCanBeInvalidated) {
+            bitSet.set(getId(), true);
+        }
+        for (Dependency dependency : getDependencies()) {
+            // TODO optional optimization: do not invalidate for conditional flags
+            bitSet.or(dependency.getOther().getInvalidFlags());
+        }
+        return bitSet;
+    }
+
+    public void setBindingExpression(boolean isBindingExpression) {
+        mIsBindingExpression = isBindingExpression;
+    }
+
+    public boolean isBindingExpression() {
+        return mIsBindingExpression;
+    }
+
+    public boolean isObservable() {
+        return getResolvedType().isObservable();
+    }
+
+    public BitSet getShouldReadFlags() {
+        if (mShouldReadFlags == null) {
+            getShouldReadFlagsWithConditionals();
+            mShouldReadFlags = resolveShouldReadFlags();
+        }
+        return mShouldReadFlags;
+    }
+
+    public BitSet getShouldReadFlagsWithConditionals() {
+        if (mShouldReadWithConditionals == null) {
+            mShouldReadWithConditionals = resolveShouldReadWithConditionals();
+        }
+        return mShouldReadWithConditionals;
+    }
+
+    public void setModel(ExprModel model) {
+        mModel = model;
+    }
+
+    private BitSet resolveShouldReadWithConditionals() {
+        // ensure we have invalid flags
+        BitSet bitSet = new BitSet();
+        // if i'm invalid, that DOES NOT mean i should be read :/.
+        if (mIsBindingExpression) {
+            bitSet.or(getInvalidFlags());
+        }
+
+        for (Dependency dependency : getDependants()) {
+            // first traverse non-conditionals because we'll avoid adding conditionals if we are get because of these anyways
+            if (dependency.getCondition() == null) {
+                bitSet.or(dependency.getDependant().getShouldReadFlagsWithConditionals());
+            } else {
+                bitSet.set(dependency.getDependant()
+                        .getRequirementFlagIndex(dependency.getExpectedOutput()));
+            }
+        }
+        return bitSet;
+    }
+
+    private BitSet resolveShouldReadFlags() {
+        // ensure we have invalid flags
+        BitSet bitSet = new BitSet();
+        if (isRead()) {
+            return bitSet;
+        }
+        if (mIsBindingExpression) {
+            bitSet.or(getInvalidFlags());
+        }
+        for (Dependency dependency : getDependants()) {
+            final boolean isElevated = unreadElevatedCheck.apply(dependency);
+            if (dependency.isConditional()) {
+                continue; // TODO
+            }
+            if (isElevated) {
+                // if i already have all flags that will require my dependant's predicate to
+                // be read, that means i'm already read thus can avoid adding its conditional
+                // dependency
+                if (!dependency.getDependant().getAllCalculationPaths().areAllPathsSatisfied(
+                        mReadSoFar)) {
+                    bitSet.set(dependency.getDependant()
+                            .getRequirementFlagIndex(dependency.getExpectedOutput()));
+                }
+            } else {
+                bitSet.or(dependency.getDependant().getShouldReadFlags());
+            }
+        }
+        bitSet.andNot(mReadSoFar);
+        // should read w/ conditionals does eleminate for unnecessary re-reads
+        bitSet.and(mShouldReadWithConditionals);
+        return bitSet;
+    }
+
+    Predicate<Dependency> unreadElevatedCheck = new Predicate<Dependency>() {
+        @Override
+        public boolean apply(Dependency input) {
+            return input.isElevated() && !input.getDependant().isRead();
+        }
+    };
+
+    private void addParents() {
+        for (Expr expr : mChildren) {
+            expr.mParents.add(this);
+        }
+    }
+
+    public void onSwappedWith(Expr existing) {
+        for (Expr child : mChildren) {
+            child.onParentSwapped(this, existing);
+        }
+    }
+
+    private void onParentSwapped(Expr oldParent, Expr newParent) {
+        Preconditions.checkState(mParents.remove(oldParent));
+        mParents.add(newParent);
+    }
+
+    public List<Expr> getChildren() {
+        return mChildren;
+    }
+
+    public List<Expr> getParents() {
+        return mParents;
+    }
+
+    /**
+     * Whether the result of this expression can change or not.
+     *
+     * For example, 3 + 5 can not change vs 3 + x may change.
+     *
+     * Default implementations checks children and returns true if any of them returns true
+     *
+     * @return True if the result of this expression may change due to variables
+     */
+    public boolean isDynamic() {
+        if (mIsDynamic == null) {
+            mIsDynamic = isAnyChildDynamic();
+        }
+        return mIsDynamic;
+    }
+
+    private boolean isAnyChildDynamic() {
+        return Iterables.any(mChildren, new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input.isDynamic();
+            }
+        });
+
+    }
+
+    public ModelClass getResolvedType() {
+        if (mResolvedType == null) {
+            // TODO not get instance
+            mResolvedType = resolveType(ModelAnalyzer.getInstance());
+        }
+        return mResolvedType;
+    }
+
+    abstract protected ModelClass resolveType(ModelAnalyzer modelAnalyzer);
+
+    abstract protected List<Dependency> constructDependencies();
+
+    /**
+     * Creates a dependency for each dynamic child. Should work for any expression besides
+     * conditionals.
+     */
+    protected List<Dependency> constructDynamicChildrenDependencies() {
+        List<Dependency> dependencies = new ArrayList<Dependency>();
+        for (Expr node : mChildren) {
+            if (!node.isDynamic()) {
+                continue;
+            }
+            dependencies.add(new Dependency(this, node));
+        }
+        return dependencies;
+    }
+
+    public final List<Dependency> getDependencies() {
+        if (mDependencies == null) {
+            mDependencies = constructDependencies();
+        }
+        return mDependencies;
+    }
+
+    void addDependant(Dependency dependency) {
+        mDependants.add(dependency);
+    }
+
+    public List<Dependency> getDependants() {
+        return mDependants;
+    }
+
+    protected static final String KEY_JOIN = "~";
+    protected static final Joiner sUniqueKeyJoiner = Joiner.on(KEY_JOIN);
+
+    /**
+     * Returns a unique string key that can identify this expression.
+     *
+     * It must take into account any dependencies
+     *
+     * @return A unique identifier for this expression
+     */
+    public final String getUniqueKey() {
+        if (mUniqueKey == null) {
+            mUniqueKey = computeUniqueKey();
+            Preconditions.checkNotNull(mUniqueKey,
+                    "if there are no children, you must override computeUniqueKey");
+            Preconditions.checkState(!mUniqueKey.trim().equals(""),
+                    "if there are no children, you must override computeUniqueKey");
+        }
+        return mUniqueKey;
+    }
+
+    protected String computeUniqueKey() {
+        return computeChildrenKey();
+    }
+
+    protected final String computeChildrenKey() {
+        return sUniqueKeyJoiner.join(Iterables.transform(mChildren, new Function<Expr, String>() {
+            @Override
+            public String apply(Expr input) {
+                return input.getUniqueKey();
+            }
+        }));
+    }
+
+    public void enableDirectInvalidation() {
+        mCanBeInvalidated = true;
+    }
+
+    public boolean canBeInvalidated() {
+        return mCanBeInvalidated;
+    }
+
+    public void trimShouldReadFlags(BitSet bitSet) {
+        mShouldReadFlags.andNot(bitSet);
+    }
+
+    public boolean isConditional() {
+        return false;
+    }
+
+    public int getRequirementId() {
+        return mRequirementId;
+    }
+
+    public void setRequirementId(int requirementId) {
+        mRequirementId = requirementId;
+    }
+
+    /**
+     * This is called w/ a dependency of mine.
+     * Base method should thr
+     */
+    public int getRequirementFlagIndex(boolean expectedOutput) {
+        Preconditions.checkState(mRequirementId != NO_ID, "If this is an expression w/ conditional"
+                + " dependencies, it must be assigned a requirement ID");
+        return expectedOutput ? mRequirementId + 1 : mRequirementId;
+    }
+
+    public boolean hasId() {
+        return mId != NO_ID;
+    }
+
+    public void markFlagsAsRead(BitSet flags) {
+        mReadSoFar.or(flags);
+    }
+
+    public boolean isRead() {
+        return mRead;
+    }
+
+    public boolean considerElevatingConditionals(Expr justRead) {
+        boolean elevated = false;
+        for (Dependency dependency : mDependencies) {
+            if (dependency.isConditional() && dependency.getCondition() == justRead) {
+                dependency.elevate();
+                elevated = true;
+            }
+        }
+        return elevated;
+    }
+
+    public void invalidateReadFlags() {
+        mShouldReadFlags = null;
+    }
+
+    public boolean hasNestedCannotRead() {
+        if (isRead()) {
+            return false;
+        }
+        if (getShouldReadFlags().isEmpty()) {
+            return true;
+        }
+        return Iterables.any(getDependencies(), hasNestedCannotRead);
+    }
+
+    Predicate<Dependency> hasNestedCannotRead = new Predicate<Dependency>() {
+        @Override
+        public boolean apply(Dependency input) {
+            return input.isConditional() || input.getOther().hasNestedCannotRead();
+        }
+    };
+
+    public boolean markAsReadIfDone() {
+        if (mRead) {
+            return false;
+        }
+        // TODO avoid clone, we can calculate this iteratively
+        BitSet clone = (BitSet) mShouldReadWithConditionals.clone();
+
+        clone.andNot(mReadSoFar);
+        mRead = clone.isEmpty();
+        if (!mRead && !mReadSoFar.isEmpty()) {
+            // check if remaining dependencies can be satisfied w/ existing values
+            // for predicate flags, this expr may already be calculated to get the predicate
+            // to detect them, traverse them later on, see which flags should be calculated to calculate
+            // them. If any of them is completely covered w/ our non-conditional flags, no reason
+            // to add them to the list since we'll already be calculated due to our non-conditional
+            // flags
+
+            for (int i = clone.nextSetBit(0); i != -1; i = clone.nextSetBit(i + 1)) {
+                final Expr expr = mModel.findFlagExpression(i);
+                if (!expr.isConditional()) {
+                    continue;
+                }
+                final BitSet readForConditional = expr.findConditionalFlags();
+                // to calculate that conditional, i should've read /readForConditional/ flags
+                // if my read-so-far bits has any common w/ that; that means i would've already
+                // read myself
+                clone.andNot(readForConditional);
+                final BitSet invalidFlags = (BitSet) getInvalidFlags().clone();
+                invalidFlags.andNot(readForConditional);
+                mRead = invalidFlags.isEmpty() || clone.isEmpty();
+            }
+
+        }
+        if (mRead) {
+            mShouldReadFlags = null; // if we've been marked as read, clear should read flags
+        }
+        return mRead;
+    }
+
+    BitSet mConditionalFlags;
+
+    private BitSet findConditionalFlags() {
+        Preconditions.checkState(isConditional(), "should not call this on a non-conditional expr");
+        if (mConditionalFlags == null) {
+            mConditionalFlags = new BitSet();
+            resolveConditionalFlags(mConditionalFlags);
+        }
+        return mConditionalFlags;
+    }
+
+    private void resolveConditionalFlags(BitSet flags) {
+        flags.or(getPredicateInvalidFlags());
+        // if i have only 1 dependency which is conditional, traverse it as well
+        if (getDependants().size() == 1) {
+            final Dependency dependency = getDependants().get(0);
+            if (dependency.getCondition() != null) {
+                flags.or(dependency.getDependant().findConditionalFlags());
+                flags.set(dependency.getDependant()
+                        .getRequirementFlagIndex(dependency.getExpectedOutput()));
+            }
+        }
+    }
+
+
+    @Override
+    public String toString() {
+        return getUniqueKey();
+    }
+
+    public BitSet getReadSoFar() {
+        return mReadSoFar;
+    }
+
+    private Node mCalculationPaths = null;
+
+    protected Node getAllCalculationPaths() {
+        if (mCalculationPaths == null) {
+            Node node = new Node();
+            // TODO distant parent w/ conditionals are still not traversed :/
+            if (isConditional()) {
+                node.mBitSet.or(getPredicateInvalidFlags());
+            } else {
+                node.mBitSet.or(getInvalidFlags());
+            }
+            for (Dependency dependency : getDependants()) {
+                final Expr dependant = dependency.getDependant();
+                if (dependency.getCondition() != null) {
+                    Node cond = new Node();
+                    cond.setConditionFlag(
+                            dependant.getRequirementFlagIndex(dependency.getExpectedOutput()));
+                    cond.mParents.add(dependant.getAllCalculationPaths());
+                } else {
+                    node.mParents.add(dependant.getAllCalculationPaths());
+                }
+            }
+            mCalculationPaths = node;
+        }
+        return mCalculationPaths;
+    }
+
+    public String getDefaultValue() {
+        return ModelAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
+    }
+
+    protected BitSet getPredicateInvalidFlags() {
+        throw new IllegalStateException(
+                "must override getPredicateInvalidFlags in " + getClass().getSimpleName());
+    }
+
+    /**
+     * Used by code generation
+     */
+    public boolean shouldReadNow(final Iterable<Expr> justRead) {
+        return !getShouldReadFlags().isEmpty() &&
+                !Iterables.any(getDependencies(), new Predicate<Dependency>() {
+                    @Override
+                    public boolean apply(Dependency input) {
+                        return !(input.getOther().isRead() || (justRead != null && Iterables
+                                .contains(justRead, input.getOther())));
+                    }
+                });
+    }
+
+    public boolean isEqualityCheck() {
+        return false;
+    }
+
+    public void setIsUsed(boolean isUsed) {
+        mIsUsed = isUsed;
+    }
+
+    public boolean isUsed() {
+        return mIsUsed;
+    }
+
+    public void updateExpr(ModelAnalyzer modelAnalyzer) {
+        for (Expr child : mChildren) {
+            child.updateExpr(modelAnalyzer);
+        }
+    }
+
+    protected String asPackage() {
+        return null;
+    }
+
+    static class Node {
+
+        BitSet mBitSet = new BitSet();
+        List<Node> mParents = new ArrayList<Node>();
+        int mConditionFlag = -1;
+
+        public boolean areAllPathsSatisfied(BitSet readSoFar) {
+            if (mConditionFlag != -1) {
+                return readSoFar.get(mConditionFlag) || mParents.get(0)
+                        .areAllPathsSatisfied(readSoFar);
+            } else {
+                final BitSet clone = (BitSet) readSoFar.clone();
+                readSoFar.and(mBitSet);
+                if (!readSoFar.isEmpty()) {
+                    return true;
+                }
+                if (mParents.isEmpty()) {
+                    return false;
+                }
+                for (Node parent : mParents) {
+                    if (!parent.areAllPathsSatisfied(readSoFar)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+
+        public void setConditionFlag(int requirementFlagIndex) {
+            mConditionFlag = requirementFlagIndex;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
new file mode 100644
index 0000000..0f8a935
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
@@ -0,0 +1,495 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.util.L;
+import android.databinding.tool.writer.FlagSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExprModel {
+
+    Map<String, Expr> mExprMap = new HashMap<String, Expr>();
+
+    List<Expr> mBindingExpressions = new ArrayList<Expr>();
+
+    private int mInvalidateableFieldLimit = 0;
+
+    private int mRequirementIdCount = 0;
+
+    private static final String TRUE_KEY_SUFFIX = "== true";
+    private static final String FALSE_KEY_SUFFIX = "== false";
+
+    /**
+     * Used by code generation. Keeps the list of expressions that are waiting to be evaluated.
+     */
+    private List<Expr> mPendingExpressions;
+
+    /**
+     * Used for converting flags into identifiers while debugging.
+     */
+    private String[] mFlagMapping;
+
+    private BitSet mInvalidateableFlags;
+    private BitSet mConditionalFlags;
+
+    private int mFlagBucketCount;// how many buckets we use to identify flags
+
+    private List<Expr> mObservables;
+
+    private Map<String, String> mImports = new HashMap<String, String>();
+
+    /**
+     * Adds the expression to the list of expressions and returns it.
+     * If it already exists, returns existing one.
+     *
+     * @param expr The new parsed expression
+     * @return The expression itself or another one if the same thing was parsed before
+     */
+    public <T extends Expr> T register(T expr) {
+        T existing = (T) mExprMap.get(expr.getUniqueKey());
+        if (existing != null) {
+            Preconditions.checkState(expr.getParents().isEmpty(),
+                    "If an expression already exists, it should've never been added to a parent,"
+                            + "if thats the case, somewhere we are creating an expression w/o"
+                            + "calling expression model");
+            // tell the expr that it is being swapped so that if it was added to some other expr
+            // as a parent, those can swap their references
+            expr.onSwappedWith(existing);
+            return existing;
+        }
+        mExprMap.put(expr.getUniqueKey(), expr);
+        expr.setModel(this);
+        return expr;
+    }
+
+    public void unregister(Expr expr) {
+        mExprMap.remove(expr.getUniqueKey());
+    }
+
+    public Map<String, Expr> getExprMap() {
+        return mExprMap;
+    }
+
+    public int size() {
+        return mExprMap.size();
+    }
+
+    public ComparisonExpr comparison(String op, Expr left, Expr right) {
+        return register(new ComparisonExpr(op, left, right));
+    }
+
+    public FieldAccessExpr field(Expr parent, String name) {
+        return register(new FieldAccessExpr(parent, name));
+    }
+
+    public FieldAccessExpr observableField(Expr parent, String name) {
+        return register(new FieldAccessExpr(parent, name, true));
+    }
+
+    public SymbolExpr symbol(String text, Class type) {
+        return register(new SymbolExpr(text, type));
+    }
+
+    public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) {
+        return register(new TernaryExpr(pred, ifTrue, ifFalse));
+    }
+
+    public IdentifierExpr identifier(String name) {
+        return register(new IdentifierExpr(name));
+    }
+
+    public StaticIdentifierExpr staticIdentifier(String name) {
+        return register(new StaticIdentifierExpr(name));
+    }
+
+    public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) {
+        return register(new MethodCallExpr(target, name, args));
+    }
+
+    public MathExpr math(Expr left, String op, Expr right) {
+        return register(new MathExpr(left, op, right));
+    }
+
+    public Expr group(Expr grouped) {
+        return register(new GroupExpr(grouped));
+    }
+
+    public Expr resourceExpr(String packageName, String resourceType, String resourceName,
+            List<Expr> args) {
+        return register(new ResourceExpr(packageName, resourceType, resourceName, args));
+    }
+
+    public Expr bracketExpr(Expr variableExpr, Expr argExpr) {
+        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;
+    }
+
+    public void addImport(String alias, String type) {
+        Preconditions.checkState(!mImports.containsKey(alias),
+                "%s has already been defined as %s", alias, type);
+        final StaticIdentifierExpr id = staticIdentifier(alias);
+        L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName());
+        id.setUserDefinedType(type);
+        mImports.put(alias, type);
+    }
+
+    public Map<String, String> getImports() {
+        return mImports;
+    }
+
+    /**
+     * The actual thingy that is set on the binding target.
+     *
+     * Input must be already registered
+     */
+    public Expr bindingExpr(Expr bindingExpr) {
+        Preconditions.checkArgument(mExprMap.containsKey(bindingExpr.getUniqueKey()),
+                "Main expression should already be registered");
+        if (!mBindingExpressions.contains(bindingExpr)) {
+            mBindingExpressions.add(bindingExpr);
+        }
+        return bindingExpr;
+    }
+
+    /**
+     * Nodes to which no one depends
+     */
+    public Iterable<Expr> findRootNodes() {
+        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input.getParents().isEmpty();
+            }
+        });
+    }
+
+    /**
+     * Nodes, which do not depend on any other node
+     */
+    public Iterable<Expr> findLeafNodes() {
+        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input.getChildren().isEmpty();
+            }
+        });
+    }
+
+    public List<Expr> getObservables() {
+        return mObservables;
+    }
+
+    /**
+     * Give id to each expression. Will be useful if we serialize.
+     */
+    public void seal() {
+        List<Expr> notifiableExpressions = new ArrayList<Expr>();
+        //ensure class analyzer. We need to know observables at this point
+        final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+
+        ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions);
+        for (Expr expr: exprs) {
+            expr.updateExpr(modelAnalyzer);
+        }
+
+        int counter = 0;
+        final Iterable<Expr> observables = filterObservables(modelAnalyzer);
+        List<String> flagMapping = Lists.newArrayList();
+        mObservables = Lists.newArrayList();
+        for (Expr expr : observables) {
+            // observables gets initial ids
+            flagMapping.add(expr.getUniqueKey());
+            expr.setId(counter++);
+            mObservables.add(expr);
+            notifiableExpressions.add(expr);
+            L.d("observable %s", expr.getUniqueKey());
+        }
+
+        // non-observable identifiers gets next ids
+        final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer);
+        for (Expr expr : nonObservableIds) {
+            flagMapping.add(expr.getUniqueKey());
+            expr.setId(counter++);
+            notifiableExpressions.add(expr);
+            L.d("non-observable %s", expr.getUniqueKey());
+        }
+
+        // descendents of observables gets following ids
+        for (Expr expr : observables) {
+            for (Expr parent : expr.getParents()) {
+                if (parent.hasId()) {
+                    continue;// already has some id, means observable
+                }
+                // only fields earn an id
+                if (parent instanceof FieldAccessExpr) {
+                    FieldAccessExpr fae = (FieldAccessExpr) parent;
+                    L.d("checking field access expr %s. getter: %s", fae,fae.getGetter());
+                    if (fae.isDynamic() && fae.getGetter().canBeInvalidated) {
+                        flagMapping.add(parent.getUniqueKey());
+                        parent.setId(counter++);
+                        notifiableExpressions.add(parent);
+                        L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(),
+                                Integer.toHexString(System.identityHashCode(parent)),
+                                expr.getUniqueKey(),
+                                Integer.toHexString(System.identityHashCode(expr)));
+                    }
+                }
+            }
+        }
+
+        // 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());
+            }
+            Preconditions.checkState(expr.isDynamic() || !expr.hasId());
+            if (!expr.isDynamic()) {
+                // give it an id for invalidateAll
+                expr.setId(counter ++);
+                notifiableExpressions.add(expr);
+            }
+        }
+
+        for (Expr expr : notifiableExpressions) {
+            expr.enableDirectInvalidation();
+        }
+
+        // make sure all dependencies are resolved to avoid future race conditions
+        for (Expr expr : mExprMap.values()) {
+            expr.getDependencies();
+        }
+
+        mInvalidateableFieldLimit = counter;
+        mInvalidateableFlags = new BitSet();
+        for (int i = 0; i < mInvalidateableFieldLimit; i++) {
+            mInvalidateableFlags.set(i, true);
+        }
+
+        // make sure all dependencies are resolved to avoid future race conditions
+        for (Expr expr : mExprMap.values()) {
+            if (expr.isConditional()) {
+                expr.setRequirementId(counter);
+                flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX);
+                flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX);
+                counter += 2;
+            }
+        }
+        mConditionalFlags = new BitSet();
+        for (int i = mInvalidateableFieldLimit; i < counter; i++) {
+            mConditionalFlags.set(i, true);
+        }
+
+        mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2;
+
+        // everybody gets an id
+        for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) {
+            final Expr value = entry.getValue();
+            if (!value.hasId()) {
+                value.setId(counter++);
+            }
+        }
+        mFlagMapping = new String[flagMapping.size()];
+        flagMapping.toArray(mFlagMapping);
+
+        for (Expr expr : mExprMap.values()) {
+            expr.getShouldReadFlagsWithConditionals();
+        }
+
+        for (Expr expr : mExprMap.values()) {
+            // ensure all types are calculated
+            expr.getResolvedType();
+        }
+
+        mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize);
+    }
+
+    public int getFlagBucketCount() {
+        return mFlagBucketCount;
+    }
+
+    public int getTotalFlagCount() {
+        return mRequirementIdCount * 2 + mInvalidateableFieldLimit;
+    }
+
+    public int getInvalidateableFieldLimit() {
+        return mInvalidateableFieldLimit;
+    }
+
+    public String[] getFlagMapping() {
+        return mFlagMapping;
+    }
+
+    public String getFlag(int id) {
+        return mFlagMapping[id];
+    }
+
+    private Iterable<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) {
+        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input instanceof IdentifierExpr
+                        && !input.hasId()
+                        && !input.isObservable()
+                        && input.isDynamic();
+            }
+        });
+    }
+
+    private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) {
+        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input.isObservable();
+            }
+        });
+    }
+
+    public List<Expr> getPendingExpressions() {
+        if (mPendingExpressions == null) {
+            mPendingExpressions = Lists.newArrayList();
+            for (Expr expr : mExprMap.values()) {
+                if (!expr.isRead() && expr.isDynamic()) {
+                    mPendingExpressions.add(expr);
+                }
+            }
+        }
+        return mPendingExpressions;
+    }
+
+    public boolean markBitsRead() {
+        L.d("marking bits as done");
+        // each has should read flags, we set them back on them
+        for (Expr expr : filterShouldRead(getPendingExpressions())) {
+            expr.markFlagsAsRead(expr.getShouldReadFlags());
+        }
+        return pruneDone();
+    }
+
+    private boolean pruneDone() {
+        boolean marked = true;
+        List<Expr> markedAsReadList = Lists.newArrayList();
+        while (marked) {
+            marked = false;
+            for (Expr expr : mExprMap.values()) {
+                if (expr.isRead()) {
+                    continue;
+                }
+                if (expr.markAsReadIfDone()) {
+                    L.d("marked %s as read ", expr.getUniqueKey());
+                    marked = true;
+                    markedAsReadList.add(expr);
+                }
+
+            }
+        }
+        boolean elevated = false;
+        for (Expr markedAsRead : markedAsReadList) {
+            for (Dependency dependency : markedAsRead.getDependants()) {
+                if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) {
+                    elevated = true;
+                }
+            }
+        }
+        if (elevated) {
+            // some conditionals are elevated. We should re-calculate flags
+            for (Expr expr : getPendingExpressions()) {
+                if (!expr.isRead()) {
+                    expr.invalidateReadFlags();
+                }
+            }
+            mPendingExpressions = null;
+        }
+        return elevated;
+    }
+
+    public static Iterable<Expr> filterShouldRead(Iterable<Expr> exprs) {
+        return toCollection(Iterables.filter(exprs, sShouldReadPred));
+    }
+
+    public static List<Expr> toCollection(Iterable<Expr> iterable) {
+        return Arrays.asList(Iterables.toArray(iterable, Expr.class));
+    }
+
+    private static final Predicate<Expr> sShouldReadPred = new Predicate<Expr>() {
+        @Override
+        public boolean apply(final Expr expr) {
+            return !expr.getShouldReadFlags().isEmpty() && !Iterables.any(
+                    expr.getDependencies(), new Predicate<Dependency>() {
+                        @Override
+                        public boolean apply(Dependency dependency) {
+                            final boolean result = dependency.isConditional() ||
+                                    dependency.getOther().hasNestedCannotRead();
+                            return result;
+                        }
+                    });
+        }
+    };
+
+    private static final  Predicate<Expr> sReadNowPred = new Predicate<Expr>() {
+        @Override
+        public boolean apply(Expr input) {
+            return !input.getShouldReadFlags().isEmpty() &&
+                    !Iterables.any(input.getDependencies(), new Predicate<Dependency>() {
+                        @Override
+                        public boolean apply(Dependency input) {
+                            return !input.getOther().isRead();
+                        }
+                    });
+        }
+    };
+
+    public Expr findFlagExpression(int flag) {
+        final String key = mFlagMapping[flag];
+        if (mExprMap.containsKey(key)) {
+            return mExprMap.get(key);
+        }
+        int falseIndex = key.indexOf(FALSE_KEY_SUFFIX);
+        if (falseIndex > -1) {
+            final String trimmed = key.substring(0, falseIndex);
+            return mExprMap.get(trimmed);
+        }
+        int trueIndex = key.indexOf(TRUE_KEY_SUFFIX);
+        if (trueIndex > -1) {
+            final String trimmed = key.substring(0, trueIndex);
+            return mExprMap.get(trimmed);
+        }
+        Preconditions.checkArgument(false, "cannot find expression for flag %d", flag);
+        return null;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
new file mode 100644
index 0000000..754cca8
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.Callable;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.util.L;
+
+import java.util.List;
+
+public class FieldAccessExpr extends Expr {
+    String mName;
+    Callable mGetter;
+    final boolean mIsObservableField;
+
+    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() {
+        return getChildren().get(0);
+    }
+
+    public Callable getGetter() {
+        if (mGetter == null) {
+            getResolvedType();
+        }
+        return mGetter;
+    }
+
+    @Override
+    public boolean isDynamic() {
+        if (!getChild().isDynamic()) {
+            return false;
+        }
+        if (mGetter == null) {
+            getResolvedType();
+        }
+        // maybe this is just a final field in which case cannot be notified as changed
+        return mGetter.type != Callable.Type.FIELD || mGetter.isDynamic;
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
+        for (Dependency dependency : dependencies) {
+            if (dependency.getOther() == getChild()) {
+                dependency.setMandatory(true);
+            }
+        }
+        return dependencies;
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        if (mIsObservableField) {
+            return sUniqueKeyJoiner.join(mName, "..", super.computeUniqueKey());
+        }
+        return sUniqueKeyJoiner.join(mName, ".", super.computeUniqueKey());
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    @Override
+    public void updateExpr(ModelAnalyzer modelAnalyzer) {
+        resolveType(modelAnalyzer);
+        super.updateExpr(modelAnalyzer);
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        if (mGetter == null) {
+            Expr child = getChild();
+            child.resolveType(modelAnalyzer);
+            boolean isStatic = child instanceof StaticIdentifierExpr;
+            ModelClass resolvedType = child.getResolvedType();
+            L.d("resolving %s. Resolved type: %s", this, resolvedType);
+
+            mGetter = resolvedType.findGetterOrField(mName, isStatic);
+            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;
+
+                getChildren().add(observableField);
+                observableField.getParents().add(this);
+                mGetter = mGetter.resolvedType.findGetterOrField("get", false);
+                mName = "";
+            }
+        }
+        return mGetter.resolvedType;
+    }
+
+    @Override
+    protected String asPackage() {
+        String parentPackage = getChild().asPackage();
+        return parentPackage == null ? null : parentPackage + "." + mName;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java
new file mode 100644
index 0000000..8af1d68
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.List;
+
+public class GroupExpr extends Expr {
+    public GroupExpr(Expr wrapped) {
+        super(wrapped);
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        return getWrapped().resolveType(modelAnalyzer);
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return getWrapped().constructDependencies();
+    }
+
+    public Expr getWrapped() {
+        return getChildren().get(0);
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
new file mode 100644
index 0000000..5207485
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
@@ -0,0 +1,87 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.util.L;
+
+import java.util.List;
+
+public class IdentifierExpr extends Expr {
+    String mName;
+    String mUserDefinedType;
+    IdentifierExpr(String name) {
+        mName = name;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * If this is root, its type should be set while parsing the XML document
+     * @param userDefinedType The type of this identifier
+     */
+    public void setUserDefinedType(String userDefinedType) {
+        mUserDefinedType = userDefinedType;
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(mName, super.computeUniqueKey());
+    }
+
+    public String getUserDefinedType() {
+        return mUserDefinedType;
+    }
+
+    public String getExpandedUserDefinedType(ModelAnalyzer modelAnalyzer) {
+        Preconditions.checkNotNull(mUserDefinedType,
+                "Identifiers must have user defined types from the XML file. %s is missing it",
+                mName);
+        final String expanded = modelAnalyzer
+                .applyImports(mUserDefinedType, getModel().getImports());
+        L.d("expanded version of %s is %s", mUserDefinedType, expanded);
+        return expanded;
+    }
+
+    @Override
+    public boolean isDynamic() {
+        return true;
+    }
+
+    @Override
+    protected ModelClass resolveType(final ModelAnalyzer modelAnalyzer) {
+        Preconditions.checkNotNull(mUserDefinedType,
+                "Identifiers must have user defined types from the XML file. %s is missing it", mName);
+        return modelAnalyzer.findClass(mUserDefinedType, getModel().getImports());
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return Lists.newArrayList();
+    }
+
+    @Override
+    protected String asPackage() {
+        return mUserDefinedType == null ? mName : null;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
new file mode 100644
index 0000000..fbd0b3d
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.List;
+
+public class MathExpr extends Expr {
+    final String mOp;
+    MathExpr(Expr left, String op, Expr right) {
+        super(left, right);
+        mOp = op;
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey());
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        if ("+".equals(mOp)) {
+            // TODO we need upper casting etc.
+            if (getLeft().getResolvedType().isString()
+                    || getRight().getResolvedType().isString()) {
+                return modelAnalyzer.findClass(String.class);
+            }
+        }
+        return modelAnalyzer.findCommonParentOf(getLeft().getResolvedType(),
+                getRight().getResolvedType());
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return constructDynamicChildrenDependencies();
+    }
+
+    public String getOp() {
+        return mOp;
+    }
+
+    public Expr getLeft() {
+        return getChildren().get(0);
+    }
+
+    public Expr getRight() {
+        return getChildren().get(1);
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
new file mode 100644
index 0000000..491adc8
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
@@ -0,0 +1,105 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.collect.Iterables;
+
+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.ModelMethod;
+import android.databinding.tool.util.L;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MethodCallExpr extends Expr {
+    final String mName;
+
+    Callable mGetter;
+
+    MethodCallExpr(Expr target, String name, List<Expr> args) {
+        super(Iterables.concat(Arrays.asList(target), args));
+        mName = name;
+    }
+
+    @Override
+    public void updateExpr(ModelAnalyzer modelAnalyzer) {
+        resolveType(modelAnalyzer);
+        super.updateExpr(modelAnalyzer);
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        if (mGetter == null) {
+            List<ModelClass> args = new ArrayList<ModelClass>();
+            for (Expr expr : getArgs()) {
+                args.add(expr.getResolvedType());
+            }
+
+            Expr target = getTarget();
+            boolean isStatic = target instanceof StaticIdentifierExpr;
+            ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic);
+            if (method == null) {
+                String message = "cannot find method '" + mName + "' in class " +
+                        target.getResolvedType().toJavaCode();
+                IllegalArgumentException e = new IllegalArgumentException(message);
+                L.e(e, "cannot find method %s in class %s", mName,
+                        target.getResolvedType().toJavaCode());
+                throw e;
+            }
+            mGetter = new Callable(Type.METHOD, method.getName(), method.getReturnType(args), true,
+                    false);
+        }
+        return mGetter.resolvedType;
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
+        for (Dependency dependency : dependencies) {
+            if (dependency.getOther() == getTarget()) {
+                dependency.setMandatory(true);
+            }
+        }
+        return dependencies;
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return sUniqueKeyJoiner.join(getTarget().computeUniqueKey(), mName,
+                super.computeUniqueKey());
+    }
+
+    public Expr getTarget() {
+        return getChildren().get(0);
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public List<Expr> getArgs() {
+        return getChildren().subList(1, getChildren().size());
+    }
+
+    public Callable getGetter() {
+        return mGetter;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
new file mode 100644
index 0000000..66391034
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
@@ -0,0 +1,205 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.collect.ImmutableMap;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.WriterPackage;
+
+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();
+
+    // lazily initialized
+    private Map<String, ModelClass> mResourceToTypeMapping;
+
+    protected final String mPackage;
+
+    protected final String mResourceType;
+
+    protected final String mResourceId;
+
+    public ResourceExpr(String packageName, String resourceType, String resourceName,
+            List<Expr> args) {
+        super(args);
+        if ("android".equals(packageName)) {
+            mPackage = "android.";
+        } else {
+            mPackage = "";
+        }
+        mResourceType = resourceType;
+        mResourceId = resourceName;
+    }
+
+    private Map<String, ModelClass> getResourceToTypeMapping(ModelAnalyzer modelAnalyzer) {
+        if (mResourceToTypeMapping == null) {
+            final Map<String, String> imports = getModel().getImports();
+            mResourceToTypeMapping = ImmutableMap.<String, ModelClass>builder()
+                    .put("anim", modelAnalyzer.findClass("android.view.animation.Animation",
+                            imports))
+                    .put("animator", modelAnalyzer.findClass("android.animation.Animator",
+                            imports))
+                    .put("colorStateList",
+                            modelAnalyzer.findClass("android.content.res.ColorStateList",
+                                    imports))
+                    .put("drawable", modelAnalyzer.findClass("android.graphics.drawable.Drawable",
+                            imports))
+                    .put("stateListAnimator",
+                            modelAnalyzer.findClass("android.animation.StateListAnimator",
+                                    imports))
+                    .put("transition", modelAnalyzer.findClass("android.transition.Transition",
+                            imports))
+                    .put("typedArray", modelAnalyzer.findClass("android.content.res.TypedArray",
+                            imports))
+                    .put("interpolator",
+                            modelAnalyzer.findClass("android.view.animation.Interpolator", imports))
+                    .put("bool", modelAnalyzer.findClass(boolean.class))
+                    .put("color", modelAnalyzer.findClass(int.class))
+                    .put("dimenOffset", modelAnalyzer.findClass(int.class))
+                    .put("dimenSize", modelAnalyzer.findClass(int.class))
+                    .put("id", modelAnalyzer.findClass(int.class))
+                    .put("integer", modelAnalyzer.findClass(int.class))
+                    .put("layout", modelAnalyzer.findClass(int.class))
+                    .put("dimen", modelAnalyzer.findClass(float.class))
+                    .put("fraction", modelAnalyzer.findClass(float.class))
+                    .put("intArray", modelAnalyzer.findClass(int[].class))
+                    .put("string", modelAnalyzer.findClass(String.class))
+                    .put("stringArray", modelAnalyzer.findClass(String[].class))
+                    .build();
+        }
+        return mResourceToTypeMapping;
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        final Map<String, ModelClass> mapping = getResourceToTypeMapping(
+                modelAnalyzer);
+        final ModelClass modelClass = mapping.get(mResourceType);
+        if (modelClass != null) {
+            return modelClass;
+        }
+        if ("plurals".equals(mResourceType)) {
+            if (getChildren().isEmpty()) {
+                return modelAnalyzer.findClass(int.class);
+            } else {
+                return modelAnalyzer.findClass(String.class);
+            }
+        }
+        return modelAnalyzer.findClass(mResourceType, getModel().getImports());
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return constructDynamicChildrenDependencies();
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        String base;
+        if (mPackage == null) {
+            base = "@" + mResourceType + "/" + mResourceId;
+        } else {
+            base = "@" + "android:" + mResourceType + "/" + mResourceId;
+        }
+        return sUniqueKeyJoiner.join(base, computeChildrenKey());
+    }
+
+    public String getResourceId() {
+        return mPackage + "R." + getResourceObject() + "." + mResourceId;
+    }
+
+    public String toJava() {
+        final String context = "getRoot().getContext()";
+        final String resources = "getRoot().getResources()";
+        final String resourceName = mPackage + "R." + getResourceObject() + "." + mResourceId;
+        if ("anim".equals(mResourceType)) return "android.view.animation.AnimationUtils.loadAnimation(" + context + ", " + resourceName + ")";
+        if ("animator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadAnimator(" + context + ", " + resourceName + ")";
+        if ("bool".equals(mResourceType)) return resources + ".getBoolean(" + resourceName + ")";
+        if ("color".equals(mResourceType)) return resources + ".getColor(" + resourceName + ")";
+        if ("colorStateList".equals(mResourceType)) return resources + ".getColorStateList(" + resourceName + ")";
+        if ("dimen".equals(mResourceType)) return resources + ".getDimension(" + resourceName + ")";
+        if ("dimenOffset".equals(mResourceType)) return resources + ".getDimensionPixelOffset(" + resourceName + ")";
+        if ("dimenSize".equals(mResourceType)) return resources + ".getDimensionPixelSize(" + resourceName + ")";
+        if ("drawable".equals(mResourceType)) return resources + ".getDrawable(" + resourceName + ")";
+        if ("fraction".equals(mResourceType)) {
+            String base = getChildCode(0, "1");
+            String pbase = getChildCode(1, "1");
+            return resources + ".getFraction(" + resourceName + ", " + base + ", " + pbase +
+                    ")";
+        }
+        if ("id".equals(mResourceType)) return resourceName;
+        if ("intArray".equals(mResourceType)) return resources + ".getIntArray(" + resourceName + ")";
+        if ("integer".equals(mResourceType)) return resources + ".getInteger(" + resourceName + ")";
+        if ("interpolator".equals(mResourceType))  return "android.view.animation.AnimationUtils.loadInterpolator(" + context + ", " + resourceName + ")";
+        if ("layout".equals(mResourceType)) return resourceName;
+        if ("plurals".equals(mResourceType)) {
+            if (getChildren().isEmpty()) {
+                return resourceName;
+            } else {
+                return makeParameterCall(resourceName, "getQuantityString");
+            }
+        }
+        if ("stateListAnimator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadStateListAnimator(" + context + ", " + resourceName + ")";
+        if ("string".equals(mResourceType)) return makeParameterCall(resourceName, "getString");
+        if ("stringArray".equals(mResourceType)) return resources + ".getStringArray(" + resourceName + ")";
+        if ("transition".equals(mResourceType)) return "android.transition.TransitionInflater.from(" + context + ").inflateTransition(" + resourceName + ")";
+        if ("typedArray".equals(mResourceType)) return resources + ".obtainTypedArray(" + resourceName + ")";
+        final String property = Character.toUpperCase(mResourceType.charAt(0)) +
+                mResourceType.substring(1);
+        return resources + ".get" + property + "(" + resourceName + ")";
+
+    }
+
+    private String getChildCode(int childIndex, String defaultValue) {
+        if (getChildren().size() <= childIndex) {
+            return defaultValue;
+        } else {
+            return WriterPackage.toCode(getChildren().get(childIndex), false).generate();
+        }
+    }
+
+    private String makeParameterCall(String resourceName, String methodCall) {
+        StringBuilder sb = new StringBuilder("getRoot().getResources().");
+        sb.append(methodCall).append("(").append(resourceName);
+        for (Expr expr : getChildren()) {
+            sb.append(", ").append(WriterPackage.toCode(expr, false).generate());
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    private String getResourceObject() {
+        String rFileObject = RESOURCE_TYPE_TO_R_OBJECT.get(mResourceType);
+        if (rFileObject == null) {
+            rFileObject = mResourceType;
+        }
+        return rFileObject;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
new file mode 100644
index 0000000..8ca5128
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
@@ -0,0 +1,34 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+public class StaticIdentifierExpr extends IdentifierExpr {
+
+    StaticIdentifierExpr(String name) {
+        super(name);
+    }
+
+    @Override
+    public boolean isObservable() {
+        return false;
+    }
+
+    @Override
+    public boolean isDynamic() {
+        return false;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
new file mode 100644
index 0000000..9e03d2f
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.collect.Lists;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.List;
+
+public class SymbolExpr extends Expr {
+    String mText;
+    Class mType;
+
+    SymbolExpr(String text, Class type) {
+        super();
+        mText = text;
+        mType = type;
+    }
+
+    public String getText() {
+        return mText;
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        return modelAnalyzer.findClass(mType);
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return mText;
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        return Lists.newArrayList();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
new file mode 100644
index 0000000..02e5cd7
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
@@ -0,0 +1,84 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.collect.Lists;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+
+import java.util.BitSet;
+import java.util.List;
+
+public class TernaryExpr extends Expr {
+    TernaryExpr(Expr pred, Expr ifTrue, Expr ifFalse) {
+        super(pred, ifTrue, ifFalse);
+    }
+
+    public Expr getPred() {
+        return getChildren().get(0);
+    }
+
+    public Expr getIfTrue() {
+        return getChildren().get(1);
+    }
+
+    public Expr getIfFalse() {
+        return getChildren().get(2);
+    }
+
+    @Override
+    protected String computeUniqueKey() {
+        return "?:" + super.computeUniqueKey();
+    }
+
+    @Override
+    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+        return modelAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(),
+                getIfFalse().getResolvedType());
+    }
+
+    @Override
+    protected List<Dependency> constructDependencies() {
+        List<Dependency> deps = Lists.newArrayList();
+        Expr predExpr = getPred();
+        if (predExpr.isDynamic()) {
+            final Dependency pred = new Dependency(this, predExpr);
+            pred.setMandatory(true);
+            deps.add(pred);
+        }
+        Expr ifTrueExpr = getIfTrue();
+        if (ifTrueExpr.isDynamic()) {
+            deps.add(new Dependency(this, ifTrueExpr, predExpr, true));
+        }
+        Expr ifFalseExpr = getIfFalse();
+        if (ifFalseExpr.isDynamic()) {
+            deps.add(new Dependency(this, ifFalseExpr, predExpr, false));
+        }
+        return deps;
+    }
+
+    @Override
+    protected BitSet getPredicateInvalidFlags() {
+        return getPred().getInvalidFlags();
+    }
+
+    @Override
+    public boolean isConditional() {
+        return true;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java
new file mode 100644
index 0000000..8883124
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java
@@ -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 android.databinding.tool.reflection;
+
+public class Callable {
+
+    public static enum Type {
+        METHOD,
+        FIELD
+    }
+
+    public final Type type;
+
+    public final String name;
+
+    public final ModelClass resolvedType;
+
+    public final boolean isDynamic;
+
+    public final boolean canBeInvalidated;
+
+    public Callable(Type type, String name, ModelClass resolvedType, boolean isDynamic,
+            boolean canBeInvalidated) {
+        this.type = type;
+        this.name = name;
+        this.resolvedType = resolvedType;
+        this.isDynamic = isDynamic;
+        this.canBeInvalidated = canBeInvalidated;
+    }
+
+    public String getTypeCodeName() {
+        return resolvedType.toJavaCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Callable{" +
+                "type=" + type +
+                ", name='" + name + '\'' +
+                ", resolvedType=" + resolvedType +
+                ", isDynamic=" + isDynamic +
+                ", canBeInvalidated=" + canBeInvalidated +
+                '}';
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
new file mode 100644
index 0000000..187b9dc
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -0,0 +1,298 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+import com.google.common.base.Preconditions;
+
+import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
+import android.databinding.tool.util.L;
+
+import java.util.Map;
+
+import javax.annotation.processing.ProcessingEnvironment;
+
+/**
+ * This is the base class for several implementations of something that
+ * acts like a ClassLoader. Different implementations work with the Annotation
+ * Processor, ClassLoader, and an Android Studio plugin.
+ */
+public abstract class ModelAnalyzer {
+
+    public static final String[] LIST_CLASS_NAMES = {
+            "java.util.List",
+            "android.util.SparseArray",
+            "android.util.SparseBooleanArray",
+            "android.util.SparseIntArray",
+            "android.util.SparseLongArray",
+            "android.util.LongSparseArray",
+            "android.support.v4.util.LongSparseArray",
+    };
+
+    public static final String MAP_CLASS_NAME = "java.util.Map";
+
+    public static final String STRING_CLASS_NAME = "java.lang.String";
+
+    public static final String OBJECT_CLASS_NAME = "java.lang.Object";
+
+    public static final String OBSERVABLE_CLASS_NAME = "android.databinding.Observable";
+
+    public static final String OBSERVABLE_LIST_CLASS_NAME = "android.databinding.ObservableList";
+
+    public static final String OBSERVABLE_MAP_CLASS_NAME = "android.databinding.ObservableMap";
+
+    public static final String[] OBSERVABLE_FIELDS = {
+            "android.databinding.ObservableBoolean",
+            "android.databinding.ObservableByte",
+            "android.databinding.ObservableChar",
+            "android.databinding.ObservableShort",
+            "android.databinding.ObservableInt",
+            "android.databinding.ObservableLong",
+            "android.databinding.ObservableFloat",
+            "android.databinding.ObservableDouble",
+            "android.databinding.ObservableField",
+    };
+
+    public static final String VIEW_DATA_BINDING =
+            "android.databinding.ViewDataBinding";
+
+    public static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub";
+
+    private ModelClass[] mListTypes;
+    private ModelClass mMapType;
+    private ModelClass mStringType;
+    private ModelClass mObjectType;
+    private ModelClass mObservableType;
+    private ModelClass mObservableListType;
+    private ModelClass mObservableMapType;
+    private ModelClass[] mObservableFieldTypes;
+    private ModelClass mViewBindingType;
+    private ModelClass mViewStubType;
+
+    private static ModelAnalyzer sAnalyzer;
+
+    protected void setInstance(ModelAnalyzer analyzer) {
+        sAnalyzer = analyzer;
+    }
+
+    public ModelClass findCommonParentOf(ModelClass modelClass1,
+            ModelClass modelClass2) {
+        ModelClass curr = modelClass1;
+        while (curr != null && !curr.isAssignableFrom(modelClass2)) {
+            curr = curr.getSuperclass();
+        }
+        if (curr == null) {
+            ModelClass primitive1 = modelClass1.unbox();
+            ModelClass primitive2 = modelClass2.unbox();
+            if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) {
+                return findCommonParentOf(primitive1, primitive2);
+            }
+        }
+        Preconditions.checkNotNull(curr,
+                "must be able to find a common parent for " + modelClass1 + " and " + modelClass2);
+        return curr;
+
+    }
+
+    public abstract ModelClass loadPrimitive(String className);
+
+    public static ModelAnalyzer getInstance() {
+        return sAnalyzer;
+    }
+
+    public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) {
+        if (sAnalyzer != null) {
+            throw new IllegalStateException("processing env is already created, you cannot "
+                    + "change class loader after that");
+        }
+        L.d("setting processing env to %s", processingEnvironment);
+        AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment);
+        sAnalyzer = annotationAnalyzer;
+    }
+
+    /**
+     * Takes a raw className (potentially w/ generics and arrays) and expands definitions using
+     * the import statements.
+     * <p>
+     * For instance, this allows user to define variables
+     * <variable type="User" name="user"/>
+     * if they previously imported User.
+     * <import name="com.example.User"/>
+     */
+    public String applyImports(String className, Map<String, String> imports) {
+        className = className.trim();
+        int numDimensions = 0;
+        String generic = null;
+        // handle array
+        while (className.endsWith("[]")) {
+            numDimensions++;
+            className = className.substring(0, className.length() - 2);
+        }
+        // handle generics
+        final int lastCharIndex = className.length() - 1;
+        if ('>' == className.charAt(lastCharIndex)) {
+            // has generic.
+            int open = className.indexOf('<');
+            if (open == -1) {
+                L.e("un-matching generic syntax for %s", className);
+                return className;
+            }
+            generic = applyImports(className.substring(open + 1, lastCharIndex), imports);
+            className = className.substring(0, open);
+        }
+        int dotIndex = className.indexOf('.');
+        final String qualifier;
+        final String rest;
+        if (dotIndex == -1) {
+            qualifier = className;
+            rest = null;
+        } else {
+            qualifier = className.substring(0, dotIndex);
+            rest = className.substring(dotIndex); // includes dot
+        }
+        final String expandedQualifier = imports.get(qualifier);
+        String result;
+        if (expandedQualifier != null) {
+            result = rest == null ? expandedQualifier : expandedQualifier + rest;
+        } else {
+            result = className; // no change
+        }
+        // now append back dimension and generics
+        if (generic != null) {
+            result = result + "<" + applyImports(generic, imports) + ">";
+        }
+        while (numDimensions-- > 0) {
+            result = result + "[]";
+        }
+        return result;
+    }
+
+    public String getDefaultValue(String className) {
+        if ("int".equals(className)) {
+            return "0";
+        }
+        if ("short".equals(className)) {
+            return "0";
+        }
+        if ("long".equals(className)) {
+            return "0L";
+        }
+        if ("float".equals(className)) {
+            return "0f";
+        }
+        if ("double".equals(className)) {
+            return "0.0";
+        }
+        if ("boolean".equals(className)) {
+            return "false";
+        }
+        if ("char".equals(className)) {
+            return "'\\u0000'";
+        }
+        if ("byte".equals(className)) {
+            return "0";
+        }
+        return "null";
+    }
+
+    public abstract ModelClass findClass(String className, Map<String, String> imports);
+
+    public abstract ModelClass findClass(Class classType);
+
+    public abstract TypeUtil createTypeUtil();
+
+    ModelClass[] getListTypes() {
+        if (mListTypes == null) {
+            mListTypes = new ModelClass[LIST_CLASS_NAMES.length];
+            for (int i = 0; i < mListTypes.length; i++) {
+                final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null);
+                if (modelClass != null) {
+                    mListTypes[i] = modelClass.erasure();
+                }
+            }
+        }
+        return mListTypes;
+    }
+
+    public ModelClass getMapType() {
+        if (mMapType == null) {
+            mMapType = loadClassErasure(MAP_CLASS_NAME);
+        }
+        return mMapType;
+    }
+
+    ModelClass getStringType() {
+        if (mStringType == null) {
+            mStringType = findClass(STRING_CLASS_NAME, null);
+        }
+        return mStringType;
+    }
+
+    ModelClass getObjectType() {
+        if (mObjectType == null) {
+            mObjectType = findClass(OBJECT_CLASS_NAME, null);
+        }
+        return mObjectType;
+    }
+
+    ModelClass getObservableType() {
+        if (mObservableType == null) {
+            mObservableType = findClass(OBSERVABLE_CLASS_NAME, null);
+        }
+        return mObservableType;
+    }
+
+    ModelClass getObservableListType() {
+        if (mObservableListType == null) {
+            mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME);
+        }
+        return mObservableListType;
+    }
+
+    ModelClass getObservableMapType() {
+        if (mObservableMapType == null) {
+            mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME);
+        }
+        return mObservableMapType;
+    }
+
+    ModelClass getViewDataBindingType() {
+        if (mViewBindingType == null) {
+            mViewBindingType = findClass(VIEW_DATA_BINDING, null);
+        }
+        return mViewBindingType;
+    }
+
+    ModelClass[] getObservableFieldTypes() {
+        if (mObservableFieldTypes == null) {
+            mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length];
+            for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
+                mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]);
+            }
+        }
+        return mObservableFieldTypes;
+    }
+
+    ModelClass getViewStubType() {
+        if (mViewStubType == null) {
+            mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
+        }
+        return mViewStubType;
+    }
+
+    private ModelClass loadClassErasure(String className) {
+        return findClass(className, null).erasure();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
new file mode 100644
index 0000000..c55a400
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
@@ -0,0 +1,388 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+import android.databinding.tool.util.L;
+
+import org.apache.commons.lang3.StringUtils;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ModelClass {
+
+    public abstract String toJavaCode();
+
+    /**
+     * @return whether this ModelClass represents an array.
+     */
+    public abstract boolean isArray();
+
+    /**
+     * For arrays, lists, and maps, this returns the contained value. For other types, null
+     * is returned.
+     *
+     * @return The component type for arrays, the value type for maps, and the element type
+     * for lists.
+     */
+    public abstract ModelClass getComponentType();
+
+    /**
+     * @return Whether or not this ModelClass can be treated as a List. This means
+     * it is a java.util.List, or one of the Sparse*Array classes.
+     */
+    public boolean isList() {
+        for (ModelClass listType : ModelAnalyzer.getInstance().getListTypes()) {
+            if (listType != null) {
+                if (listType.isAssignableFrom(this)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return whether or not this ModelClass can be considered a Map or not.
+     */
+    public boolean isMap()  {
+        return ModelAnalyzer.getInstance().getMapType().isAssignableFrom(erasure());
+    }
+
+    /**
+     * @return whether or not this ModelClass is a java.lang.String.
+     */
+    public boolean isString() {
+        return ModelAnalyzer.getInstance().getStringType().equals(this);
+    }
+
+    /**
+     * @return whether or not this ModelClass represents a Reference type.
+     */
+    public abstract boolean isNullable();
+
+    /**
+     * @return whether or not this ModelClass represents a primitive type.
+     */
+    public abstract boolean isPrimitive();
+
+    /**
+     * @return whether or not this ModelClass represents a Java boolean
+     */
+    public abstract boolean isBoolean();
+
+    /**
+     * @return whether or not this ModelClass represents a Java char
+     */
+    public abstract boolean isChar();
+
+    /**
+     * @return whether or not this ModelClass represents a Java byte
+     */
+    public abstract boolean isByte();
+
+    /**
+     * @return whether or not this ModelClass represents a Java short
+     */
+    public abstract boolean isShort();
+
+    /**
+     * @return whether or not this ModelClass represents a Java int
+     */
+    public abstract boolean isInt();
+
+    /**
+     * @return whether or not this ModelClass represents a Java long
+     */
+    public abstract boolean isLong();
+
+    /**
+     * @return whether or not this ModelClass represents a Java float
+     */
+    public abstract boolean isFloat();
+
+    /**
+     * @return whether or not this ModelClass represents a Java double
+     */
+    public abstract boolean isDouble();
+
+    /**
+     * @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass.
+     */
+    public boolean isObject() {
+        return ModelAnalyzer.getInstance().getObjectType().equals(this);
+    }
+
+    /**
+     * @return whether or not this ModelClass type extends ViewStub.
+     */
+    public boolean extendsViewStub() {
+        return ModelAnalyzer.getInstance().getViewStubType().isAssignableFrom(this);
+    }
+
+    /**
+     * @return whether or not this is an Observable type such as ObservableMap, ObservableList,
+     * or Observable.
+     */
+    public boolean isObservable() {
+        ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+        return modelAnalyzer.getObservableType().isAssignableFrom(this) ||
+                modelAnalyzer.getObservableListType().isAssignableFrom(this) ||
+                modelAnalyzer.getObservableMapType().isAssignableFrom(this);
+
+    }
+
+    /**
+     * @return whether or not this is an ObservableField, or any of the primitive versions
+     * such as ObservableBoolean and ObservableInt
+     */
+    public boolean isObservableField() {
+        ModelClass erasure = erasure();
+        for (ModelClass observableField : ModelAnalyzer.getInstance().getObservableFieldTypes()) {
+            if (observableField.isAssignableFrom(erasure)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return whether or not this ModelClass represents a void
+     */
+    public abstract boolean isVoid();
+
+    /**
+     * When this is a boxed type, such as Integer, this will return the unboxed value,
+     * such as int. If this is not a boxed type, this is returned.
+     *
+     * @return The unboxed type of the class that this ModelClass represents or this if it isn't a
+     * boxed type.
+     */
+    public abstract ModelClass unbox();
+
+    /**
+     * When this is a primitive type, such as boolean, this will return the boxed value,
+     * such as Boolean. If this is not a primitive type, this is returned.
+     *
+     * @return The boxed type of the class that this ModelClass represents or this if it isn't a
+     * primitive type.
+     */
+    public abstract ModelClass box();
+
+    /**
+     * Returns whether or not the type associated with <code>that</code> can be assigned to
+     * the type associated with this ModelClass. If this and that only require boxing or unboxing
+     * then true is returned.
+     *
+     * @param that the ModelClass to compare.
+     * @return true if <code>that</code> requires only boxing or if <code>that</code> is an
+     * implementation of or subclass of <code>this</code>.
+     */
+    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.
+     *
+     * @param name The name of the method to find.
+     * @param args The types that the method should accept.
+     * @param isStatic Whether only static methods should be returned or instance methods.
+     * @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 isStatic) {
+        ModelMethod[] methods = getDeclaredMethods();
+        ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
+        for (ModelMethod method : methods) {
+            if (method.isPublic() && method.isStatic() == isStatic &&
+                    name.equals(method.getName()) && method.acceptsArguments(args)) {
+                matching.add(method);
+            }
+        }
+        return matching.toArray(new ModelMethod[matching.size()]);
+    }
+
+    /**
+     * Returns all public instance methods with the given name and number of parameters.
+     *
+     * @param name The name of the method to find.
+     * @param numParameters The number of parameters that the method should take
+     * @return An array containing all public methods with the given name and number of parameters.
+     */
+    public ModelMethod[] getMethods(String name, int numParameters) {
+        ModelMethod[] methods = getDeclaredMethods();
+        ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
+        for (ModelMethod method : methods) {
+            if (method.isPublic() && !method.isStatic() &&
+                    name.equals(method.getName()) &&
+                    method.getParameterTypes().length == numParameters) {
+                matching.add(method);
+            }
+        }
+        return matching.toArray(new ModelMethod[matching.size()]);
+    }
+
+    /**
+     * Returns the public method with the name <code>name</code> with the parameters that
+     * best match args. <code>staticAccess</code> governs whether a static or instance method
+     * will be returned. If no matching method was found, null is returned.
+     *
+     * @param name The method name to find
+     * @param args The arguments that the method should accept
+     * @param staticAccess true if the returned method should be static or false if it should
+     *                     be an instance method.
+     */
+    public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticAccess) {
+        ModelMethod[] methods = getMethods(name, args, staticAccess);
+        if (methods.length == 0) {
+            return null;
+        }
+        ModelMethod bestMethod = methods[0];
+        for (int i = 1; i < methods.length; i++) {
+            if (methods[i].isBetterArgMatchThan(bestMethod, args)) {
+                bestMethod = methods[i];
+            }
+        }
+        return bestMethod;
+    }
+
+    /**
+     * If this represents a class, the super class that it extends is returned. If this
+     * represents an interface, the interface that this extends is returned.
+     * <code>null</code> is returned if this is not a class or interface, such as an int, or
+     * if it is java.lang.Object or an interface that does not extend any other type.
+     *
+     * @return The class or interface that this ModelClass extends or null.
+     */
+    public abstract ModelClass getSuperclass();
+
+    /**
+     * @return A String representation of the class or interface that this represents, not
+     * including any type arguments.
+     */
+    public String getCanonicalName() {
+        return erasure().toJavaCode();
+    }
+
+    /**
+     * Returns this class type without any generic type arguments.
+     * @return this class type without any generic type arguments.
+     */
+    public abstract ModelClass erasure();
+
+    /**
+     * Since when this class is available. Important for Binding expressions so that we don't
+     * call non-existing APIs when setting UI.
+     *
+     * @return The SDK_INT where this method was added. If it is not a framework method, should
+     * return 1.
+     */
+    public int getMinApi() {
+        return SdkUtil.getMinApi(this);
+    }
+
+    /**
+     * Returns the JNI description of the method which can be used to lookup it in SDK.
+     * @see TypeUtil
+     */
+    public abstract String getJniDescription();
+
+    /**
+     * Returns the getter method or field that the name refers to.
+     * @param name The name of the field or the body of the method name -- can be name(),
+     *             getName(), or isName().
+     * @param staticAccess Whether this should look for static methods and fields or instance
+     *                     versions
+     * @return the getter method or field that the name refers to.
+     * @throws IllegalArgumentException if there is no such method or field available.
+     */
+    public Callable findGetterOrField(String name, boolean staticAccess) {
+        String capitalized = StringUtils.capitalize(name);
+        String[] methodNames = {
+                "get" + capitalized,
+                "is" + capitalized,
+                name
+        };
+        final ModelField backingField = getField(name, true, staticAccess);
+        L.d("Finding getter or field for %s, field = %s", name, backingField == null ? null : backingField.getName());
+        for (String methodName : methodNames) {
+            ModelMethod[] methods = getMethods(methodName, 0);
+            for (ModelMethod method : methods) {
+                if (method.isPublic() && method.isStatic() == staticAccess) {
+                    final Callable result = new Callable(Callable.Type.METHOD, methodName,
+                            method.getReturnType(null), true, method.isBindable() ||
+                            (backingField != null && backingField.isBindable()));
+                    L.d("backing field for %s is %s", result, backingField);
+                    return result;
+                }
+            }
+        }
+
+        if (backingField != null && backingField.isPublic()) {
+            ModelClass fieldType = backingField.getFieldType();
+            return new Callable(Callable.Type.FIELD, name, fieldType,
+                    !backingField.isFinal() || fieldType.isObservable(), backingField.isBindable());
+        }
+        throw new IllegalArgumentException(
+                "cannot find " + name + " in " + toJavaCode());
+
+    }
+
+    public ModelField getField(String name, boolean allowPrivate, boolean staticAccess) {
+        ModelField[] fields = getDeclaredFields();
+        for (ModelField field : fields) {
+            if (name.equals(stripFieldName(field.getName())) && field.isStatic() == staticAccess &&
+                    (allowPrivate || !field.isPublic())) {
+                return field;
+            }
+        }
+        return null;
+    }
+
+    protected abstract ModelField[] getDeclaredFields();
+
+    protected abstract ModelMethod[] getDeclaredMethods();
+
+    private static String stripFieldName(String fieldName) {
+        // TODO: Make this configurable through IntelliJ
+        if (fieldName.length() > 2) {
+            final char start = fieldName.charAt(2);
+            if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
+                return Character.toLowerCase(start) + fieldName.substring(3);
+            }
+        }
+        if (fieldName.length() > 1) {
+            final char start = fieldName.charAt(1);
+            final char fieldIdentifier = fieldName.charAt(0);
+            final boolean strip;
+            if (fieldIdentifier == '_') {
+                strip = true;
+            } else if (fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
+                    !Character.isLowerCase(start)) {
+                strip = true;
+            } else {
+                strip = false; // not mUppercase format
+            }
+            if (strip) {
+                return Character.toLowerCase(start) + fieldName.substring(2);
+            }
+        }
+        return fieldName;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java
new file mode 100644
index 0000000..0cde85b
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+public abstract class ModelField {
+
+    /**
+     * @return Whether this field has been annotated with Bindable.
+     */
+    public abstract boolean isBindable();
+
+    /**
+     * @return The field name.
+     */
+    public abstract String getName();
+
+    /**
+     * @return true if this field is marked public.
+     */
+    public abstract boolean isPublic();
+
+    /**
+     * @return true if this is a static field.
+     */
+    public abstract boolean isStatic();
+
+    /**
+     * @return true if the field was declared final.
+     */
+    public abstract boolean isFinal();
+
+    /**
+     * @return The declared type of the field variable.
+     */
+    public abstract ModelClass getFieldType();
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
new file mode 100644
index 0000000..b4ee671
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+import android.databinding.Bindable;
+
+import java.util.List;
+
+public abstract class ModelMethod {
+    public abstract ModelClass getDeclaringClass();
+
+    public abstract ModelClass[] getParameterTypes();
+
+    public abstract String getName();
+
+    public abstract ModelClass getReturnType(List<ModelClass> args);
+
+    public abstract boolean isVoid();
+
+    public abstract boolean isPublic();
+
+    public abstract boolean isStatic();
+
+    /**
+     * @return whether or not this method has been given the {@link Bindable} annotation.
+     */
+    public abstract boolean isBindable();
+
+    /**
+     * Since when this method is available. Important for Binding expressions so that we don't
+     * call non-existing APIs when setting UI.
+     *
+     * @return The SDK_INT where this method was added. If it is not a framework method, should
+     * return 1.
+     */
+    public abstract int getMinApi();
+
+    /**
+     * Returns the JNI description of the method which can be used to lookup it in SDK.
+     * @see TypeUtil
+     */
+    public abstract String getJniDescription();
+
+    /**
+     * @return true if the final parameter is a varargs parameter.
+     */
+    public abstract boolean isVarArgs();
+
+    /**
+     * @param args The arguments to the method
+     * @return Whether the arguments would be accepted as parameters to this method.
+     */
+    public boolean acceptsArguments(List<ModelClass> args) {
+        boolean isVarArgs = isVarArgs();
+        ModelClass[] parameterTypes = getParameterTypes();
+        if ((!isVarArgs && args.size() != parameterTypes.length) ||
+                (isVarArgs && args.size() < parameterTypes.length - 1)) {
+            return false; // The wrong number of parameters
+        }
+        boolean parametersMatch = true;
+        for (int i = 0; i < args.size(); i++) {
+            ModelClass parameterType = getParameter(i, parameterTypes);
+            ModelClass arg = args.get(i);
+            if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) {
+                parametersMatch = false;
+                break;
+            }
+        }
+        return parametersMatch;
+    }
+
+    public boolean isBetterArgMatchThan(ModelMethod other, List<ModelClass> args) {
+        final ModelClass[] parameterTypes = getParameterTypes();
+        final ModelClass[] otherParameterTypes = other.getParameterTypes();
+        for (int i = 0; i < args.size(); i++) {
+            final ModelClass arg = args.get(i);
+            final ModelClass thisParameter = getParameter(i, parameterTypes);
+            final ModelClass thatParameter = other.getParameter(i, otherParameterTypes);
+            final int diff = compareParameter(arg, thisParameter, thatParameter);
+            if (diff != 0) {
+                return diff < 0;
+            }
+        }
+        return false;
+    }
+
+    private ModelClass getParameter(int index, ModelClass[] parameterTypes) {
+        int normalParamCount = isVarArgs() ? parameterTypes.length - 1 : parameterTypes.length;
+        if (index < normalParamCount) {
+            return parameterTypes[index];
+        } else {
+            return parameterTypes[parameterTypes.length - 1].getComponentType();
+        }
+    }
+
+    private static int compareParameter(ModelClass arg, ModelClass thisParameter,
+            ModelClass thatParameter) {
+        if (thatParameter.equals(arg)) {
+            return 1;
+        } else if (thisParameter.equals(arg)) {
+            return -1;
+        } else if (isBoxingConversion(thatParameter, arg)) {
+            return 1;
+        } else if (isBoxingConversion(thisParameter, arg)) {
+            // Boxing/unboxing is second best
+            return -1;
+        } else {
+            int argConversionLevel = getImplicitConversionLevel(arg);
+            if (argConversionLevel != -1) {
+                int oldConversionLevel = getImplicitConversionLevel(thatParameter);
+                int newConversionLevel = getImplicitConversionLevel(thisParameter);
+                if (newConversionLevel != -1 &&
+                        (oldConversionLevel == -1 || newConversionLevel < oldConversionLevel)) {
+                    return -1;
+                } else if (oldConversionLevel != -1) {
+                    return 1;
+                }
+            }
+            // Look for more exact match
+            if (thatParameter.isAssignableFrom(thisParameter)) {
+                return -1;
+            }
+        }
+        return 0; // no difference
+    }
+
+    public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) {
+        if (class1.isPrimitive() != class2.isPrimitive()) {
+            return (class1.box().equals(class2.box()));
+        } else {
+            return false;
+        }
+    }
+
+    public static int getImplicitConversionLevel(ModelClass primitive) {
+        if (primitive == null) {
+            return -1;
+        } else if (primitive.isByte()) {
+            return 0;
+        } else if (primitive.isChar()) {
+            return 1;
+        } else if (primitive.isShort()) {
+            return 2;
+        } else if (primitive.isInt()) {
+            return 3;
+        } else if (primitive.isLong()) {
+            return 4;
+        } else if (primitive.isFloat()) {
+            return 5;
+        } else if (primitive.isDouble()) {
+            return 6;
+        } else {
+            return -1;
+        }
+    }
+
+    public static boolean isImplicitConversion(ModelClass from, ModelClass to) {
+        if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) {
+            if (from.isBoolean() || to.isBoolean() || to.isChar()) {
+                return false;
+            }
+            int fromConversionLevel = getImplicitConversionLevel(from);
+            int toConversionLevel = getImplicitConversionLevel(to);
+            return fromConversionLevel < toConversionLevel;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java
new file mode 100644
index 0000000..177935a
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java
@@ -0,0 +1,159 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+import com.google.common.base.Preconditions;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import android.databinding.tool.util.L;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+/**
+ * Class that is used for SDK related stuff.
+ * <p>
+ * Must be initialized with the sdk location to work properly
+ */
+public class SdkUtil {
+
+    static File mSdkPath;
+
+    static ApiChecker mApiChecker;
+
+    static int mMinSdk;
+
+    public static void initialize(int minSdk, File sdkPath) {
+        mSdkPath = sdkPath;
+        mMinSdk = minSdk;
+        mApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath()
+                + "/platform-tools/api/api-versions.xml"));
+        L.d("SdkUtil init, minSdk: %s", minSdk);
+    }
+
+    public static int getMinApi(ModelClass modelClass) {
+        return mApiChecker.getMinApi(modelClass.getJniDescription(), null);
+    }
+
+    public static int getMinApi(ModelMethod modelMethod) {
+        ModelClass declaringClass = modelMethod.getDeclaringClass();
+        Preconditions.checkNotNull(mApiChecker, "should've initialized api checker");
+        while (declaringClass != null) {
+            String classDesc = declaringClass.getJniDescription();
+            String methodDesc = modelMethod.getJniDescription();
+            int result = mApiChecker.getMinApi(classDesc, methodDesc);
+            L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(),
+                    classDesc, methodDesc, result);
+            if (result > 1) {
+                return result;
+            }
+            declaringClass = declaringClass.getSuperclass();
+        }
+        return 1;
+    }
+
+    private static class ApiChecker {
+
+        private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
+
+        private Document mDoc;
+
+        private XPath mXPath;
+
+        public ApiChecker(File apiFile) {
+            try {
+                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+                DocumentBuilder builder = factory.newDocumentBuilder();
+                mDoc = builder.parse(apiFile);
+                XPathFactory xPathFactory = XPathFactory.newInstance();
+                mXPath = xPathFactory.newXPath();
+                buildFullLookup();
+            } catch (Throwable t) {
+                L.e(t, "cannot load api descriptions from %s", apiFile);
+            }
+        }
+
+        private void buildFullLookup() throws XPathExpressionException {
+            NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes();
+            for (int j = 0; j < allClasses.getLength(); j++) {
+                Node node = allClasses.item(j);
+                if (node.getNodeType() != Node.ELEMENT_NODE || !"class"
+                        .equals(node.getNodeName())) {
+                    continue;
+                }
+                //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue());
+                int classSince = getSince(node);
+                String classDesc = node.getAttributes().getNamedItem("name").getNodeValue();
+
+                final NodeList childNodes = node.getChildNodes();
+                for (int i = 0; i < childNodes.getLength(); i++) {
+                    Node child = childNodes.item(i);
+                    if (child.getNodeType() != Node.ELEMENT_NODE || !"method"
+                            .equals(child.getNodeName())) {
+                        continue;
+                    }
+                    int methodSince = getSince(child);
+                    int since = Math.max(classSince, methodSince);
+                    if (since > SdkUtil.mMinSdk) {
+                        String methodDesc = child.getAttributes().getNamedItem("name")
+                                .getNodeValue();
+                        String key = cacheKey(classDesc, methodDesc);
+                        mFullLookup.put(key, since);
+                    }
+                }
+            }
+        }
+
+        public int getMinApi(String classDesc, String methodOrFieldDesc) {
+            if (mDoc == null || mXPath == null) {
+                return 1;
+            }
+            if (classDesc == null || classDesc.isEmpty()) {
+                return 1;
+            }
+            final String key = cacheKey(classDesc, methodOrFieldDesc);
+            Integer since = mFullLookup.get(key);
+            return since == null ? 1 : since;
+        }
+
+        private static String cacheKey(String classDesc, String methodOrFieldDesc) {
+            return classDesc + "~" + methodOrFieldDesc;
+        }
+
+        private static int getSince(Node node) {
+            final Node since = node.getAttributes().getNamedItem("since");
+            if (since != null) {
+                final String nodeValue = since.getNodeValue();
+                if (nodeValue != null && !nodeValue.isEmpty()) {
+                    try {
+                        return Integer.parseInt(nodeValue);
+                    } catch (Throwable t) {
+                    }
+                }
+            }
+
+            return 1;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java
new file mode 100644
index 0000000..f396bd7
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.databinding.tool.reflection;
+
+public abstract class TypeUtil {
+
+    public static final String BYTE = "B";
+
+    public static final String CHAR = "C";
+
+    public static final String DOUBLE = "D";
+
+    public static final String FLOAT = "F";
+
+    public static final String INT = "I";
+
+    public static final String LONG = "J";
+
+    public static final String SHORT = "S";
+
+    public static final String VOID = "V";
+
+    public static final String BOOLEAN = "Z";
+
+    public static final String ARRAY = "[";
+
+    public static final String CLASS_PREFIX = "L";
+
+    public static final String CLASS_SUFFIX = ";";
+
+    private static TypeUtil sInstance;
+
+    abstract public String getDescription(ModelClass modelClass);
+
+    abstract public String getDescription(ModelMethod modelMethod);
+
+    public static TypeUtil getInstance() {
+        if (sInstance == null) {
+            sInstance = ModelAnalyzer.getInstance().createTypeUtil();
+        }
+        return sInstance;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
new file mode 100644
index 0000000..d01f579
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.databinding.tool.reflection.annotation;
+
+import com.google.common.collect.ImmutableMap;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.TypeUtil;
+import android.databinding.tool.util.L;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+public class AnnotationAnalyzer extends ModelAnalyzer {
+
+    public static final Map<String, TypeKind> PRIMITIVE_TYPES =
+            new ImmutableMap.Builder<String, TypeKind>()
+                    .put("boolean", TypeKind.BOOLEAN)
+                    .put("byte", TypeKind.BYTE)
+                    .put("short", TypeKind.SHORT)
+                    .put("char", TypeKind.CHAR)
+                    .put("int", TypeKind.INT)
+                    .put("long", TypeKind.LONG)
+                    .put("float", TypeKind.FLOAT)
+                    .put("double", TypeKind.DOUBLE)
+                    .build();
+
+    public final ProcessingEnvironment mProcessingEnv;
+
+    public AnnotationAnalyzer(ProcessingEnvironment processingEnvironment) {
+        mProcessingEnv = processingEnvironment;
+        setInstance(this);
+    }
+
+    public static AnnotationAnalyzer get() {
+        return (AnnotationAnalyzer) getInstance();
+    }
+
+    @Override
+    public AnnotationClass loadPrimitive(String className) {
+        TypeKind typeKind = PRIMITIVE_TYPES.get(className);
+        if (typeKind == null) {
+            return null;
+        } else {
+            Types typeUtils = getTypeUtils();
+            return new AnnotationClass(typeUtils.getPrimitiveType(typeKind));
+        }
+    }
+
+    @Override
+    public AnnotationClass findClass(String className, Map<String, String> imports) {
+        className = className.trim();
+        int numDimensions = 0;
+        while (className.endsWith("[]")) {
+            numDimensions++;
+            className = className.substring(0, className.length() - 2);
+        }
+        AnnotationClass primitive = loadPrimitive(className);
+        if (primitive != null) {
+            return addDimension(primitive.mTypeMirror, numDimensions);
+        }
+        int templateOpenIndex = className.indexOf('<');
+        DeclaredType declaredType;
+        if (templateOpenIndex < 0) {
+            TypeElement typeElement = getTypeElement(className, imports);
+            if (typeElement == null) {
+                return null;
+            }
+            declaredType = (DeclaredType) typeElement.asType();
+        } else {
+            int templateCloseIndex = className.lastIndexOf('>');
+            String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex);
+
+            String baseClassName = className.substring(0, templateOpenIndex);
+            TypeElement typeElement = getTypeElement(baseClassName, imports);
+            if (typeElement == null) {
+                L.e("cannot find type element for %s", baseClassName);
+                return null;
+            }
+
+            ArrayList<String> templateParameters = splitTemplateParameters(paramStr);
+            TypeMirror[] typeArgs = new TypeMirror[templateParameters.size()];
+            for (int i = 0; i < typeArgs.length; i++) {
+                typeArgs[i] = findClass(templateParameters.get(i), imports).mTypeMirror;
+                if (typeArgs[i] == null) {
+                    L.e("cannot find type argument for %s in %s", templateParameters.get(i),
+                            baseClassName);
+                    return null;
+                }
+            }
+            Types typeUtils = getTypeUtils();
+            declaredType = typeUtils.getDeclaredType(typeElement, typeArgs);
+        }
+        return addDimension(declaredType, numDimensions);
+    }
+
+    private AnnotationClass addDimension(TypeMirror type, int numDimensions) {
+        while (numDimensions > 0) {
+            type = getTypeUtils().getArrayType(type);
+            numDimensions--;
+        }
+        return new AnnotationClass(type);
+    }
+
+    private TypeElement getTypeElement(String className, Map<String, String> imports) {
+        Elements elementUtils = getElementUtils();
+        if (className.indexOf('.') < 0 && imports != null) {
+            // try the imports
+            String importedClass = imports.get(className);
+            if (importedClass != null) {
+                className = importedClass;
+            }
+        }
+        if (className.indexOf('.') < 0) {
+            // try java.lang.
+            String javaLangClass = "java.lang." + className;
+            try {
+                TypeElement javaLang = elementUtils.getTypeElement(javaLangClass);
+                if (javaLang != null) {
+                    return javaLang;
+                }
+            } catch (Exception e) {
+                // try the normal way
+            }
+        }
+        try {
+            return elementUtils.getTypeElement(className);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private ArrayList<String> splitTemplateParameters(String templateParameters) {
+        ArrayList<String> list = new ArrayList<String>();
+        int index = 0;
+        int openCount = 0;
+        StringBuilder arg = new StringBuilder();
+        while (index < templateParameters.length()) {
+            char c = templateParameters.charAt(index);
+            if (c == ',' && openCount == 0) {
+                list.add(arg.toString());
+                arg.delete(0, arg.length());
+            } else if (!Character.isWhitespace(c)) {
+                arg.append(c);
+                if (c == '<') {
+                    openCount++;
+                } else if (c == '>') {
+                    openCount--;
+                }
+            }
+            index++;
+        }
+        list.add(arg.toString());
+        return list;
+    }
+
+    @Override
+    public ModelClass findClass(Class classType) {
+        return findClass(classType.getCanonicalName(), null);
+    }
+
+    public Types getTypeUtils() {
+        return mProcessingEnv.getTypeUtils();
+    }
+
+    public Elements getElementUtils() {
+        return mProcessingEnv.getElementUtils();
+    }
+
+    public ProcessingEnvironment getProcessingEnv() {
+        return mProcessingEnv;
+    }
+
+    @Override
+    public TypeUtil createTypeUtil() {
+        return new AnnotationTypeUtil(this);
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
new file mode 100644
index 0000000..a1bec3c3
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
@@ -0,0 +1,323 @@
+/*
+ * 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 android.databinding.tool.reflection.annotation;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelField;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.TypeUtil;
+import android.databinding.tool.util.L;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * This is the implementation of ModelClass for the annotation
+ * processor. It relies on AnnotationAnalyzer.
+ */
+class AnnotationClass extends ModelClass {
+
+    final TypeMirror mTypeMirror;
+
+    public AnnotationClass(TypeMirror typeMirror) {
+        mTypeMirror = typeMirror;
+    }
+
+    @Override
+    public String toJavaCode() {
+        return mTypeMirror.toString();
+    }
+
+    @Override
+    public boolean isArray() {
+        return mTypeMirror.getKind() == TypeKind.ARRAY;
+    }
+
+    @Override
+    public AnnotationClass getComponentType() {
+        TypeMirror component = null;
+        if (isArray()) {
+            component = ((ArrayType) mTypeMirror).getComponentType();
+        } else if (isList()) {
+            for (ModelMethod method : getMethods("get", 1)) {
+                ModelClass parameter = method.getParameterTypes()[0];
+                if (parameter.isInt() || parameter.isLong()) {
+                    ArrayList<ModelClass> parameters = new ArrayList<ModelClass>(1);
+                    parameters.add(parameter);
+                    return (AnnotationClass) method.getReturnType(parameters);
+                }
+            }
+            // no "get" call found!
+            return null;
+        } else {
+            AnnotationClass mapClass = (AnnotationClass) ModelAnalyzer.getInstance().getMapType();
+            DeclaredType mapType = findInterface(mapClass.mTypeMirror);
+            if (mapType == null) {
+                return null;
+            }
+            component = mapType.getTypeArguments().get(1);
+        }
+
+        return new AnnotationClass(component);
+    }
+
+    private DeclaredType findInterface(TypeMirror interfaceType) {
+        Types typeUtil = getTypeUtils();
+        TypeMirror foundInterface = null;
+        if (typeUtil.isSameType(interfaceType, typeUtil.erasure(mTypeMirror))) {
+            foundInterface = mTypeMirror;
+        } else {
+            ArrayList<TypeMirror> toCheck = new ArrayList<TypeMirror>();
+            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) {
+                L.e("Detected " + interfaceType + " type for " + mTypeMirror +
+                        ", but not able to find the implemented interface.");
+                return null;
+            }
+        }
+        if (foundInterface.getKind() != TypeKind.DECLARED) {
+            L.e("Found " + interfaceType + " type for " + mTypeMirror +
+                    ", but it isn't a declared type: " + foundInterface);
+            return null;
+        }
+        return (DeclaredType) foundInterface;
+    }
+
+    @Override
+    public boolean isNullable() {
+        switch (mTypeMirror.getKind()) {
+            case ARRAY:
+            case DECLARED:
+            case NULL:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean isPrimitive() {
+        switch (mTypeMirror.getKind()) {
+            case BOOLEAN:
+            case BYTE:
+            case SHORT:
+            case INT:
+            case LONG:
+            case CHAR:
+            case FLOAT:
+            case DOUBLE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean isBoolean() {
+        return mTypeMirror.getKind() == TypeKind.BOOLEAN;
+    }
+
+    @Override
+    public boolean isChar() {
+        return mTypeMirror.getKind() == TypeKind.CHAR;
+    }
+
+    @Override
+    public boolean isByte() {
+        return mTypeMirror.getKind() == TypeKind.BYTE;
+    }
+
+    @Override
+    public boolean isShort() {
+        return mTypeMirror.getKind() == TypeKind.SHORT;
+    }
+
+    @Override
+    public boolean isInt() {
+        return mTypeMirror.getKind() == TypeKind.INT;
+    }
+
+    @Override
+    public boolean isLong() {
+        return mTypeMirror.getKind() == TypeKind.LONG;
+    }
+
+    @Override
+    public boolean isFloat() {
+        return mTypeMirror.getKind() == TypeKind.FLOAT;
+    }
+
+    @Override
+    public boolean isDouble() {
+        return mTypeMirror.getKind() == TypeKind.DOUBLE;
+    }
+
+    @Override
+    public boolean isVoid() {
+        return mTypeMirror.getKind() == TypeKind.VOID;
+    }
+
+    @Override
+    public AnnotationClass unbox() {
+        if (!isNullable()) {
+            return this;
+        }
+        try {
+            return new AnnotationClass(getTypeUtils().unboxedType(mTypeMirror));
+        } catch (IllegalArgumentException e) {
+            // I'm being lazy. This is much easier than checking every type.
+            return this;
+        }
+    }
+
+    @Override
+    public AnnotationClass box() {
+        if (!isPrimitive()) {
+            return this;
+        }
+        return new AnnotationClass(getTypeUtils().boxedClass((PrimitiveType) mTypeMirror).asType());
+    }
+
+    @Override
+    public boolean isAssignableFrom(ModelClass that) {
+        if (that == null) {
+            return false;
+        }
+        AnnotationClass thatAnnotationClass = (AnnotationClass) that;
+        return getTypeUtils().isAssignable(thatAnnotationClass.mTypeMirror, this.mTypeMirror);
+    }
+
+    @Override
+    public ModelMethod[] getDeclaredMethods() {
+        final ModelMethod[] declaredMethods;
+        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
+            DeclaredType declaredType = (DeclaredType) mTypeMirror;
+            Elements elementUtils = getElementUtils();
+            TypeElement typeElement = (TypeElement) declaredType.asElement();
+            List<? extends Element> members = elementUtils.getAllMembers(typeElement);
+            List<ExecutableElement> methods = ElementFilter.methodsIn(members);
+            declaredMethods = new ModelMethod[methods.size()];
+            for (int i = 0; i < declaredMethods.length; i++) {
+                declaredMethods[i] = new AnnotationMethod(declaredType, methods.get(i));
+            }
+        } else {
+            declaredMethods = new ModelMethod[0];
+        }
+        return declaredMethods;
+    }
+
+    @Override
+    public AnnotationClass getSuperclass() {
+        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
+            DeclaredType declaredType = (DeclaredType) mTypeMirror;
+            TypeElement typeElement = (TypeElement) declaredType.asElement();
+            TypeMirror superClass = typeElement.getSuperclass();
+            if (superClass.getKind() == TypeKind.DECLARED) {
+                return new AnnotationClass(superClass);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getCanonicalName() {
+        return getTypeUtils().erasure(mTypeMirror).toString();
+    }
+
+    @Override
+    public ModelClass erasure() {
+        final TypeMirror erasure = getTypeUtils().erasure(mTypeMirror);
+        if (erasure == mTypeMirror) {
+            return this;
+        } else {
+            return new AnnotationClass(erasure);
+        }
+    }
+
+    @Override
+    public String getJniDescription() {
+        return TypeUtil.getInstance().getDescription(this);
+    }
+
+    @Override
+    protected ModelField[] getDeclaredFields() {
+        final ModelField[] declaredFields;
+        if (mTypeMirror.getKind() == TypeKind.DECLARED) {
+            DeclaredType declaredType = (DeclaredType) mTypeMirror;
+            Elements elementUtils = getElementUtils();
+            TypeElement typeElement = (TypeElement) declaredType.asElement();
+            List<? extends Element> members = elementUtils.getAllMembers(typeElement);
+            List<VariableElement> fields = ElementFilter.fieldsIn(members);
+            declaredFields = new ModelField[fields.size()];
+            for (int i = 0; i < declaredFields.length; i++) {
+                declaredFields[i] = new AnnotationField(typeElement, fields.get(i));
+            }
+        } else {
+            declaredFields = new ModelField[0];
+        }
+        return declaredFields;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof AnnotationClass) {
+            return getTypeUtils().isSameType(mTypeMirror, ((AnnotationClass) obj).mTypeMirror);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return mTypeMirror.toString().hashCode();
+    }
+
+    private static Types getTypeUtils() {
+        return AnnotationAnalyzer.get().mProcessingEnv.getTypeUtils();
+    }
+
+    private static Elements getElementUtils() {
+        return AnnotationAnalyzer.get().mProcessingEnv.getElementUtils();
+    }
+
+    @Override
+    public String toString() {
+        return mTypeMirror.toString();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java
new file mode 100644
index 0000000..9373c6c
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.databinding.tool.reflection.annotation;
+
+import android.databinding.Bindable;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelField;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+class AnnotationField extends ModelField {
+
+    final VariableElement mField;
+
+    final TypeElement mDeclaredClass;
+
+    public AnnotationField(TypeElement declaredClass, VariableElement field) {
+        mDeclaredClass = declaredClass;
+        mField = field;
+    }
+
+    @Override
+    public String toString() {
+        return mField.toString();
+    }
+
+    @Override
+    public boolean isBindable() {
+        return mField.getAnnotation(Bindable.class) != null;
+    }
+
+    @Override
+    public String getName() {
+        return mField.getSimpleName().toString();
+    }
+
+    @Override
+    public boolean isPublic() {
+        return mField.getModifiers().contains(Modifier.PUBLIC);
+    }
+
+    @Override
+    public boolean isStatic() {
+        return mField.getModifiers().contains(Modifier.STATIC);
+    }
+
+    @Override
+    public boolean isFinal() {
+        return mField.getModifiers().contains(Modifier.FINAL);
+    }
+
+    @Override
+    public ModelClass getFieldType() {
+        return new AnnotationClass(mField.asType());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof AnnotationField) {
+            AnnotationField that = (AnnotationField) obj;
+            return mDeclaredClass.equals(that.mDeclaredClass) && AnnotationAnalyzer.get()
+                    .getTypeUtils().isSameType(mField.asType(), that.mField.asType());
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
new file mode 100644
index 0000000..00e1a4e
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
@@ -0,0 +1,122 @@
+/*
+ * 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 android.databinding.tool.reflection.annotation;
+
+import android.databinding.Bindable;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.SdkUtil;
+import android.databinding.tool.reflection.TypeUtil;
+
+import java.util.List;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+class AnnotationMethod extends ModelMethod {
+    final ExecutableType mMethod;
+    final DeclaredType mDeclaringType;
+    final ExecutableElement mExecutableElement;
+    int mApiLevel = -1; // calculated on demand
+
+    public AnnotationMethod(DeclaredType declaringType, ExecutableElement executableElement) {
+        mDeclaringType = declaringType;
+        mExecutableElement = executableElement;
+        Types typeUtils = AnnotationAnalyzer.get().getTypeUtils();
+        mMethod = (ExecutableType) typeUtils.asMemberOf(declaringType, executableElement);
+    }
+
+    @Override
+    public ModelClass getDeclaringClass() {
+        return new AnnotationClass(mDeclaringType);
+    }
+
+    @Override
+    public ModelClass[] getParameterTypes() {
+        List<? extends TypeMirror> parameters = mMethod.getParameterTypes();
+        ModelClass[] parameterTypes = new ModelClass[parameters.size()];
+        for (int i = 0; i < parameters.size(); i++) {
+            parameterTypes[i] = new AnnotationClass(parameters.get(i));
+        }
+        return parameterTypes;
+    }
+
+    @Override
+    public String getName() {
+        return mExecutableElement.getSimpleName().toString();
+    }
+
+    @Override
+    public ModelClass getReturnType(List<ModelClass> args) {
+        TypeMirror returnType = mMethod.getReturnType();
+        // TODO: support argument-supplied types
+        // for example: public T[] toArray(T[] arr)
+        return new AnnotationClass(returnType);
+    }
+
+    @Override
+    public boolean isVoid() {
+        return mMethod.getReturnType().getKind() == TypeKind.VOID;
+    }
+
+    @Override
+    public boolean isPublic() {
+        return mExecutableElement.getModifiers().contains(Modifier.PUBLIC);
+    }
+
+    @Override
+    public boolean isStatic() {
+        return mExecutableElement.getModifiers().contains(Modifier.STATIC);
+    }
+
+    @Override
+    public boolean isBindable() {
+        return mExecutableElement.getAnnotation(Bindable.class) != null;
+    }
+
+    @Override
+    public int getMinApi() {
+        if (mApiLevel == -1) {
+            mApiLevel = SdkUtil.getMinApi(this);
+        }
+        return mApiLevel;
+    }
+
+    @Override
+    public String getJniDescription() {
+        return TypeUtil.getInstance().getDescription(this);
+    }
+
+    @Override
+    public boolean isVarArgs() {
+        return mExecutableElement.isVarArgs();
+    }
+
+    @Override
+    public String toString() {
+        return "AnnotationMethod{" +
+                "mMethod=" + mMethod +
+                ", mDeclaringType=" + mDeclaringType +
+                ", mExecutableElement=" + mExecutableElement +
+                ", mApiLevel=" + mApiLevel +
+                '}';
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java
new file mode 100644
index 0000000..ebc6a07
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java
@@ -0,0 +1,105 @@
+/*
+ * 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 android.databinding.tool.reflection.annotation;
+
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.TypeUtil;
+
+import java.util.List;
+
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+
+public class AnnotationTypeUtil extends TypeUtil {
+    javax.lang.model.util.Types mTypes;
+
+    public AnnotationTypeUtil(
+            AnnotationAnalyzer annotationAnalyzer) {
+        mTypes = annotationAnalyzer.getTypeUtils();
+    }
+
+    @Override
+    public String getDescription(ModelClass modelClass) {
+        // TODO use interface
+        return modelClass.getCanonicalName().replace('.', '/');
+    }
+
+    @Override
+    public String getDescription(ModelMethod modelMethod) {
+        // TODO use interface
+        return modelMethod.getName() + getDescription(
+                ((AnnotationMethod) modelMethod).mExecutableElement.asType());
+    }
+
+    private String getDescription(TypeMirror typeMirror) {
+        if (typeMirror == null) {
+            throw new UnsupportedOperationException();
+        }
+        switch (typeMirror.getKind()) {
+            case BOOLEAN:
+                return BOOLEAN;
+            case BYTE:
+                return BYTE;
+            case SHORT:
+                return SHORT;
+            case INT:
+                return INT;
+            case LONG:
+                return LONG;
+            case CHAR:
+                return CHAR;
+            case FLOAT:
+                return FLOAT;
+            case DOUBLE:
+                return DOUBLE;
+            case DECLARED:
+                return CLASS_PREFIX + mTypes.erasure(typeMirror).toString().replace('.', '/') + CLASS_SUFFIX;
+            case VOID:
+                return VOID;
+            case ARRAY:
+                final ArrayType arrayType = (ArrayType) typeMirror;
+                final String componentType = getDescription(arrayType.getComponentType());
+                return ARRAY + componentType;
+            case TYPEVAR:
+                final TypeVariable typeVariable = (TypeVariable) typeMirror;
+                final String name = typeVariable.toString();
+                return CLASS_PREFIX + name.replace('.', '/') + CLASS_SUFFIX;
+            case EXECUTABLE:
+                final ExecutableType executableType = (ExecutableType) typeMirror;
+                final int argStart = mTypes.erasure(executableType).toString().indexOf('(');
+                final String methodName = executableType.toString().substring(0, argStart);
+                final String args = joinArgs(executableType.getParameterTypes());
+                // TODO detect constructor?
+                return methodName + "(" + args + ")" + getDescription(
+                        executableType.getReturnType());
+            default:
+                throw new UnsupportedOperationException("cannot understand type "
+                        + typeMirror.toString() + ", kind:" + typeMirror.getKind().name());
+        }
+    }
+
+    private String joinArgs(List<? extends TypeMirror> mirrorList) {
+        StringBuilder result = new StringBuilder();
+        for (TypeMirror mirror : mirrorList) {
+            result.append(getDescription(mirror));
+        }
+        return result.toString();
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java
new file mode 100644
index 0000000..59099221
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java
@@ -0,0 +1,276 @@
+/*
+ * 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 android.databinding.tool.store;
+
+import com.google.common.base.Preconditions;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import android.databinding.tool.util.L;
+import android.databinding.tool.util.ParserHelper;
+import android.databinding.tool.util.XmlEditor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+/**
+ * Gets the list of XML files and creates a list of
+ * {@link android.databinding.tool.store.ResourceBundle} that can be persistent or converted to
+ * LayoutBinder.
+ */
+public class LayoutFileParser {
+    private static final String XPATH_VARIABLE_DEFINITIONS = "//variable";
+    private static final String XPATH_BINDING_ELEMENTS = "//*[@*[starts-with(., '@{') and substring(., string-length(.)) = '}']]";
+    private static final String XPATH_ID_ELEMENTS = "//*[@*[local-name()='id']]";
+    private static final String XPATH_IMPORT_DEFINITIONS = "//import";
+    private static final String XPATH_MERGE_TAG = "/merge";
+    final String LAYOUT_PREFIX = "@layout/";
+
+    public ResourceBundle.LayoutFileBundle parseXml(File xml, String pkg)
+            throws ParserConfigurationException, IOException, SAXException,
+            XPathExpressionException {
+        final String xmlNoExtension = ParserHelper.INSTANCE$.stripExtension(xml.getName());
+        final String newTag = xml.getParentFile().getName() + '/' + xmlNoExtension;
+        File original = stripFileAndGetOriginal(xml, newTag);
+        if (original == null) {
+            L.d("assuming the file is the original for %s", xml.getAbsoluteFile());
+            original = xml;
+        }
+        L.d("parsing file %s", xml.getAbsolutePath());
+
+        ResourceBundle.LayoutFileBundle bundle = new ResourceBundle.LayoutFileBundle(
+                xmlNoExtension, xml.getParentFile().getName(), pkg);
+
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        final Document doc = builder.parse(original);
+
+        final XPathFactory xPathFactory = XPathFactory.newInstance();
+        final XPath xPath = xPathFactory.newXPath();
+
+        List<Node> variableNodes = getVariableNodes(doc, xPath);
+
+        L.d("number of variable nodes %d", variableNodes.size());
+        for (Node item : variableNodes) {
+            L.d("reading variable node %s", item);
+            NamedNodeMap attributes = item.getAttributes();
+            String variableName = attributes.getNamedItem("name").getNodeValue();
+            String variableType = attributes.getNamedItem("type").getNodeValue();
+            L.d("name: %s, type:%s", variableName, variableType);
+            bundle.addVariable(variableName, variableType);
+        }
+
+        final List<Node> imports = getImportNodes(doc, xPath);
+        L.d("import node count %d", imports.size());
+        for (Node item : imports) {
+            NamedNodeMap attributes = item.getAttributes();
+            String type = attributes.getNamedItem("type").getNodeValue();
+            final Node aliasNode = attributes.getNamedItem("alias");
+            final String alias;
+            if (aliasNode == null) {
+                final String[] split = StringUtils.split(type, '.');
+                alias = split[split.length - 1];
+            } else {
+                alias = aliasNode.getNodeValue();
+            }
+            bundle.addImport(alias, type);
+        }
+
+        final List<Node> bindingNodes = getBindingNodes(doc, xPath);
+        L.d("number of binding nodes %d", bindingNodes.size());
+        int tagNumber = 0;
+        for (Node parent : bindingNodes) {
+            NamedNodeMap attributes = parent.getAttributes();
+            String nodeName = parent.getNodeName();
+            String className;
+            String includedLayoutName = null;
+            final Node id = attributes.getNamedItem("android:id");
+            if ("include".equals(nodeName)) {
+                if (id == null) {
+                    L.e("<include> must have android:id attribute with binding expressions.");
+                    throw new RuntimeException("<include> must have android:id attribute " +
+                            "with binding expressions.");
+                }
+                // get the layout attribute
+                final Node includedLayout = attributes.getNamedItem("layout");
+                Preconditions.checkNotNull(includedLayout, "must include a layout");
+                final String includeValue = includedLayout.getNodeValue();
+                Preconditions.checkArgument(includeValue.startsWith(LAYOUT_PREFIX));
+                // if user is binding something there, there MUST be a layout file to be
+                // generated.
+                String layoutName = includeValue.substring(LAYOUT_PREFIX.length());
+                className = pkg + ".databinding." +
+                        ParserHelper.INSTANCE$.toClassName(layoutName) + "Binding";
+                includedLayoutName = layoutName;
+            } else {
+                className = getFullViewClassName(parent);
+            }
+            final Node originalTag = attributes.getNamedItem("android:tag");
+            final String tag;
+            if (doc.getDocumentElement() == parent) {
+                tag = null;
+            } else {
+                tag = String.valueOf(tagNumber++);
+            }
+            final ResourceBundle.BindingTargetBundle bindingTargetBundle =
+                    bundle.createBindingTarget(id == null ? null : id.getNodeValue(),
+                            className, true, tag, originalTag == null ? null : originalTag.getNodeValue());
+            bindingTargetBundle.setIncludedLayout(includedLayoutName);
+
+            final int attrCount = attributes.getLength();
+            for (int i = 0; i < attrCount; i ++) {
+                final Node attr = attributes.item(i);
+                String value = attr.getNodeValue();
+                if (value.charAt(0) == '@' && value.charAt(1) == '{' &&
+                        value.charAt(value.length() - 1) == '}') {
+                    final String strippedValue = value.substring(2, value.length() - 1);
+                    bindingTargetBundle.addBinding(attr.getNodeName(), strippedValue);
+                }
+            }
+        }
+
+        if (!bindingNodes.isEmpty() || !imports.isEmpty() || !variableNodes.isEmpty()) {
+            if (isMergeLayout(doc, xPath)) {
+                L.e("<merge> is not allowed with data binding.");
+                throw new RuntimeException("<merge> is not allowed with data binding.");
+            }
+            final List<Node> idNodes = getNakedIds(doc, xPath);
+            for (Node node : idNodes) {
+                if (!bindingNodes.contains(node) && !"include".equals(node.getNodeName())) {
+                    final Node id = node.getAttributes().getNamedItem("android:id");
+                    final String className = getFullViewClassName(node);
+                    bundle.createBindingTarget(id.getNodeValue(), className, true, null, null);
+                }
+            }
+        }
+
+        return bundle;
+    }
+
+    private boolean isMergeLayout(Document doc, XPath xPath) throws XPathExpressionException {
+        return !get(doc, xPath, XPATH_MERGE_TAG).isEmpty();
+    }
+
+    private List<Node> getBindingNodes(Document doc, XPath xPath) throws XPathExpressionException {
+        return get(doc, xPath, XPATH_BINDING_ELEMENTS);
+    }
+
+    private List<Node> getVariableNodes(Document doc, XPath xPath) throws XPathExpressionException {
+        return get(doc, xPath, XPATH_VARIABLE_DEFINITIONS);
+    }
+
+    private List<Node> getImportNodes(Document doc, XPath xPath) throws XPathExpressionException {
+        return get(doc, xPath, XPATH_IMPORT_DEFINITIONS);
+    }
+
+    private List<Node> getNakedIds(Document doc, XPath xPath) throws XPathExpressionException {
+        return get(doc, xPath, XPATH_ID_ELEMENTS);
+    }
+
+    private List<Node> get(Document doc, XPath xPath, String pattern)
+            throws XPathExpressionException {
+        final XPathExpression expr = xPath.compile(pattern);
+        return toList((NodeList) expr.evaluate(doc, XPathConstants.NODESET));
+    }
+
+    private List<Node> toList(NodeList nodeList) {
+        List<Node> result = new ArrayList<Node>();
+        for (int i = 0; i < nodeList.getLength(); i ++) {
+            result.add(nodeList.item(i));
+        }
+        return result;
+    }
+
+    private String getFullViewClassName(Node viewNode) {
+        String viewName = viewNode.getNodeName();
+        if ("view".equals(viewName)) {
+            Node classNode = viewNode.getAttributes().getNamedItem("class");
+            if (classNode == null) {
+                L.e("No class attribute for 'view' node");
+            } else {
+                viewName = classNode.getNodeValue();
+            }
+        }
+        if (viewName.indexOf('.') == -1) {
+            if (ObjectUtils.equals(viewName, "View") || ObjectUtils.equals(viewName, "ViewGroup") ||
+                    ObjectUtils.equals(viewName, "ViewStub")) {
+                return "android.view." + viewName;
+            }
+            return "android.widget." + viewName;
+        }
+        return viewName;
+    }
+
+    private void stripBindingTags(File xml, String newTag) throws IOException {
+        String res = XmlEditor.INSTANCE$.strip(xml, newTag);
+        if (res != null) {
+            L.d("file %s has changed, overwriting %s", xml.getName(), xml.getAbsolutePath());
+            FileUtils.writeStringToFile(xml, res);
+        }
+    }
+
+    private File stripFileAndGetOriginal(File xml, String binderId)
+            throws ParserConfigurationException, IOException, SAXException,
+            XPathExpressionException {
+        L.d("parsing resource file %s", xml.getAbsolutePath());
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(xml);
+        XPathFactory xPathFactory = XPathFactory.newInstance();
+        XPath xPath = xPathFactory.newXPath();
+        final XPathExpression commentElementExpr = xPath
+                .compile("//comment()[starts-with(., \" From: file:\")][last()]");
+        final NodeList commentElementNodes = (NodeList) commentElementExpr
+                .evaluate(doc, XPathConstants.NODESET);
+        L.d("comment element nodes count %s", commentElementNodes.getLength());
+        if (commentElementNodes.getLength() == 0) {
+            L.d("cannot find comment element to find the actual file");
+            return null;
+        }
+        final Node first = commentElementNodes.item(0);
+        String actualFilePath = first.getNodeValue().substring(" From: file:".length()).trim();
+        L.d("actual file to parse: %s", actualFilePath);
+        File actualFile = new File(actualFilePath);
+        if (!actualFile.canRead()) {
+            L.d("cannot find original, skipping. %s", actualFile.getAbsolutePath());
+            return null;
+        }
+
+        // now if file has any binding expressions, find and delete them
+        // TODO we should rely on namespace to avoid parsing file twice
+        boolean changed = getVariableNodes(doc, xPath).size() > 0 || getImportNodes(doc, xPath).size() > 0;
+        if (changed) {
+            stripBindingTags(xml, binderId);
+        }
+        return actualFile;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java
new file mode 100644
index 0000000..0cb29e7
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java
@@ -0,0 +1,509 @@
+/*
+ * 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 android.databinding.tool.store;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.util.L;
+import android.databinding.tool.util.ParserHelper;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * This is a serializable class that can keep the result of parsing layout files.
+ */
+public class ResourceBundle implements Serializable {
+
+    private String mAppPackage;
+
+    private HashMap<String, List<LayoutFileBundle>> mLayoutBundles
+            = new HashMap<String, List<LayoutFileBundle>>();
+
+    public ResourceBundle(String appPackage) {
+        mAppPackage = appPackage;
+    }
+
+    public void addLayoutBundle(LayoutFileBundle bundle) {
+        Preconditions.checkArgument(bundle.mFileName != null, "File bundle must have a name");
+        if (!mLayoutBundles.containsKey(bundle.mFileName)) {
+            mLayoutBundles.put(bundle.mFileName, new ArrayList<LayoutFileBundle>());
+        }
+        final List<LayoutFileBundle> bundles = mLayoutBundles.get(bundle.mFileName);
+        for (LayoutFileBundle existing : bundles) {
+            if (existing.equals(bundle)) {
+                L.d("skipping layout bundle %s because it already exists.", bundle);
+                return;
+            }
+        }
+        L.d("adding bundle %s", bundle);
+        bundles.add(bundle);
+    }
+
+    public HashMap<String, List<LayoutFileBundle>> getLayoutBundles() {
+        return mLayoutBundles;
+    }
+
+    public String getAppPackage() {
+        return mAppPackage;
+    }
+
+    public void validateMultiResLayouts() {
+        final Iterable<Map.Entry<String, List<LayoutFileBundle>>> multiResLayouts = Iterables
+                .filter(mLayoutBundles.entrySet(),
+                        new Predicate<Map.Entry<String, List<LayoutFileBundle>>>() {
+                            @Override
+                            public boolean apply(Map.Entry<String, List<LayoutFileBundle>> input) {
+                                return input.getValue().size() > 1;
+                            }
+                        });
+
+        for (Map.Entry<String, List<LayoutFileBundle>> bundles : multiResLayouts) {
+            // validate all ids are in correct view types
+            // and all variables have the same name
+            Map<String, String> variableTypes = new HashMap<String, String>();
+            Map<String, String> importTypes = new HashMap<String, String>();
+
+            for (LayoutFileBundle bundle : bundles.getValue()) {
+                bundle.mHasVariations = true;
+                for (Map.Entry<String, String> variable : bundle.mVariables.entrySet()) {
+                    String existing = variableTypes.get(variable.getKey());
+                    Preconditions
+                            .checkState(existing == null || existing.equals(variable.getValue()),
+                                    "inconsistent variable types for %s for layout %s",
+                                    variable.getKey(), bundle.mFileName);
+                    variableTypes.put(variable.getKey(), variable.getValue());
+                }
+                for (Map.Entry<String, String> userImport : bundle.mImports.entrySet()) {
+                    String existing = importTypes.get(userImport.getKey());
+                    Preconditions
+                            .checkState(existing == null || existing.equals(userImport.getValue()),
+                                    "inconsistent variable types for %s for layout %s",
+                                    userImport.getKey(), bundle.mFileName);
+                    importTypes.put(userImport.getKey(), userImport.getValue());
+                }
+            }
+
+            for (LayoutFileBundle bundle : bundles.getValue()) {
+                // now add missing ones to each to ensure they can be referenced
+                L.d("checking for missing variables in %s / %s", bundle.mFileName,
+                        bundle.mConfigName);
+                for (Map.Entry<String, String> variable : variableTypes.entrySet()) {
+                    if (!bundle.mVariables.containsKey(variable.getKey())) {
+                        bundle.mVariables.put(variable.getKey(), variable.getValue());
+                        L.d("adding missing variable %s to %s / %s", variable.getKey(),
+                                bundle.mFileName, bundle.mConfigName);
+                    }
+                }
+                for (Map.Entry<String, String> userImport : importTypes.entrySet()) {
+                    if (!bundle.mImports.containsKey(userImport.getKey())) {
+                        bundle.mImports.put(userImport.getKey(), userImport.getValue());
+                        L.d("adding missing import %s to %s / %s", userImport.getKey(),
+                                bundle.mFileName, bundle.mConfigName);
+                    }
+                }
+            }
+
+            Set<String> includeBindingIds = new HashSet<String>();
+            Set<String> viewBindingIds = new HashSet<String>();
+            Map<String, String> viewTypes = new HashMap<String, String>();
+            Map<String, String> includes = new HashMap<String, String>();
+            L.d("validating ids for %s", bundles.getKey());
+            for (LayoutFileBundle bundle : bundles.getValue()) {
+                for (BindingTargetBundle target : bundle.mBindingTargetBundles) {
+                    L.d("checking %s %s %s", target.getId(), target.mFullClassName, target.isBinder());
+                    if (target.isBinder()) {
+                        Preconditions.checkState(!viewBindingIds.contains(target.mFullClassName),
+                                "Cannot use the same id for a View and an include tag. Error in "
+                                        + "file %s / %s", bundle.mFileName, bundle.mConfigName);
+                        includeBindingIds.add(target.mFullClassName);
+                    } else {
+                        Preconditions.checkState(!includeBindingIds.contains(target.mFullClassName),
+                                "Cannot use the same id for a View and an include tag. Error in "
+                                        + "file %s / %s", bundle.mFileName, bundle.mConfigName);
+                        viewBindingIds.add(target.mFullClassName);
+                    }
+                    String existingType = viewTypes.get(target.mId);
+                    if (existingType == null) {
+                        L.d("assigning %s as %s", target.getId(), target.mFullClassName);
+                        viewTypes.put(target.mId, target.mFullClassName);
+                        if (target.isBinder()) {
+                            includes.put(target.mId, target.getIncludedLayout());
+                        }
+                    } else if (!existingType.equals(target.mFullClassName)) {
+                        if (target.isBinder()) {
+                            L.d("overriding %s as base binder", target.getId());
+                            viewTypes.put(target.mId,
+                                    "android.databinding.ViewDataBinding");
+                            includes.put(target.mId, target.getIncludedLayout());
+                        } else {
+                            L.d("overriding %s as base view", target.getId());
+                            viewTypes.put(target.mId, "android.view.View");
+                        }
+                    }
+                }
+            }
+
+            for (LayoutFileBundle bundle : bundles.getValue()) {
+                for (Map.Entry<String, String> viewType : viewTypes.entrySet()) {
+                    BindingTargetBundle target = bundle.getBindingTargetById(viewType.getKey());
+                    if (target == null) {
+                        bundle.createBindingTarget(viewType.getKey(), viewType.getValue(), false,
+                                null, null).setIncludedLayout(includes.get(viewType.getKey()));
+                    } else {
+                        L.d("setting interface type on %s (%s) as %s", target.mId, target.mFullClassName, viewType.getValue());
+                        target.setInterfaceType(viewType.getValue());
+                    }
+                }
+            }
+        }
+        // assign class names to each
+        for (Map.Entry<String, List<LayoutFileBundle>> entry : mLayoutBundles.entrySet()) {
+            for (LayoutFileBundle bundle : entry.getValue()) {
+                final String configName;
+                if (bundle.hasVariations()) {
+                    // append configuration specifiers.
+                    final String parentFileName = bundle.mDirectory;
+                    L.d("parent file for %s is %s", bundle.getFileName(), parentFileName);
+                    if ("layout".equals(parentFileName)) {
+                        configName = "";
+                    } else {
+                        configName = ParserHelper.INSTANCE$.toClassName(parentFileName.substring("layout-".length()));
+                    }
+                } else {
+                    configName = "";
+                }
+                bundle.mConfigName = configName;
+            }
+        }
+    }
+
+    @XmlAccessorType(XmlAccessType.NONE)
+    @XmlRootElement(name="Layout")
+    public static class LayoutFileBundle implements Serializable {
+        @XmlAttribute(name="layout", required = true)
+        public String mFileName;
+        @XmlAttribute(name="modulePackage", required = true)
+        public String mModulePackage;
+        private String mConfigName;
+
+        @XmlAttribute(name="directory", required = true)
+        public String mDirectory;
+        public boolean mHasVariations;
+
+        @XmlElement(name="Variables")
+        @XmlJavaTypeAdapter(NameTypeAdapter.class)
+        public Map<String, String> mVariables = new HashMap<String, String>();
+
+        @XmlElement(name="Imports")
+        @XmlJavaTypeAdapter(NameTypeAdapter.class)
+        public Map<String, String> mImports = new HashMap<String, String>();
+
+        @XmlElementWrapper(name="Targets")
+        @XmlElement(name="Target")
+        public List<BindingTargetBundle> mBindingTargetBundles = new ArrayList<BindingTargetBundle>();
+
+        // for XML binding
+        public LayoutFileBundle() {
+        }
+
+        public LayoutFileBundle(String fileName, String directory, String modulePackage) {
+            mFileName = fileName;
+            mDirectory = directory;
+            mModulePackage = modulePackage;
+        }
+
+        public void addVariable(String name, String type) {
+            mVariables.put(name, type);
+        }
+
+        public void addImport(String alias, String type) {
+            mImports.put(alias, type);
+        }
+
+        public BindingTargetBundle createBindingTarget(String id, String fullClassName,
+                boolean used, String tag, String originalTag) {
+            BindingTargetBundle target = new BindingTargetBundle(id, fullClassName, used, tag,
+                    originalTag);
+            mBindingTargetBundles.add(target);
+            return target;
+        }
+
+        public boolean isEmpty() {
+            return mVariables.isEmpty() && mImports.isEmpty() && mBindingTargetBundles.isEmpty();
+        }
+
+        public BindingTargetBundle getBindingTargetById(String key) {
+            for (BindingTargetBundle target : mBindingTargetBundles) {
+                if (key.equals(target.mId)) {
+                    return target;
+                }
+            }
+            return null;
+        }
+
+        public String getFileName() {
+            return mFileName;
+        }
+
+        public String getConfigName() {
+            return mConfigName;
+        }
+
+        public String getDirectory() {
+            return mDirectory;
+        }
+
+        public boolean hasVariations() {
+            return mHasVariations;
+        }
+
+        public Map<String, String> getVariables() {
+            return mVariables;
+        }
+
+        public Map<String, String> getImports() {
+            return mImports;
+        }
+
+        public List<BindingTargetBundle> getBindingTargetBundles() {
+            return mBindingTargetBundles;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            LayoutFileBundle bundle = (LayoutFileBundle) o;
+
+            if (mConfigName != null ? !mConfigName.equals(bundle.mConfigName)
+                    : bundle.mConfigName != null) {
+                return false;
+            }
+            if (mDirectory != null ? !mDirectory.equals(bundle.mDirectory)
+                    : bundle.mDirectory != null) {
+                return false;
+            }
+            if (mFileName != null ? !mFileName.equals(bundle.mFileName)
+                    : bundle.mFileName != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mFileName != null ? mFileName.hashCode() : 0;
+            result = 31 * result + (mConfigName != null ? mConfigName.hashCode() : 0);
+            result = 31 * result + (mDirectory != null ? mDirectory.hashCode() : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "LayoutFileBundle{" +
+                    "mHasVariations=" + mHasVariations +
+                    ", mDirectory='" + mDirectory + '\'' +
+                    ", mConfigName='" + mConfigName + '\'' +
+                    ", mModulePackage='" + mModulePackage + '\'' +
+                    ", mFileName='" + mFileName + '\'' +
+                    '}';
+        }
+
+        public String getModulePackage() {
+            return mModulePackage;
+        }
+    }
+
+    @XmlAccessorType(XmlAccessType.NONE)
+    public static class MarshalledNameType {
+        @XmlAttribute(name="type", required = true)
+        public String type;
+
+        @XmlAttribute(name="name", required = true)
+        public String name;
+    }
+
+    public static class MarshalledMapType {
+        public List<MarshalledNameType> entries;
+    }
+
+    @XmlAccessorType(XmlAccessType.NONE)
+    public static class BindingTargetBundle implements Serializable {
+        // public for XML serialization
+
+        @XmlAttribute(name="id")
+        public String mId;
+        @XmlAttribute(name="tag", required = true)
+        public String mTag;
+        @XmlAttribute(name="originalTag")
+        public String mOriginalTag;
+        @XmlAttribute(name="boundClass", required = true)
+        public String mFullClassName;
+        public boolean mUsed = true;
+        @XmlElementWrapper(name="Expressions")
+        @XmlElement(name="Expression")
+        public List<BindingBundle> mBindingBundleList = new ArrayList<BindingBundle>();
+        @XmlAttribute(name="include")
+        public String mIncludedLayout;
+        private String mInterfaceType;
+
+        // For XML serialization
+        public BindingTargetBundle() {}
+
+        public BindingTargetBundle(String id, String fullClassName, boolean used,
+                String tag, String originalTag) {
+            mId = id;
+            mFullClassName = fullClassName;
+            mUsed = used;
+            mTag = tag;
+            mOriginalTag = originalTag;
+        }
+
+        public void addBinding(String name, String expr) {
+            mBindingBundleList.add(new BindingBundle(name, expr));
+        }
+
+        public void setIncludedLayout(String includedLayout) {
+            mIncludedLayout = includedLayout;
+        }
+
+        public String getIncludedLayout() {
+            return mIncludedLayout;
+        }
+
+        public boolean isBinder() {
+            return mIncludedLayout != null;
+        }
+
+        public void setInterfaceType(String interfaceType) {
+            mInterfaceType = interfaceType;
+        }
+
+        public String getId() {
+            return mId;
+        }
+
+        public String getTag() {
+            return mTag;
+        }
+
+        public String getOriginalTag() {
+            return mOriginalTag;
+        }
+
+        public String getFullClassName() {
+            return mFullClassName;
+        }
+
+        public boolean isUsed() {
+            return mUsed;
+        }
+
+        public List<BindingBundle> getBindingBundleList() {
+            return mBindingBundleList;
+        }
+
+        public String getInterfaceType() {
+            return mInterfaceType;
+        }
+
+        @XmlAccessorType(XmlAccessType.NONE)
+        public static class BindingBundle implements Serializable {
+
+            private String mName;
+            private String mExpr;
+
+            public BindingBundle() {}
+
+            public BindingBundle(String name, String expr) {
+                mName = name;
+                mExpr = expr;
+            }
+
+            @XmlAttribute(name="attribute", required=true)
+            public String getName() {
+                return mName;
+            }
+
+            @XmlAttribute(name="text", required=true)
+            public String getExpr() {
+                return mExpr;
+            }
+
+            public void setName(String name) {
+                mName = name;
+            }
+
+            public void setExpr(String expr) {
+                mExpr = expr;
+            }
+        }
+    }
+
+    private final static class NameTypeAdapter
+            extends XmlAdapter<MarshalledMapType, Map<String, String>> {
+
+        @Override
+        public HashMap<String, String> unmarshal(MarshalledMapType v) throws Exception {
+            HashMap<String, String> map = new HashMap<String, String>();
+            if (v.entries != null) {
+                for (MarshalledNameType entry : v.entries) {
+                    map.put(entry.name, entry.type);
+                }
+            }
+            return map;
+        }
+
+        @Override
+        public MarshalledMapType marshal(Map<String, String> v) throws Exception {
+            if (v.isEmpty()) {
+                return null;
+            }
+            MarshalledMapType marshalled = new MarshalledMapType();
+            marshalled.entries = new ArrayList<MarshalledNameType>();
+            for (String name : v.keySet()) {
+                MarshalledNameType nameType = new MarshalledNameType();
+                nameType.name = name;
+                nameType.type = v.get(name);
+                marshalled.entries.add(nameType);
+            }
+            return marshalled;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
new file mode 100644
index 0000000..3a53c2c
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -0,0 +1,612 @@
+/*
+ * 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 android.databinding.tool.store;
+
+import org.apache.commons.lang3.StringUtils;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.util.GenerationalClassUtil;
+import android.databinding.tool.util.L;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+public class SetterStore {
+
+    public static final String SETTER_STORE_FILE_EXT = "-setter_store.bin";
+
+    private static SetterStore sStore;
+
+    private final IntermediateV1 mStore;
+    private final ModelAnalyzer mClassAnalyzer;
+
+    private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) {
+        mClassAnalyzer = modelAnalyzer;
+        mStore = store;
+    }
+
+    public static SetterStore get(ModelAnalyzer modelAnalyzer) {
+        if (sStore == null) {
+            sStore = load(modelAnalyzer, SetterStore.class.getClassLoader());
+        }
+        return sStore;
+    }
+
+    private static SetterStore load(ModelAnalyzer modelAnalyzer, ClassLoader classLoader) {
+        IntermediateV1 store = new IntermediateV1();
+        List<Intermediate> previousStores = GenerationalClassUtil
+                .loadObjects(classLoader,
+                        new GenerationalClassUtil.ExtensionFilter(SETTER_STORE_FILE_EXT));
+        for (Intermediate intermediate : previousStores) {
+            merge(store, intermediate);
+        }
+        return new SetterStore(modelAnalyzer, store);
+    }
+
+    public void addRenamedMethod(String attribute, String declaringClass, String method,
+            TypeElement declaredOn) {
+        HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
+        if (renamed == null) {
+            renamed = new HashMap<String, MethodDescription>();
+            mStore.renamedMethods.put(attribute, renamed);
+        }
+        MethodDescription methodDescription =
+                new MethodDescription(declaredOn.getQualifiedName().toString(), method);
+        L.d("STORE addmethod desc %s", methodDescription);
+        renamed.put(declaringClass, methodDescription);
+    }
+
+    public void addBindingAdapter(String attribute, ExecutableElement bindingMethod) {
+        L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod);
+        HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
+
+        if (adapters == null) {
+            adapters = new HashMap<AccessorKey, MethodDescription>();
+            mStore.adapterMethods.put(attribute, adapters);
+        }
+        List<? extends VariableElement> parameters = bindingMethod.getParameters();
+        String view = getQualifiedName(parameters.get(0).asType());
+        String value = getQualifiedName(parameters.get(1).asType());
+
+        AccessorKey key = new AccessorKey(view, value);
+        if (adapters.containsKey(key)) {
+            throw new IllegalArgumentException("Already exists!");
+        }
+
+        adapters.put(key, new MethodDescription(bindingMethod));
+    }
+
+    public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) {
+        L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn);
+        String declaredType = declaredOn.getQualifiedName().toString();
+        for (String type : typeNames) {
+            mStore.untaggableTypes.put(type, declaredType);
+        }
+    }
+
+    private static String getQualifiedName(TypeMirror type) {
+        switch (type.getKind()) {
+            case BOOLEAN:
+            case BYTE:
+            case SHORT:
+            case INT:
+            case LONG:
+            case CHAR:
+            case FLOAT:
+            case DOUBLE:
+            case VOID:
+                return type.toString();
+            case ARRAY:
+                return getQualifiedName(((ArrayType) type).getComponentType()) + "[]";
+            case DECLARED:
+                return ((TypeElement) ((DeclaredType) type).asElement()).getQualifiedName()
+                        .toString();
+            default:
+                return "-- no type --";
+        }
+    }
+
+    public void addConversionMethod(ExecutableElement conversionMethod) {
+        L.d("STORE addConversionMethod %s", conversionMethod);
+        List<? extends VariableElement> parameters = conversionMethod.getParameters();
+        String fromType = getQualifiedName(parameters.get(0).asType());
+        String toType = getQualifiedName(conversionMethod.getReturnType());
+        MethodDescription methodDescription = new MethodDescription(conversionMethod);
+        HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType);
+        if (convertTo == null) {
+            convertTo = new HashMap<String, MethodDescription>();
+            mStore.conversionMethods.put(fromType, convertTo);
+        }
+        convertTo.put(toType, methodDescription);
+    }
+
+    public void clear(Set<String> classes) {
+        ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>();
+        for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) {
+            for (AccessorKey key : adapters.keySet()) {
+                MethodDescription description = adapters.get(key);
+                if (classes.contains(description.type)) {
+                    removedAccessorKeys.add(key);
+                }
+            }
+            removeFromMap(adapters, removedAccessorKeys);
+        }
+
+        ArrayList<String> removedRenamed = new ArrayList<String>();
+        for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) {
+            for (String key : renamed.keySet()) {
+                if (classes.contains(renamed.get(key).type)) {
+                    removedRenamed.add(key);
+                }
+            }
+            removeFromMap(renamed, removedRenamed);
+        }
+
+        ArrayList<String> removedConversions = new ArrayList<String>();
+        for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) {
+            for (String toType : convertTos.keySet()) {
+                MethodDescription methodDescription = convertTos.get(toType);
+                if (classes.contains(methodDescription.type)) {
+                    removedConversions.add(toType);
+                }
+            }
+            removeFromMap(convertTos, removedConversions);
+        }
+
+        ArrayList<String> removedUntaggable = new ArrayList<String>();
+        for (String typeName : mStore.untaggableTypes.keySet()) {
+            if (classes.contains(mStore.untaggableTypes.get(typeName))) {
+                removedUntaggable.add(typeName);
+            }
+        }
+        removeFromMap(mStore.untaggableTypes, removedUntaggable);
+    }
+
+    private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) {
+        for (K key : keys) {
+            map.remove(key);
+        }
+        keys.clear();
+    }
+
+    public void write(String projectPackage, ProcessingEnvironment processingEnvironment)
+            throws IOException {
+        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
+                projectPackage, projectPackage + SETTER_STORE_FILE_EXT, mStore);
+    }
+
+    public SetterCall getSetterCall(String attribute, ModelClass viewType,
+            ModelClass valueType, Map<String, String> imports) {
+        if (!attribute.startsWith("android:")) {
+            int colon = attribute.indexOf(':');
+            if (colon >= 0) {
+                attribute = attribute.substring(colon + 1);
+            }
+        }
+        SetterCall setterCall = null;
+        MethodDescription conversionMethod = null;
+        if (viewType != null) {
+            HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
+            ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports);
+            ModelClass bestViewType = null;
+            ModelClass bestValueType = null;
+            if (bestSetterMethod != null) {
+                bestViewType = bestSetterMethod.getDeclaringClass();
+                bestValueType = bestSetterMethod.getParameterTypes()[0];
+                setterCall = new ModelMethodSetter(bestSetterMethod);
+            }
+
+            if (adapters != null) {
+                for (AccessorKey key : adapters.keySet()) {
+                    try {
+                        ModelClass adapterViewType = mClassAnalyzer
+                                .findClass(key.viewType, imports);
+                        if (adapterViewType.isAssignableFrom(viewType)) {
+                            try {
+                                ModelClass adapterValueType = mClassAnalyzer
+                                        .findClass(key.valueType, imports);
+                                boolean isBetterView = bestViewType == null ||
+                                        bestValueType.isAssignableFrom(adapterValueType);
+                                if (isBetterParameter(valueType, adapterValueType, bestValueType,
+                                        isBetterView, imports)) {
+                                    bestViewType = adapterViewType;
+                                    bestValueType = adapterValueType;
+                                    MethodDescription adapter = adapters.get(key);
+                                    setterCall = new AdapterSetter(adapter);
+                                }
+
+                            } catch (Exception e) {
+                                L.e(e, "Unknown class: %s", key.valueType);
+                            }
+                        }
+                    } catch (Exception e) {
+                        L.e(e, "Unknown class: %s", key.viewType);
+                    }
+                }
+            }
+
+            conversionMethod = getConversionMethod(valueType, bestValueType, imports);
+            if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) {
+                setterCall.setCast(bestValueType);
+            }
+        }
+        if (setterCall == null) {
+            setterCall = new DummySetter(getDefaultSetter(attribute));
+            // might be an include tag etc. just note it and continue.
+            L.d("Cannot find the setter for attribute " + attribute + ". might be an include file,"
+                    + " moving on.");
+        }
+        setterCall.setConverter(conversionMethod);
+        return setterCall;
+    }
+
+    public boolean isUntaggable(String viewType) {
+        return mStore.untaggableTypes.containsKey(viewType);
+    }
+
+    private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType,
+            String attribute, Map<String, String> imports) {
+        List<String> setterCandidates = new ArrayList<String>();
+        HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
+        if (renamed != null) {
+            for (String className : renamed.keySet()) {
+                try {
+                    ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports);
+                    if (renamedViewType.isAssignableFrom(viewType)) {
+                        setterCandidates.add(renamed.get(className).method);
+                        break;
+                    }
+                } catch (Exception e) {
+                    //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
+                }
+            }
+        }
+        setterCandidates.add(getDefaultSetter(attribute));
+        setterCandidates.add(trimAttributeNamespace(attribute));
+
+        ModelMethod bestMethod = null;
+        ModelClass bestParameterType = null;
+        List<ModelClass> args = new ArrayList<ModelClass>();
+        args.add(argumentType);
+        for (String name : setterCandidates) {
+            ModelMethod[] methods = viewType.getMethods(name, 1);
+
+            for (ModelMethod method : methods) {
+                ModelClass[] parameterTypes = method.getParameterTypes();
+                ModelClass param = parameterTypes[0];
+                if (method.isVoid() &&
+                        isBetterParameter(argumentType, param, bestParameterType, true, imports)) {
+                    bestParameterType = param;
+                    bestMethod = method;
+                }
+            }
+        }
+        return bestMethod;
+
+    }
+
+    private static String trimAttributeNamespace(String attribute) {
+        final int colonIndex = attribute.indexOf(':');
+        return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1);
+    }
+
+    private static String getDefaultSetter(String attribute) {
+        return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute));
+    }
+
+    private boolean isBetterParameter(ModelClass argument, ModelClass parameter,
+            ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) {
+        // Right view type. Check the value
+        if (!isBetterViewTypeMatch && oldParameter.equals(argument)) {
+            return false;
+        } else if (argument.equals(parameter)) {
+            // Exact match
+            return true;
+        } else if (!isBetterViewTypeMatch &&
+                ModelMethod.isBoxingConversion(oldParameter, argument)) {
+            return false;
+        } else if (ModelMethod.isBoxingConversion(parameter, argument)) {
+            // Boxing/unboxing is second best
+            return true;
+        } else {
+            int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter);
+            if (ModelMethod.isImplicitConversion(argument, parameter)) {
+                // Better implicit conversion
+                int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter);
+                return oldConversionLevel < 0 || conversionLevel < oldConversionLevel;
+            } else if (oldConversionLevel >= 0) {
+                return false;
+            } else if (parameter.isAssignableFrom(argument)) {
+                // Right type, see if it is better than the current best match.
+                if (oldParameter == null) {
+                    return true;
+                } else {
+                    return oldParameter.isAssignableFrom(parameter);
+                }
+            } else {
+                MethodDescription conversionMethod = getConversionMethod(argument, parameter,
+                        imports);
+                if (conversionMethod != null) {
+                    return true;
+                }
+                if (getConversionMethod(argument, oldParameter, imports) != null) {
+                    return false;
+                }
+                return argument.isObject() && !parameter.isPrimitive();
+            }
+        }
+    }
+
+    private MethodDescription getConversionMethod(ModelClass from, ModelClass to,
+            Map<String, String> imports) {
+        if (from != null && to != null) {
+            for (String fromClassName : mStore.conversionMethods.keySet()) {
+                try {
+                    ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports);
+                    if (canUseForConversion(from, convertFrom)) {
+                        HashMap<String, MethodDescription> conversion =
+                                mStore.conversionMethods.get(fromClassName);
+                        for (String toClassName : conversion.keySet()) {
+                            try {
+                                ModelClass convertTo = mClassAnalyzer.findClass(toClassName,
+                                        imports);
+                                if (canUseForConversion(convertTo, to)) {
+                                    return conversion.get(toClassName);
+                                }
+                            } catch (Exception e) {
+                                L.d(e, "Unknown class: %s", toClassName);
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    L.d(e, "Unknown class: %s", fromClassName);
+                }
+            }
+        }
+        return null;
+    }
+
+    private boolean canUseForConversion(ModelClass from, ModelClass to) {
+        return from.equals(to) || ModelMethod.isBoxingConversion(from, to) ||
+                to.isAssignableFrom(from);
+    }
+
+    private static void merge(IntermediateV1 store, Intermediate dumpStore) {
+        IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade();
+        merge(store.adapterMethods, intermediateV1.adapterMethods);
+        merge(store.renamedMethods, intermediateV1.renamedMethods);
+        merge(store.conversionMethods, intermediateV1.conversionMethods);
+        store.untaggableTypes.putAll(intermediateV1.untaggableTypes);
+    }
+
+    private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first,
+            HashMap<K, HashMap<V, MethodDescription>> second) {
+        for (K key : second.keySet()) {
+            HashMap<V, MethodDescription> firstVals = first.get(key);
+            HashMap<V, MethodDescription> secondVals = second.get(key);
+            if (firstVals == null) {
+                first.put(key, secondVals);
+            } else {
+                for (V key2 : secondVals.keySet()) {
+                    if (!firstVals.containsKey(key2)) {
+                        firstVals.put(key2, secondVals.get(key2));
+                    }
+                }
+            }
+        }
+    }
+
+    private static class MethodDescription implements Serializable {
+
+        private static final long serialVersionUID = 1;
+
+        public final String type;
+
+        public final String method;
+
+        public MethodDescription(String type, String method) {
+            this.type = type;
+            this.method = method;
+            L.d("BINARY created method desc 1 %s %s", type, method );
+        }
+
+        public MethodDescription(ExecutableElement method) {
+            TypeElement enclosingClass = (TypeElement) method.getEnclosingElement();
+            this.type = enclosingClass.getQualifiedName().toString();
+            this.method = method.getSimpleName().toString();
+            L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof MethodDescription) {
+                MethodDescription that = (MethodDescription) obj;
+                return that.type.equals(this.type) && that.method.equals(this.method);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type, method);
+        }
+
+        @Override
+        public String toString() {
+            return type + "." + method + "()";
+        }
+    }
+
+    private static class AccessorKey implements Serializable {
+
+        private static final long serialVersionUID = 1;
+
+        public final String viewType;
+
+        public final String valueType;
+
+        public AccessorKey(String viewType, String valueType) {
+            this.viewType = viewType;
+            this.valueType = valueType;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(viewType, valueType);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof AccessorKey) {
+                AccessorKey that = (AccessorKey) obj;
+                return viewType.equals(that.valueType) && valueType.equals(that.valueType);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "AK(" + viewType + ", " + valueType + ")";
+        }
+    }
+
+    private interface Intermediate extends Serializable {
+        Intermediate upgrade();
+    }
+
+    private static class IntermediateV1 implements Serializable, Intermediate {
+        private static final long serialVersionUID = 1;
+        public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods =
+                new HashMap<String, HashMap<AccessorKey, MethodDescription>>();
+        public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods =
+                new HashMap<String, HashMap<String, MethodDescription>>();
+        public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods =
+                new HashMap<String, HashMap<String, MethodDescription>>();
+        public final HashMap<String, String> untaggableTypes = new HashMap<String, String>();
+
+        public IntermediateV1() {
+        }
+
+        @Override
+        public Intermediate upgrade() {
+            return this;
+        }
+    }
+
+    public static class DummySetter extends SetterCall {
+        private String mMethodName;
+
+        public DummySetter(String methodName) {
+            mMethodName = methodName;
+        }
+
+        @Override
+        public String toJavaInternal(String viewExpression, String valueExpression) {
+            return viewExpression + "." + mMethodName + "(" + valueExpression + ")";
+        }
+
+        @Override
+        public int getMinApi() {
+            return 1;
+        }
+    }
+
+    public static class AdapterSetter extends SetterCall {
+        final MethodDescription mAdapter;
+
+        public AdapterSetter(MethodDescription adapter) {
+            mAdapter = adapter;
+        }
+
+        @Override
+        public String toJavaInternal(String viewExpression, String valueExpression) {
+            return mAdapter.type + "." + mAdapter.method + "(" + viewExpression + ", " +
+                    mCastString + valueExpression + ")";
+        }
+
+        @Override
+        public int getMinApi() {
+            return 1;
+        }
+    }
+
+    public static class ModelMethodSetter extends SetterCall {
+        final ModelMethod mModelMethod;
+
+        public ModelMethodSetter(ModelMethod modelMethod) {
+            mModelMethod = modelMethod;
+        }
+
+        @Override
+        public String toJavaInternal(String viewExpression, String valueExpression) {
+            return viewExpression + "." + mModelMethod.getName() + "(" + mCastString +
+                    valueExpression + ")";
+        }
+
+        @Override
+        public int getMinApi() {
+            return mModelMethod.getMinApi();
+        }
+    }
+
+    public static abstract class SetterCall {
+        private MethodDescription mConverter;
+        protected String mCastString = "";
+
+        public SetterCall() {
+        }
+
+        public void setConverter(MethodDescription converter) {
+            mConverter = converter;
+        }
+
+        protected abstract String toJavaInternal(String viewExpression, String converted);
+
+        public final String toJava(String viewExpression, String valueExpression) {
+            return toJavaInternal(viewExpression, convertValue(valueExpression));
+        }
+
+        protected String convertValue(String valueExpression) {
+            return mConverter == null ? valueExpression :
+                    mConverter.type + "." + mConverter.method + "(" + valueExpression + ")";
+        }
+
+        abstract public int getMinApi();
+
+        public void setCast(ModelClass castTo) {
+            mCastString = "(" + castTo.toJavaCode() + ") ";
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java
new file mode 100644
index 0000000..e7373ac
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.databinding.tool.util;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+/**
+ * A utility class that helps adding build specific objects to the jar file
+ * and their extraction later on.
+ */
+public class GenerationalClassUtil {
+    public static <T extends Serializable> List<T> loadObjects(ClassLoader classLoader, Filter filter) {
+        final List<T> result = new ArrayList<T>();
+        if (!(classLoader instanceof URLClassLoader)) {
+            L.d("class loader is not url class loader (%s). skipping.", classLoader.getClass());
+            return result;
+        }
+        final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+        for (URL url : urlClassLoader.getURLs()) {
+            L.d("checking url %s for intermediate data", url);
+            try {
+                final File file = new File(url.toURI());
+                if (!file.exists()) {
+                    L.d("cannot load file for %s", url);
+                    continue;
+                }
+                if (file.isDirectory()) {
+                    // probably exported classes dir.
+                    loadFromDirectory(filter, result, file);
+                } else {
+                    // assume it is a zip file
+                    loadFomZipFile(filter, result, file);
+                }
+            } catch (IOException e) {
+                L.d("cannot open zip file from %s", url);
+            } catch (URISyntaxException e) {
+                L.d("cannot open zip file from %s", url);
+            }
+        }
+        return result;
+    }
+
+    private static <T extends Serializable> void loadFromDirectory(final Filter filter, List<T> result,
+            File directory) {
+        //noinspection unchecked
+        Collection<File> files = FileUtils.listFiles(directory, new IOFileFilter() {
+            @Override
+            public boolean accept(File file) {
+                return filter.accept(file.getName());
+            }
+
+            @Override
+            public boolean accept(File dir, String name) {
+                return filter.accept(name);
+            }
+        }, TrueFileFilter.INSTANCE);
+        for (File file : files) {
+            InputStream inputStream = null;
+            try {
+                inputStream = FileUtils.openInputStream(file);
+                T item = fromInputStream(result, inputStream);
+                L.d("loaded item %s from file", item);
+                if (item != null) {
+                    result.add(item);
+                }
+            } catch (IOException e) {
+                L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
+            } catch (ClassNotFoundException e) {
+                L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath());
+            } finally {
+                IOUtils.closeQuietly(inputStream);
+            }
+        }
+    }
+
+    private static <T extends Serializable> void loadFomZipFile(Filter filter,
+            List<T> result, File file) throws IOException {
+        ZipFile zipFile = new ZipFile(file);
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            if (!filter.accept(entry.getName())) {
+                continue;
+            }
+            L.d("loading data from file %s", entry.getName());
+            InputStream inputStream = null;
+            try {
+                inputStream = zipFile.getInputStream(entry);
+                T item = fromInputStream(result, inputStream);
+                L.d("loaded item %s from zip file", item);
+                if (item != null) {
+                    result.add(item);
+                }
+            } catch (IOException e) {
+                L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
+            } catch (ClassNotFoundException e) {
+                L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath());
+            } finally {
+                IOUtils.closeQuietly(inputStream);
+            }
+        }
+    }
+
+    private static <T extends Serializable> T fromInputStream(List<T> result,
+            InputStream inputStream) throws IOException, ClassNotFoundException {
+        ObjectInputStream in = new ObjectInputStream(inputStream);
+        return (T) in.readObject();
+
+    }
+
+    public static void writeIntermediateFile(ProcessingEnvironment processingEnv,
+            String packageName, String fileName, Serializable object) {
+        ObjectOutputStream oos = null;
+        try {
+            FileObject intermediate = processingEnv.getFiler().createResource(
+                    StandardLocation.CLASS_OUTPUT, packageName,
+                    fileName);
+            OutputStream ios = intermediate.openOutputStream();
+            oos = new ObjectOutputStream(ios);
+            oos.writeObject(object);
+            oos.close();
+            L.d("wrote intermediate bindable file %s %s", packageName, fileName);
+        } catch (IOException e) {
+            L.e(e, "Could not write to intermediate file: %s", fileName);
+        } finally {
+            IOUtils.closeQuietly(oos);
+        }
+    }
+
+
+    public static interface Filter {
+        public boolean accept(String entryName);
+    }
+
+    public static class ExtensionFilter implements Filter {
+        private final String mExtension;
+        public ExtensionFilter(String extension) {
+            mExtension = extension;
+        }
+
+        @Override
+        public boolean accept(String entryName) {
+            return entryName.endsWith(mExtension);
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java
new file mode 100644
index 0000000..5439f59
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.databinding.tool.util;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
+
+import javax.tools.Diagnostic;
+
+public class L {
+
+    public static void d(String msg, Object... args) {
+        printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
+    }
+
+    public static void d(Throwable t, String msg, Object... args) {
+        printMessage(Diagnostic.Kind.NOTE,
+                String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t));
+    }
+
+    public static void e(String msg, Object... args) {
+        printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
+    }
+
+    public static void e(Throwable t, String msg, Object... args) {
+        printMessage(Diagnostic.Kind.ERROR,
+                String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t));
+    }
+
+    private static void printMessage(Diagnostic.Kind kind, String message) {
+        ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+        System.out.println("[" + kind.name() + "]: " + message);
+        if (modelAnalyzer instanceof AnnotationAnalyzer) {
+            ((AnnotationAnalyzer) modelAnalyzer).getProcessingEnv().getMessager()
+                    .printMessage(kind, message);
+            if (kind == Diagnostic.Kind.ERROR) {
+                throw new RuntimeException("failure, see logs for details.\n" + message);
+            }
+        } else {
+
+            if (kind == Diagnostic.Kind.ERROR) {
+                throw new RuntimeException(message);
+            }
+        }
+    }
+
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java
new file mode 100644
index 0000000..4089905
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.databinding.tool.writer;
+
+import org.apache.commons.io.IOUtils;
+
+import android.databinding.tool.util.L;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.tools.JavaFileObject;
+
+public class AnnotationJavaFileWriter extends JavaFileWriter {
+    private final ProcessingEnvironment mProcessingEnvironment;
+
+    public AnnotationJavaFileWriter(ProcessingEnvironment processingEnvironment) {
+        mProcessingEnvironment = processingEnvironment;
+    }
+
+    @Override
+    public void writeToFile(String canonicalName, String contents) {
+        Writer writer = null;
+        try {
+            L.d("writing file %s", canonicalName);
+            JavaFileObject javaFileObject =
+                    mProcessingEnvironment.getFiler().createSourceFile(canonicalName);
+            writer = javaFileObject.openWriter();
+            writer.write(contents);
+        } catch (IOException e) {
+            L.e(e, "Could not write to %s", canonicalName);
+        } finally {
+            if (writer != null) {
+                IOUtils.closeQuietly(writer);
+            }
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java
new file mode 100644
index 0000000..a5e799e
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java
@@ -0,0 +1,142 @@
+/*
+ * 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 android.databinding.tool.writer;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Used for code generation. A BitSet can be converted into a flag set,
+ * which is basically a list of longs that can be divided into pieces.
+ */
+public class FlagSet {
+    public static final int sBucketSize = 64;// long
+    public final String type;
+    public final long[] buckets;
+    private String mLocalName;
+    private boolean mIsDynamic = false;
+
+    public FlagSet(BitSet bitSet, int bucketCount) {
+        buckets = new long[bucketCount];
+        for (int i = bitSet.nextSetBit(0);
+                i != -1; i = bitSet.nextSetBit(i + 1)) {
+            buckets[i / sBucketSize] |= 1 << (i % sBucketSize);
+        }
+        type = "long";
+    }
+
+    public FlagSet(long[] buckets) {
+        this.buckets = new long[buckets.length];
+        System.arraycopy(buckets, 0, this.buckets, 0, buckets.length);
+        type = "long";
+    }
+
+    public FlagSet(long[] buckets, int minBucketCount) {
+        this.buckets = new long[Math.max(buckets.length, minBucketCount)];
+        System.arraycopy(buckets, 0, this.buckets, 0, buckets.length);
+        type = "long";
+    }
+
+    public FlagSet(int... bits) {
+        int max = 0;
+        for (int i = 0 ; i < bits.length; i ++) {
+            max = Math.max(i, bits[i]);
+        }
+        buckets = new long[1 + (max / sBucketSize)];
+        for (int x = 0 ; x < bits.length; x ++) {
+            final int i = bits[x];
+            buckets[i / sBucketSize] |= 1 << (i % sBucketSize);
+        }
+        type = "long";
+    }
+
+    public boolean intersect(FlagSet other, int bucketIndex) {
+        return (buckets[bucketIndex] & other.buckets[bucketIndex]) != 0;
+    }
+
+    public String getLocalName() {
+        return mLocalName;
+    }
+
+    public void setLocalName(String localName) {
+        mLocalName = localName;
+    }
+
+    public boolean hasLocalName() {
+        return mLocalName != null;
+    }
+
+    public boolean isDynamic() {
+        return mIsDynamic;
+    }
+
+    public void setDynamic(boolean isDynamic) {
+        mIsDynamic = isDynamic;
+    }
+
+    public FlagSet andNot(FlagSet other) {
+        FlagSet result = new FlagSet(buckets);
+        final int min = Math.min(buckets.length, other.buckets.length);
+        for (int i = 0; i < min; i ++) {
+            result.buckets[i] &= ~(other.buckets[i]);
+        }
+        return result;
+    }
+
+    public FlagSet or(FlagSet other) {
+        final FlagSet result = new FlagSet(buckets, other.buckets.length);
+        for (int i = 0; i < other.buckets.length; i ++) {
+            result.buckets[i] |= other.buckets[i];
+        }
+        return result;
+    }
+
+    public boolean isEmpty() {
+        for (int i = 0; i < buckets.length; i ++) {
+            if (buckets[i] != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < buckets.length; i ++) {
+            sb.append(Long.toBinaryString(buckets[i])).append(" ");
+        }
+        return sb.toString();
+    }
+
+    private long getBucket(int bucketIndex) {
+        if (bucketIndex >= buckets.length) {
+            return 0;
+        }
+        return buckets[bucketIndex];
+    }
+
+    public boolean bitsEqual(FlagSet other) {
+        final int max = Math.max(buckets.length, other.buckets.length);
+        for (int i = 0; i < max; i ++) {
+            if (getBucket(i) != other.getBucket(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java
new file mode 100644
index 0000000..d4e0565
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java
@@ -0,0 +1,35 @@
+/*
+ * 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 android.databinding.tool.writer;
+
+import org.apache.commons.io.FileUtils;
+
+import android.databinding.tool.util.L;
+
+import java.io.File;
+import java.io.IOException;
+
+public abstract class JavaFileWriter {
+    public abstract void writeToFile(String canonicalName, String contents);
+    public void writeToFile(File exactPath, String contents) {
+        File parent = exactPath.getParentFile();
+        parent.mkdirs();
+        try {
+            L.d("writing file %s", exactPath.getAbsoluteFile());
+            FileUtils.writeStringToFile(exactPath, contents);
+        } catch (IOException e) {
+            L.e(e, "Could not write to %s", exactPath);
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt
new file mode 100644
index 0000000..9d950b1
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt
@@ -0,0 +1,80 @@
+/*
+ * 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 android.databinding.tool.ext
+
+import kotlin.properties.ReadOnlyProperty
+import kotlin.properties.Delegates
+import android.databinding.tool.ext.joinToCamelCase
+import android.databinding.tool.ext.joinToCamelCaseAsVar
+import android.databinding.tool.reflection.ModelAnalyzer
+import android.databinding.tool.reflection.ModelClass
+import android.databinding.tool.reflection.ModelAnalyzer
+
+private class LazyExt<K, T>(private val initializer: (k : K) -> T) : ReadOnlyProperty<K, T> {
+    private val mapping = hashMapOf<K, T>()
+    override fun get(thisRef: K, desc: PropertyMetadata): T {
+        val t = mapping.get(thisRef)
+        if (t != null) {
+            return t
+        }
+        val result = initializer(thisRef)
+        mapping.put(thisRef, result)
+        return result
+    }
+}
+
+fun Delegates.lazy<K, T>(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = LazyExt(initializer)
+
+public fun Class<*>.toJavaCode() : String {
+    val name = getName();
+    if (name.startsWith('[')) {
+        val numArray = name.lastIndexOf('[') + 1;
+        val componentType : String;
+        when (name.charAt(numArray)) {
+            'Z' -> componentType = "boolean"
+            'B' -> componentType = "byte"
+            'C' -> componentType = "char"
+            'L' -> componentType = name.substring(numArray + 1, name.length() - 1).replace('$', '.');
+            'D' -> componentType = "double"
+            'F' -> componentType = "float"
+            'I' -> componentType = "int"
+            'J' -> componentType = "long"
+            'S' -> componentType = "short"
+            else -> componentType = name.substring(numArray)
+        }
+        val arrayComp = name.substring(0, numArray).replace("[", "[]");
+        return componentType + arrayComp;
+    } else {
+        return name.replace("$", ".")
+    }
+}
+
+public fun String.androidId() : String = this.split("/")[1]
+
+public fun String.toCamelCase() : String {
+    val split = this.split("_")
+    if (split.size == 0) return ""
+    if (split.size == 1) return split[0].capitalize()
+    return split.joinToCamelCase()
+}
+
+public fun String.toCamelCaseAsVar() : String {
+    val split = this.split("_")
+    if (split.size == 0) return ""
+    if (split.size == 1) return split[0]
+    return split.joinToCamelCaseAsVar()
+}
+
+public fun String.br() : String =
+    "BR.${if (this == "") "_all" else this}"
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt
new file mode 100644
index 0000000..12c8af2
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.ext
+
+import android.databinding.tool.ext.toCamelCase
+import android.databinding.tool.ext.toCamelCaseAsVar
+
+public fun List<String>.joinToCamelCase(): String = when(size()) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this.get(0).toCamelCase()
+    else -> this.map {it.toCamelCase()}.joinToString("")
+}
+
+public fun List<String>.joinToCamelCaseAsVar(): String = when(size()) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this.get(0).toCamelCaseAsVar()
+    else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase()
+}
+
+public fun Array<String>.joinToCamelCase(): String = when(size) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this.get(0).toCamelCase()
+    else -> this.map {it.toCamelCase()}.joinToString("")
+}
+
+public fun Array<String>.joinToCamelCaseAsVar(): String = when(size) {
+    0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
+    1 -> this.get(0).toCamelCaseAsVar()
+    else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase()
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt
new file mode 100644
index 0000000..089e3c8
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.ext
+
+import org.w3c.dom.Node
+import org.w3c.dom.NodeList
+import java.util.ArrayList
+
+public fun Node.getAndroidId() : String? =
+    getAttributes()?.getNamedItem("android:id")?.getNodeValue()
+
+public fun Node.getAndroidIdPath(includeRoot : Boolean) : List<String> {
+    val ids = arrayListOf<String>()
+    ids.add(getAndroidId()!!)
+    var parent : Node? = getParentNode()
+    while (parent != null && (includeRoot || parent?.getParentNode()?.getParentNode() != null)) {
+        val id = parent?.getAndroidId()
+        if (id != null) {
+            ids.add(id)
+        }
+        parent = parent?.getParentNode()
+    }
+    return ids
+}
+
+public fun NodeList.forEach( f : (Node) -> Unit ) {
+    val cnt = getLength()
+    if (cnt == 0) return
+    for (i in 0..cnt - 1) {
+        f(item(i))
+    }
+}
+public fun NodeList.toArrayList() : ArrayList<Node> {
+    val cnt = getLength()
+    val arrayList = arrayListOf<Node>()
+    for (i in 0..cnt - 1) {
+        arrayList.add(item(i))
+    }
+    return arrayList
+}
+
+
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt
new file mode 100644
index 0000000..fb6eb35
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 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.util
+
+object Log {
+    fun d(s : String) {
+        System.out.println("[debug] $s")
+    }
+
+    fun d(f : () -> String) {
+        System.out.println("[debug] ${f()}")
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt
new file mode 100644
index 0000000..6972448
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.tool.util
+
+object ParserHelper {
+    public fun toClassName(name:String) : String  {
+
+        return stripExtension(name).split("[_-]").map { it.capitalize() }.join("")
+    }
+
+
+    public fun stripExtension(name : String) : String {
+        val dot = name.indexOf(".")
+        return if (dot == -1) name else name.substring(0, name.indexOf("."))
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt
new file mode 100644
index 0000000..bea9df5
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 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.util
+
+import java.io.File
+import org.antlr.v4.runtime.ANTLRInputStream
+import java.io.FileReader
+import android.databinding.parser.XMLLexer
+import org.antlr.v4.runtime.CommonTokenStream
+import android.databinding.parser.XMLParser
+import android.databinding.parser.log
+import android.databinding.parser.XMLParserBaseVisitor
+import android.databinding.parser.Position
+import android.databinding.parser.toPosition
+import android.databinding.parser.toEndPosition
+import java.util.Comparator
+import com.google.common.base.Preconditions
+import java.util.ArrayList
+
+/**
+ * Ugly inefficient class to strip unwanted tags from XML.
+ * Band-aid solution to unblock development
+ */
+object XmlEditor {
+    val reservedElementNames = arrayListOf("variable", "import")
+    var rootNodeContext: XMLParser.ElementContext? = null
+    var rootNodeHasTag = false
+    data class LayoutXmlElements(val start: Position, val end: Position,
+                                 val insertionPoint: Position,
+                                 val isTag: kotlin.Boolean, val isReserved: kotlin.Boolean,
+                                 val attributes: MutableList<LayoutXmlElements>?)
+
+    val visitor = object : XMLParserBaseVisitor<MutableList<LayoutXmlElements>>() {
+        override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList<LayoutXmlElements>? {
+            log { "attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()} . parent: ${ctx.getParent()}" }
+            if (ctx.getParent() == rootNodeContext && ctx.attrName.getText() == "android:tag") {
+                rootNodeHasTag = true
+            }
+            val isTag = ctx.attrName.getText().equals("android:tag")
+            if (isTag || ctx.attrName.getText().startsWith("bind:") ||
+                    (ctx.attrValue.getText().startsWith("\"@{") && ctx.attrValue.getText().endsWith("}\"")) ||
+                    (ctx.attrValue.getText().startsWith("'@{") && ctx.attrValue.getText().endsWith("}'"))) {
+                return arrayListOf(LayoutXmlElements(ctx.getStart().toPosition(),
+                    ctx.getStop().toEndPosition(), ctx.getStart().toPosition(), isTag, false, null))
+            }
+
+            //log{"visiting attr: ${ctx.getText()} at location ${ctx.getStart().toS()} ${ctx.getStop().toS()}"}
+            return super<XMLParserBaseVisitor>.visitAttribute(ctx)
+        }
+
+        override fun visitElement(ctx: XMLParser.ElementContext): MutableList<LayoutXmlElements>? {
+            log { "elm ${ctx.elmName.getText()} || ${ctx.Name()} paren : ${ctx.getParent()}" }
+            if (rootNodeContext == null) {
+                rootNodeContext = ctx
+            }
+            val insertionPoint : Position
+            if (ctx.content() == null) {
+                insertionPoint = ctx.getStop().toPosition()
+                insertionPoint.charIndex;
+            } else {
+                insertionPoint = ctx.content().getStart().toPosition()
+                insertionPoint.charIndex -= 2;
+            }
+            if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) {
+                return arrayListOf(LayoutXmlElements(ctx.getStart().toPosition(),
+                        ctx.getStop().toEndPosition(), insertionPoint, false, true, arrayListOf()));
+            }
+            val elements = super<XMLParserBaseVisitor>.visitElement(ctx);
+            if (elements != null && !elements.isEmpty()) {
+                val attributes : MutableList<LayoutXmlElements> = arrayListOf();
+                val others : MutableList<LayoutXmlElements> = arrayListOf();
+                elements.forEach {
+                    if (it.attributes == null) {
+                        attributes.add(it);
+                    } else {
+                        others.add(it);
+                    }
+                }
+                if (attributes.isEmpty()) {
+                    return elements;
+                } else {
+                    val element = LayoutXmlElements(ctx.getStart().toPosition(),
+                            ctx.getStop().toEndPosition(), insertionPoint, false, false, attributes)
+                    others.add(0, element);
+                    return others;
+                }
+            } else {
+                return elements;
+            }
+        }
+
+        override fun defaultResult(): MutableList<LayoutXmlElements>? = arrayListOf()
+
+        override fun aggregateResult(aggregate: MutableList<LayoutXmlElements>?, nextResult: MutableList<LayoutXmlElements>?): MutableList<LayoutXmlElements>? =
+                if (aggregate == null) {
+                    nextResult
+                } else if (nextResult == null) {
+                    aggregate
+                } else {
+                    aggregate.addAll(nextResult)
+                    aggregate
+                }
+    }
+
+    fun strip(f: File, newTag: String? = null): String? {
+        rootNodeContext = null //clear it
+        rootNodeHasTag = false
+        val inputStream = ANTLRInputStream(FileReader(f))
+        val lexer = XMLLexer(inputStream)
+        val tokenStream = CommonTokenStream(lexer)
+        val parser = XMLParser(tokenStream)
+        val expr = parser.document()
+        val parsedExpr = expr.accept(visitor)
+        if (parsedExpr.isEmpty()) {
+            return null//nothing to strip
+        }
+        Preconditions.checkNotNull(rootNodeContext, "Cannot find root node for ${f.getName()}")
+        Preconditions.checkState(rootNodeHasTag == false, """You cannot set a tag in the layout
+        root if you are using binding. Invalid file: ${f}""")
+        val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(),
+                rootNodeContext!!.getStop().toEndPosition())
+
+        log { "root node bounds: ${rootNodeBounds}" }
+        val out = StringBuilder()
+        val lines = f.readLines("utf-8")
+
+        lines.forEach { out.appendln(it) }
+
+        // TODO we probably don't need to sort
+        val sorted = parsedExpr.sortBy(object : Comparator<LayoutXmlElements> {
+            override fun compare(o1: LayoutXmlElements, o2: LayoutXmlElements): Int {
+                val lineCmp = o1.start.line.compareTo(o2.start.charIndex)
+                if (lineCmp != 0) {
+                    return lineCmp
+                }
+                return o1.start.line.compareTo(o2.start.charIndex)
+            }
+        })
+        var lineStarts = arrayListOf(0)
+        lines.withIndex().forEach {
+            if (it.index > 0) {
+                lineStarts.add(lineStarts[it.index - 1] + lines[it.index - 1].length() + 1)
+            }
+        }
+
+        val separator = System.lineSeparator().charAt(0)
+        val noTag : ArrayList<Pair<String, LayoutXmlElements>> = ArrayList()
+        var bindingIndex = 0
+        val rootNodeEnd = toIndex(lineStarts, rootNodeContext!!.content().getStart().toPosition())
+        sorted.forEach {
+            if (it.isReserved) {
+                log {"Replacing reserved tag at ${it.start} to ${it.end}"}
+                replace(out, lineStarts, it, "", separator);
+            } else if (it.attributes != null) {
+                log {"Found attribute for tag at ${it.start} to ${it.end}"}
+                if (it.attributes.size() == 1 && it.attributes.get(0).isTag) {
+                    log {"only android:tag"}
+                    // no binding, just tag -- don't replace anything
+                } else {
+                    var replaced = false
+                    val tag : String
+                    if (toIndex(lineStarts, it.start) < rootNodeEnd) {
+                        tag = ""
+                    } else {
+                        val index = bindingIndex++;
+                        tag = "android:tag=\"bindingTag${index}\"";
+                    }
+                    it.attributes.forEach {
+                        if (!replaced && tagWillFit(it.start, it.end, tag)) {
+                            replace(out, lineStarts, it, tag, separator)
+                            replaced = true;
+                        } else {
+                            replace(out, lineStarts, it, "", separator)
+                        }
+                    }
+                    if (!replaced && !tag.isEmpty()) {
+                        log {"Could not find place for ${tag}"}
+                        noTag.add(0, Pair(tag, it))
+                    }
+                }
+            }
+        }
+
+        noTag.forEach {
+            val element = it.second
+            val tag = it.first;
+
+            val insertionPoint = toIndex(lineStarts, element.insertionPoint)
+            out.insert(insertionPoint, " ${tag}")
+        }
+
+        Log.d{"new tag to set: $newTag"}
+        if (newTag != null) {
+            Preconditions.checkState(rootNodeBounds.first.line != rootNodeBounds.second.line,
+                    """The root tag should be multi line to add the tag. ${f.getName()}""")
+            val line = rootNodeBounds.first.line
+            out.insert(lineStarts[line] + lines[line].length(), """ android:tag = "$newTag" """)
+        }
+
+
+        return out.toString()
+    }
+
+    fun tagWillFit(start: Position, end: Position, tag: String) : kotlin.Boolean {
+        if (start.line != end.line) {
+            return end.charIndex >= tag.length();
+        }
+        return (end.charIndex - start.charIndex >= tag.length());
+    }
+
+    fun replace(out : StringBuilder, lineStarts : ArrayList<kotlin.Int>, element : LayoutXmlElements,
+                tag : String, separator : kotlin.Char) {
+        val posStart = toIndex(lineStarts, element.start)
+        val posEnd = toIndex(lineStarts, element.end)
+        log {"replacing '${out.substring(posStart, posEnd)}' with '${tag}'"}
+        val spaceEnd = posEnd - tag.length();
+        for ( i in posStart..(spaceEnd - 1)) {
+            if (out.charAt(i) != separator) {
+                out.setCharAt(i, ' ')
+            }
+        }
+        out.replace(spaceEnd, posEnd, tag);
+    }
+
+    fun toIndex(lineStarts : ArrayList<kotlin.Int>, pos : Position) : kotlin.Int {
+        return lineStarts[pos.line] + pos.charIndex;
+    }
+}
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt
new file mode 100644
index 0000000..f7afc93
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt
@@ -0,0 +1,60 @@
+/*
+ * 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 android.databinding.tool.writer
+
+import android.databinding.tool.LayoutBinder
+
+class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String,
+        val layoutBinders : List<LayoutBinder>, val minSdk : kotlin.Int) {
+    fun write() =
+            kcode("") {
+                nl("package $pkg;")
+                nl("import $projectPackage.BR;")
+                nl("class $className {") {
+                    tab("public android.databinding.ViewDataBinding getDataBinder(android.view.View view, int layoutId) {") {
+                        tab("switch(layoutId) {") {
+                            layoutBinders.groupBy{it.getLayoutname()}.forEach {
+                                tab("case ${it.value.first!!.getModulePackage()}.R.layout.${it.value.first!!.getLayoutname()}:") {
+                                    if (it.value.size() == 1) {
+                                        tab("return ${it.value.first!!.getPackage()}.${it.value.first!!.getImplementationName()}.bind(view);")
+                                    } else {
+                                        // we should check the tag to decide which layout we need to inflate
+                                        tab("{") {
+                                            tab("final Object tag = view.getTag();")
+                                            tab("if(tag == null) throw new java.lang.RuntimeException(\"view must have a tag\");")
+                                            it.value.forEach {
+                                                tab("if (\"${it.getTag()}\".equals(tag)) {") {
+                                                    tab("return new ${it.getPackage()}.${it.getImplementationName()}(view);")
+                                                } tab("}")
+                                            }
+                                        }tab("}")
+                                    }
+
+                                }
+                            }
+                        }
+                        tab("}")
+                        tab("return null;")
+                    }
+                    tab("}")
+
+                    tab("public int getId(String key) {") {
+                        tab("return BR.getId(key);")
+                    } tab("}")
+
+                    tab("final static int TARGET_MIN_SDK = ${minSdk};")
+                }
+                nl("}")
+            }.generate()
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt
new file mode 100644
index 0000000..f09d018
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt
@@ -0,0 +1,169 @@
+/*
+ * 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 android.databinding.tool.writer
+
+import java.util.BitSet
+
+class KCode (private val s : String? = null){
+
+    private var sameLine = false
+
+    private val lineSeparator = System.getProperty("line.separator")
+
+    class Appendix(val glue : String, val code : KCode)
+
+    private val nodes = arrayListOf<Any>()
+
+    class object {
+        private val cachedIndentations = BitSet()
+        private val indentCache = arrayListOf<String>()
+        fun indent(n: Int): String {
+            if (cachedIndentations.get(n)) {
+                return indentCache.get(n)
+            }
+            val s = (0..n-1).fold(""){prev, next -> "${prev}    "}
+            cachedIndentations.set(n, true )
+            while (indentCache.size() <= n) {
+                indentCache.add("");
+            }
+            indentCache.set(n, s)
+            return s
+        }
+    }
+
+    fun isNull(kcode : KCode?) = kcode == null || (kcode.nodes.isEmpty() && (kcode.s == null || kcode.s.trim() == ""))
+
+    fun tab(vararg codes : KCode?) : KCode {
+        codes.forEach { tab(it) }
+        return this
+    }
+
+    fun tab(codes : Collection<KCode?> ) : KCode {
+        codes.forEach { tab(it) }
+        return this
+    }
+
+    fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+        val c = KCode(s)
+        if (init != null) {
+            c.init()
+        }
+        return tab(c)
+    }
+
+    private fun tab(c : KCode?) : KCode {
+        if (isNull(c)) {
+            return this
+        }
+        nodes.add(c)
+        return this
+    }
+
+    fun nls(vararg codes : KCode?) : KCode {
+        codes.forEach { nl(it) }
+        return this
+    }
+
+    fun nls(codes : Collection<KCode?>) : KCode {
+        codes.forEach { nl(it) }
+        return this
+    }
+
+    fun nl(c : KCode?) : KCode {
+        if (isNull(c)) {
+            return this
+        }
+        nodes.add(c)
+        c!!.sameLine = true
+        return this
+    }
+
+    fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+        val c = KCode(s)
+        if (init != null) {
+            c.init()
+        }
+        return nl(c)
+    }
+
+    fun apps(glue : String = "", vararg codes : KCode?) : KCode {
+        codes.forEach { app(glue, it)}
+        return this
+    }
+
+    fun apps(glue : String = "", codes : Collection<KCode?>) : KCode {
+        codes.forEach { app(glue, it)}
+        return this
+    }
+
+    fun app(glue : String = "", c : KCode?) : KCode {
+        if (isNull(c)) {
+            return this
+        }
+        nodes.add(Appendix(glue, c!!))
+        return this
+    }
+
+    fun app(s : String) : KCode {
+        val c = KCode(s)
+        return app("", c)
+    }
+
+    fun app(glue : String = "", s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+        val c = KCode(s)
+        if (init != null) {
+            c.init()
+        }
+        return app(glue, c)
+    }
+
+
+    fun toS(n : Int, sb : StringBuilder) {
+        if (s != null) {
+            sb.append(s)
+        }
+        val newlineFirstNode = s != null || (nodes.isNotEmpty() && nodes.first() is Appendix)
+        var addedChild = false
+        nodes.forEach { when(it) {
+            is Appendix -> {
+                sb.append(it.glue)
+                it.code.toS(n, sb)
+            }
+            is KCode -> {
+                val childTab = n + (if(it.sameLine) 0 else 1)
+                if (addedChild || newlineFirstNode) {
+                    sb.append(lineSeparator)
+                    sb.append("${indent(childTab)}")
+                }
+                it.toS(childTab, sb)
+                addedChild = true
+            }
+        } }
+
+    }
+
+    fun generate() : String {
+        val sb = StringBuilder()
+        toS(0, sb)
+        return sb.toString()
+    }
+}
+
+fun kcode(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+    val c = KCode(s)
+    if (init != null) {
+        c.init()
+    }
+    return c
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
new file mode 100644
index 0000000..61f0d2d
--- /dev/null
+++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
@@ -0,0 +1,878 @@
+/*
+ * 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 android.databinding.tool.writer
+
+import android.databinding.tool.LayoutBinder
+import android.databinding.tool.expr.Expr
+import kotlin.properties.Delegates
+import android.databinding.tool.ext.joinToCamelCaseAsVar
+import android.databinding.tool.BindingTarget
+import android.databinding.tool.expr.IdentifierExpr
+import android.databinding.tool.util.Log
+import java.util.BitSet
+import android.databinding.tool.expr.ExprModel
+import java.util.Arrays
+import android.databinding.tool.expr.TernaryExpr
+import android.databinding.tool.expr.FieldAccessExpr
+import android.databinding.tool.expr.ComparisonExpr
+import android.databinding.tool.expr.GroupExpr
+import android.databinding.tool.expr.MathExpr
+import android.databinding.tool.expr.MethodCallExpr
+import android.databinding.tool.expr.StaticIdentifierExpr
+import android.databinding.tool.expr.SymbolExpr
+import android.databinding.tool.ext.androidId
+import android.databinding.tool.ext.lazy
+import android.databinding.tool.ext.br
+import android.databinding.tool.expr.ResourceExpr
+import android.databinding.tool.expr.BracketExpr
+import android.databinding.tool.reflection.Callable
+import android.databinding.tool.expr.CastExpr
+import android.databinding.tool.reflection.ModelAnalyzer
+import java.util.HashMap
+
+fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar()
+
+class ExprModelExt {
+    val usedFieldNames = hashSetOf<String>()
+    val localizedFlags = arrayListOf<FlagSet>()
+
+    fun localizeFlag(set : FlagSet, name:String) : FlagSet {
+        localizedFlags.add(set)
+        val result = getUniqueFieldName(name)
+        set.setLocalName(result)
+        return set
+    }
+
+    fun getUniqueFieldName(base : String) : String {
+        var candidate = base
+        var i = 0
+        while (usedFieldNames.contains(candidate)) {
+            i ++
+            candidate = base + i
+        }
+        usedFieldNames.add(candidate)
+        return candidate
+    }
+}
+
+val ExprModel.ext by Delegates.lazy { target : ExprModel ->
+    ExprModelExt()
+}
+
+fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueFieldName(base)
+
+fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base)
+
+val BindingTarget.readableUniqueName by Delegates.lazy { target: BindingTarget ->
+    val variableName : String
+    if (target.getId() == null) {
+        variableName = "boundView" + target.getTag()
+    } else {
+        variableName = target.getId().androidId().stripNonJava()
+    }
+    target.getModel().ext.getUniqueFieldName(variableName)
+}
+
+fun BindingTarget.superConversion(variable : String) : String {
+    if (isBinder()) {
+        return "${getViewClass()}.bind(${variable})"
+    } else if (getResolvedType() != null && getResolvedType().extendsViewStub()) {
+        return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})"
+    } else {
+        return "(${interfaceType}) ${variable}"
+    }
+}
+
+val BindingTarget.fieldName by Delegates.lazy { target : BindingTarget ->
+    if (target.getFieldName() == null) {
+        if (target.getId() == null) {
+            target.setFieldName("m${target.readableUniqueName.capitalize()}")
+        } else {
+            target.androidId.stripNonJava();
+            target.setFieldName(target.readableUniqueName);
+        }
+    }
+    target.getFieldName();
+}
+
+val BindingTarget.getterName by Delegates.lazy { target : BindingTarget ->
+    "get${target.readableUniqueName.capitalize()}"
+}
+
+val BindingTarget.androidId by Delegates.lazy { target : BindingTarget ->
+    "R.id.${target.getId().androidId()}"
+}
+
+val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget ->
+    if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) {
+        "android.databinding.ViewStubProxy"
+    } else {
+        target.getInterfaceType()
+    }
+}
+
+val Expr.readableUniqueName by Delegates.lazy { expr : Expr ->
+    Log.d { "readableUniqueName for ${expr.getUniqueKey()}" }
+    val stripped = "${expr.getUniqueKey().stripNonJava()}"
+    expr.getModel().ext.getUniqueFieldName(stripped)
+}
+
+val Expr.readableName by Delegates.lazy { expr : Expr ->
+    Log.d { "readableUniqueName for ${expr.getUniqueKey()}" }
+    "${expr.getUniqueKey().stripNonJava()}"
+}
+
+val Expr.fieldName by Delegates.lazy { expr : Expr ->
+    "m${expr.readableName.capitalize()}"
+}
+
+val Expr.hasFlag by Delegates.lazy { expr : Expr ->
+    expr.getId() < expr.getModel().getInvalidateableFieldLimit()
+}
+
+val Expr.localName by Delegates.lazy { expr : Expr ->
+    if(expr.isVariable()) expr.fieldName else "${expr.readableUniqueName}"
+}
+
+val Expr.setterName by Delegates.lazy { expr : Expr ->
+    "set${expr.readableName.capitalize()}"
+}
+
+val Expr.onChangeName by Delegates.lazy { expr : Expr ->
+    "onChange${expr.readableUniqueName.capitalize()}"
+}
+
+val Expr.getterName by Delegates.lazy { expr : Expr ->
+    "get${expr.readableName.capitalize()}"
+}
+
+val Expr.dirtyFlagName by Delegates.lazy { expr : Expr ->
+    "sFlag${expr.readableUniqueName.capitalize()}"
+}
+
+val Expr.shouldReadFlagName by Delegates.lazy { expr : Expr ->
+    "sFlagRead${expr.readableUniqueName.capitalize()}"
+}
+
+val Expr.invalidateFlagName by Delegates.lazy { expr : Expr ->
+    "sFlag${expr.readableUniqueName.capitalize()}Invalid"
+}
+
+val Expr.conditionalFlagPrefix by Delegates.lazy { expr : Expr ->
+    "sFlag${expr.readableUniqueName.capitalize()}Is"
+}
+
+
+fun Expr.toCode(full : Boolean = false) : KCode {
+    val it = this
+    if (isDynamic() && !full) {
+        return kcode(localName)
+    }
+    return when (it) {
+        is ComparisonExpr -> kcode("") {
+            app("", it.getLeft().toCode())
+            app(it.getOp())
+            app("", it.getRight().toCode())
+        }
+        is FieldAccessExpr -> kcode("") {
+            app("", it.getChild().toCode())
+            if (it.getGetter().type == Callable.Type.FIELD) {
+                app(".", it.getGetter().name)
+            } else {
+                app(".", it.getGetter().name).app("()")
+            }
+        }
+        is GroupExpr -> kcode("(").app("", it.getWrapped().toCode()).app(")")
+        is StaticIdentifierExpr -> kcode(it.getResolvedType().toJavaCode())
+        is IdentifierExpr -> kcode(it.localName)
+        is MathExpr -> kcode("") {
+            app("", it.getLeft().toCode())
+            app(it.getOp())
+            app("", it.getRight().toCode())
+        }
+        is MethodCallExpr -> kcode("") {
+            app("", it.getTarget().toCode())
+            app(".", it.getGetter().name)
+            app("(")
+            var first = true
+            it.getArgs().forEach {
+                apps(if (first) "" else ",", it.toCode())
+                first = false
+            }
+            app(")")
+        }
+        is SymbolExpr -> kcode(it.getText()) // TODO
+        is TernaryExpr -> kcode("") {
+            app("", it.getPred().toCode())
+            app("?", it.getIfTrue().toCode())
+            app(":", it.getIfFalse().toCode())
+        }
+        is ResourceExpr -> kcode("") {
+            app("", it.toJava())
+        }
+        is BracketExpr -> kcode("") {
+            app("", it.getTarget().toCode())
+            val bracketType = it.getAccessor();
+            when (bracketType) {
+                BracketExpr.BracketAccessor.ARRAY -> {
+                    app("[", it.getArg().toCode())
+                    app("]")
+                }
+                BracketExpr.BracketAccessor.LIST -> {
+                    app(".get(")
+                    if (it.argCastsInteger()) {
+                        app("(Integer)")
+                    }
+                    app("", it.getArg().toCode())
+                    app(")")
+                }
+                BracketExpr.BracketAccessor.MAP -> {
+                    app(".get(", it.getArg().toCode())
+                    app(")")
+                }
+            }
+        }
+        is CastExpr -> kcode("") {
+            app("(", it.getCastType())
+            app(") ", it.getCastExpr().toCode())
+        }
+        else -> kcode("//NOT IMPLEMENTED YET")
+    }
+
+}
+
+fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic()
+
+fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix"
+
+
+val Expr.dirtyFlagSet by Delegates.lazy { expr : Expr ->
+    FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount())
+}
+
+val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr ->
+    FlagSet(expr.getId())
+}
+
+val Expr.shouldReadFlagSet by Delegates.lazy { expr : Expr ->
+    FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount())
+}
+
+val Expr.conditionalFlags by Delegates.lazy { expr : Expr ->
+    arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)),
+            FlagSet(expr.getRequirementFlagIndex(true)))
+}
+
+fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0]
+
+fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) {
+    buckets.withIndex().forEach {
+        if (it.value != 0L) {
+            cb(getWordSuffix(it.index), buckets[it.index])
+        }
+    }
+}
+
+fun FlagSet.getBitSuffix(bitIndex : Int) : String {
+    val word = bitIndex / FlagSet.sBucketSize
+    return getWordSuffix(word)
+}
+
+fun FlagSet.getWordSuffix(wordIndex : Int) : String {
+    return if(wordIndex == 0) "" else "_${wordIndex}"
+}
+
+fun FlagSet.localValue(bucketIndex : Int) =
+        if (getLocalName() == null) binaryCode(bucketIndex)
+        else "${getLocalName()}${getWordSuffix(bucketIndex)}"
+
+fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex])
+
+
+fun longToBinary(l : Long) =
+        "0b${java.lang.Long.toBinaryString(l)}L"
+
+fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> {
+    val min = Math.min(buckets.size(), other.buckets.size())
+    val result = arrayListOf<T>()
+    for (i in 0..(min - 1)) {
+        // if these two can match by any chance, call the callback
+        if (intersect(other, i)) {
+            result.add(cb(getWordSuffix(i), i))
+        }
+    }
+    return result
+}
+
+class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
+    val model = layoutBinder.getModel()
+    val indices = HashMap<BindingTarget, kotlin.Int>()
+    val mDirtyFlags by Delegates.lazy {
+        val fs = FlagSet(BitSet(), model.getFlagBucketCount());
+        Arrays.fill(fs.buckets, -1)
+        fs.setDynamic(true)
+        model.localizeFlag(fs, "mDirtyFlags")
+        fs
+    }
+
+    val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } }
+    val className = layoutBinder.getImplementationName()
+
+    val identifiers by Delegates.lazy {
+        dynamics.filter { it is IdentifierExpr }
+    }
+
+    val baseClassName = "${layoutBinder.getClassName()}"
+
+    val includedBinders by Delegates.lazy {
+        layoutBinder.getBindingTargets().filter { it.isBinder() }
+    }
+
+    val variables by Delegates.lazy {
+        model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() }
+    }
+
+    val usedVariables by Delegates.lazy {
+        variables.filter {it.isUsed()}
+    }
+
+    public fun write() : String  {
+        layoutBinder.resolveWhichExpressionsAreUsed()
+        calculateIndices();
+        return kcode("package ${layoutBinder.getPackage()};") {
+            nl("import ${layoutBinder.getModulePackage()}.R;")
+            nl("import ${layoutBinder.getModulePackage()}.BR;")
+            nl("import android.view.View;")
+            val classDeclaration : String
+            if (layoutBinder.hasVariations()) {
+                classDeclaration = "${className} extends ${baseClassName}"
+            } else {
+                classDeclaration = "${className} extends android.databinding.ViewDataBinding"
+            }
+            nl("public class ${classDeclaration} {") {
+                tab(declareIncludeViews())
+                tab(declareViews())
+                tab(declareVariables())
+                tab(declareConstructor())
+                tab(declareInvalidateAll())
+                tab(declareLog())
+                tab(declareSetVariable())
+                tab(variableSettersAndGetters())
+                tab(onFieldChange())
+
+                tab(executePendingBindings())
+
+                tab(declareDirtyFlags())
+                if (!layoutBinder.hasVariations()) {
+                    tab(declareFactories())
+                }
+            }
+            nl("}")
+            tab(flagMapping())
+            tab("//end")
+        }.generate()
+    }
+    fun calculateIndices() : Unit {
+        val numTaggedViews = layoutBinder.getBindingTargets().
+                filter{it.isUsed() && it.getTag() != null}.count()
+        layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() != null }.forEach {
+            indices.put(it, Integer.parseInt(it.getTag()));
+        }
+        layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() == null && it.getId() != null }.withIndex().forEach {
+            indices.put(it.value, it.index + numTaggedViews);
+        }
+    }
+    fun declareIncludeViews() = kcode("") {
+        nl("private static final android.util.SparseIntArray sIncludes;")
+        nl("private static final android.util.SparseIntArray sViewsWithIds;")
+        nl("static {") {
+            val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null
+            if (!hasBinders) {
+                tab("sIncludes = null;")
+            } else {
+                tab("sIncludes = new android.util.SparseIntArray();")
+                layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.forEach {
+                    tab("sIncludes.put(${it.androidId}, ${indices.get(it)});")
+                }
+            }
+            val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{
+                it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null))
+            } != null
+            if (!hasViewsWithIds) {
+                tab("sViewsWithIds = null;")
+            } else {
+                tab("sViewsWithIds = new android.util.SparseIntArray();")
+                layoutBinder.getBindingTargets().filter{
+                    it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null))
+                }.forEach {
+                    tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});")
+                }
+            }
+        }
+        nl("}")
+    }
+    fun declareConstructor() = kcode("") {
+        val viewCount = layoutBinder.getBindingTargets().filter{it.isUsed()}.count()
+        if (layoutBinder.hasVariations()) {
+            nl("")
+            nl("public ${className}(View root) {") {
+                tab("this(root, mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds));")
+            }
+            nl("}")
+            nl("private ${className}(View root, View[] views) {") {
+                tab("super(root, ${model.getObservables().size()}") {
+                    layoutBinder.getBindingTargets().filter { it.getId() != null }.forEach {
+                        tab(", ${fieldConversion(it)}")
+                    }
+                    tab(");")
+                }
+            }
+        } else {
+            nl("${baseClassName}(View root) {") {
+                tab("super(root, ${model.getObservables().size()});")
+                tab("final View[] views = mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds);")
+            }
+        }
+        val taggedViews = layoutBinder.getBindingTargets().filter{it.isUsed()}
+        taggedViews.forEach {
+            if (!layoutBinder.hasVariations() || it.getId() == null) {
+                tab("this.${it.fieldName} = ${fieldConversion(it)};")
+            }
+            if (!it.isBinder()) {
+                if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) {
+                    tab("this.${it.fieldName}.setContainingBinding(this);")
+                }
+                if (it.supportsTag() && it.getTag() != null) {
+                    val originalTag = it.getOriginalTag();
+                    var tagValue = "null"
+                    if (originalTag != null) {
+                        tagValue = "\"${originalTag}\""
+                        if (originalTag.startsWith("@")) {
+                            var packageName = layoutBinder.getModulePackage()
+                            if (originalTag.startsWith("@android:")) {
+                                packageName = "android"
+                            }
+                            val slashIndex = originalTag.indexOf('/')
+                            val resourceId = originalTag.substring(slashIndex + 1)
+                            tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})"
+                        }
+                    }
+                    tab("this.${it.fieldName}.setTag(${tagValue});")
+                }
+            }
+        }
+        tab("invalidateAll();");
+        nl("}")
+    }
+
+    fun fieldConversion(target : BindingTarget) : String {
+        val index = indices.get(target)
+        if (!target.isUsed()) {
+            return "null"
+        } else {
+            val variableName: String
+            if (index == null) {
+                variableName = "root";
+            } else {
+                variableName = "views[${index}]"
+            }
+            return target.superConversion(variableName)
+        }
+    }
+
+    fun declareInvalidateAll() = kcode("") {
+        nl("@Override")
+        nl("public void invalidateAll() {") {
+            val bs = BitSet()
+            bs.set(0, model.getInvalidateableFieldLimit())
+            val fs = FlagSet(bs, mDirtyFlags.buckets.size())
+            for (i in (0..(mDirtyFlags.buckets.size() - 1))) {
+                tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};")
+            }
+            includedBinders.filter{it.isUsed()}.forEach { binder ->
+                tab("${binder.fieldName}.invalidateAll();")
+            }
+        }
+        nl("}")
+    }
+
+    fun declareSetVariable() = kcode("") {
+        nl("public boolean setVariable(int variableId, Object variable) {") {
+            tab("switch(variableId) {") {
+                usedVariables.forEach {
+                    tab ("case ${it.getName().br()} :") {
+                        tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);")
+                        tab("return true;")
+                    }
+                }
+            }
+            tab("}")
+            tab("return false;")
+        }
+        nl("}")
+    }
+
+    fun declareLog() = kcode("") {
+        nl("private void log(String msg, long i) {") {
+            tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""")
+        }
+        nl("}")
+    }
+
+    fun variableSettersAndGetters() = kcode("") {
+        variables.filterNot{it.isUsed()}.forEach {
+            nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") {
+                tab("// not used, ignore")
+            }
+            nl("}")
+            nl("")
+            nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
+                tab("return ${it.getDefaultValue()};")
+            }
+            nl("}")
+        }
+        usedVariables.forEach {
+            if (it.getUserDefinedType() != null) {
+                nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") {
+                    if (it.isObservable()) {
+                        tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});");
+                    }
+                    tab("this.${it.fieldName} = ${it.readableUniqueName};")
+                    // set dirty flags!
+                    val flagSet = it.invalidateFlagSet
+                    mDirtyFlags.mapOr(flagSet) { suffix, index ->
+                        tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
+                    }
+                    tab("super.requestRebind();")
+                }
+                nl("}")
+                nl("")
+                nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
+                    tab("return ${it.fieldName};")
+                }
+                nl("}")
+            }
+        }
+    }
+
+    fun onFieldChange() = kcode("") {
+        nl("@Override")
+        nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") {
+            tab("switch (localFieldId) {") {
+                model.getObservables().forEach {
+                    tab("case ${it.getId()} :") {
+                        tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);")
+                    }
+                }
+            }
+            tab("}")
+            tab("return false;")
+        }
+        nl("}")
+        nl("")
+
+        model.getObservables().forEach {
+            nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}, int fieldId) {") {
+                tab("switch (fieldId) {", {
+                    val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>())
+                    accessedFields.filter { it.canBeInvalidated() }
+                            .groupBy { it.getName() }
+                            .forEach {
+                                tab("case ${it.key.br()}:") {
+                                    val field = it.value.first()
+                                    mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index ->
+                                        tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};")
+                                    }
+                                    tab("return true;")
+                                }
+
+                            }
+                    tab("case ${"".br()}:") {
+                        val flagSet = it.invalidateFlagSet
+                        mDirtyFlags.mapOr(flagSet) { suffix, index ->
+                            tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
+                        }
+                        tab("return true;")
+                    }
+
+                })
+                tab("}")
+                tab("return false;")
+            }
+            nl("}")
+            nl("")
+        }
+    }
+
+    fun declareViews() = kcode("// views") {
+        val oneLayout = !layoutBinder.hasVariations();
+        layoutBinder.getBindingTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach {
+            val access : String
+            if (oneLayout && it.getId() != null) {
+                access = "public"
+            } else {
+                access = "private"
+            }
+            nl("${access} final ${it.interfaceType} ${it.fieldName};")
+        }
+    }
+
+    fun declareVariables() = kcode("// variables") {
+        usedVariables.forEach {
+            nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};")
+        }
+    }
+
+    fun declareDirtyFlags() = kcode("// dirty flag") {
+        model.ext.localizedFlags.forEach { flag ->
+            flag.notEmpty { suffix, value ->
+                nl("private")
+                app(" ", if(flag.isDynamic()) null else "static final");
+                app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};")
+            }
+        }
+    }
+
+    fun flagMapping() = kcode("/* flag mapping") {
+        if (model.getFlagMapping() != null) {
+            val mapping = model.getFlagMapping()
+            for (i in mapping.indices) {
+                tab("flag $i: ${mapping[i]}")
+            }
+        }
+        nl("flag mapping end*/")
+    }
+
+    fun executePendingBindings() = kcode("") {
+        nl("@Override")
+        nl("public void executePendingBindings() {") {
+            val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets)
+            tmpDirtyFlags.setLocalName("dirtyFlags");
+            for (i in (0..mDirtyFlags.buckets.size() - 1)) {
+                tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};")
+                tab("${mDirtyFlags.localValue(i)} = 0;")
+            }
+            model.getPendingExpressions().filterNot {it.isVariable()}.forEach {
+                tab("${it.getResolvedType().toJavaCode()} ${it.localName} = ${it.getDefaultValue()};")
+            }
+
+            do {
+                val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList()
+                val mJustRead = arrayListOf<Expr>()
+                while (!batch.none()) {
+                    val readNow = batch.filter { it.shouldReadNow(mJustRead) }
+                    if (readNow.isEmpty()) {
+                        throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}")
+                    }
+                    Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" }
+
+                    readNow.forEach {
+                        nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags))
+                    }
+                    batch.removeAll(mJustRead)
+                }
+                tab("// batch finished")
+            } while(model.markBitsRead())
+
+            //
+            layoutBinder.getBindingTargets().filter { it.isUsed() }
+                    .flatMap { it.getBindings() }
+                    .groupBy { it.getExpr() }
+                    .forEach {
+                        val flagSet = it.key.dirtyFlagSet
+                        tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
+                            "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
+                        }.joinToString(" || ")
+                        }) {") {
+                            it.value.forEach { binding ->
+                                tab("// api target ${binding.getMinApi()}")
+                                val fieldName : String
+                                if (binding.getTarget().getViewClass().
+                                        equals(binding.getTarget().getInterfaceType())) {
+                                    fieldName = "this.${binding.getTarget().fieldName}"
+                                } else {
+                                    fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})"
+                                }
+                                val bindingCode = binding.toJavaCode(fieldName, binding.getExpr().toCode().generate())
+                                if (binding.getMinApi() > 1) {
+                                    tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") {
+                                        tab("$bindingCode;")
+                                    }
+                                    tab("}")
+                                } else {
+                                    tab("$bindingCode;")
+                                }
+                            }
+                        }
+                        tab("}")
+                    }
+            includedBinders.filter{it.isUsed()}.forEach { binder ->
+                tab("${binder.fieldName}.executePendingBindings();")
+            }
+            layoutBinder.getBindingTargets().filter{
+                it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub()
+            }.forEach {
+                tab("if (${it.fieldName}.getBinding() != null) {") {
+                    tab("${it.fieldName}.getBinding().executePendingBindings();")
+                }
+                tab("}")
+            }
+        }
+        nl("}")
+    }
+
+    fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>,
+            tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") {
+        mJustRead.add(expr)
+        Log.d { expr.getUniqueKey() }
+        val flagSet = expr.shouldReadFlagSet
+        val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags)
+        val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
+            "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
+        }.joinToString(" || ")
+        })"
+
+        val readCode = kcode("") {
+            if (!expr.isVariable()) {
+                // it is not a variable read it.
+                tab("// read ${expr.getUniqueKey()}")
+                // create an if case for all dependencies that might be null
+                val nullables = expr.getDependencies().filter {
+                    it.isMandatory() && it.getOther().getResolvedType().isNullable()
+                }.map { it.getOther() }
+                if (!expr.isEqualityCheck() && nullables.isNotEmpty()) {
+                    tab ("if ( ${nullables.map { "${it.localName} != null" }.joinToString(" && ")}) {") {
+                        tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";")
+                    }
+                    tab("}")
+                } else {
+                    tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";")
+                }
+                if (expr.isObservable()) {
+                    tab("updateRegistration(${expr.getId()}, ${expr.localName});")
+                }
+            }
+
+            // if I am the condition for an expression, set its flag
+            val conditionals = expr.getDependants().filter { !it.isConditional()
+                    && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr }
+                    .map { it.getDependant() }
+            if (conditionals.isNotEmpty()) {
+                tab("// setting conditional flags")
+                tab("if (${expr.localName}) {") {
+                    conditionals.forEach {
+                        val set = it.getRequirementFlagSet(true)
+                        mDirtyFlags.mapOr(set) { suffix , index ->
+                            tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+                        }
+                    }
+                }
+                tab("} else {") {
+                    conditionals.forEach {
+                        val set = it.getRequirementFlagSet(false)
+                        mDirtyFlags.mapOr(set) { suffix , index ->
+                            tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+                        }
+                    }
+                } tab("}")
+            }
+
+            val chosen = expr.getDependants().filter {
+                val dependant = it.getDependant()
+                batch.contains(dependant) &&
+                        dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() &&
+                        dependant.shouldReadNow(mJustRead)
+            }
+            if (chosen.isNotEmpty()) {
+                val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags
+                chosen.forEach {
+                    nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags))
+                }
+            }
+        }
+        if (needsIfWrapper) {
+            tab(ifClause) {
+                app(" {")
+                nl(readCode)
+            }
+            tab("}")
+        } else {
+            nl(readCode)
+        }
+    }
+
+    fun declareFactories() = kcode("") {
+        nl("public static ${baseClassName} inflate(android.view.ViewGroup root) {") {
+            tab("return bind(android.view.LayoutInflater.from(root.getContext()).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true));")
+        }
+        nl("}")
+        nl("public static ${baseClassName} inflate(android.content.Context context) {") {
+            tab("return bind(android.view.LayoutInflater.from(context).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));")
+        }
+        nl("}")
+        nl("public static ${baseClassName} bind(android.view.View view) {") {
+            tab("if (!\"${layoutBinder.getTag()}\".equals(view.getTag())) {") {
+                tab("throw new RuntimeException(\"view tag isn't correct on view\");")
+            }
+            tab("}")
+            tab("return new ${baseClassName}(view);")
+        }
+        nl("}")
+    }
+
+    public fun writeBaseClass() : String =
+        kcode("package ${layoutBinder.getPackage()};") {
+            nl("import android.databinding.Bindable;")
+            nl("import android.databinding.DataBindingUtil;")
+            nl("import android.databinding.ViewDataBinding;")
+            nl("public abstract class ${baseClassName} extends ViewDataBinding {")
+            layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach {
+                tab("public final ${it.interfaceType} ${it.fieldName};")
+            }
+            nl("")
+            tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") {
+                layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach {
+                    tab(", ${it.interfaceType} ${it.readableUniqueName}")
+                }
+            }
+            tab(") {") {
+                tab("super(root_, localFieldCount);")
+                layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach {
+                    tab("this.${it.fieldName} = ${it.readableUniqueName};")
+                }
+            }
+            tab("}")
+            nl("")
+            variables.forEach {
+                if (it.getUserDefinedType() != null) {
+                    //it.getExpandedUserDefinedType(ModelAnalyzer.getInstance());
+                    val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports())
+                    tab("public abstract void ${it.setterName}(${type} ${it.readableUniqueName});")
+                }
+            }
+            tab("public static ${baseClassName} inflate(android.view.ViewGroup root) {") {
+                tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);")
+            }
+            tab("}")
+            tab("public static ${baseClassName} inflate(android.content.Context context) {") {
+                tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);")
+            }
+            tab("}")
+            tab("public static ${baseClassName} bind(android.view.View view) {") {
+                tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});")
+            }
+            tab("}")
+            nl("}")
+        }.generate()
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java
new file mode 100644
index 0000000..fd1562d
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2014 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;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class CallbackRegistryTest {
+
+    final Integer callback1 = 1;
+    final Integer callback2 = 2;
+    final Integer callback3 = 3;
+    CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry;
+    int notify1;
+    int notify2;
+    int notify3;
+    int[] deepNotifyCount = new int[300];
+    Integer argValue;
+
+    private void addNotifyCount(Integer callback) {
+        if (callback == callback1) {
+            notify1++;
+        } else if (callback == callback2) {
+            notify2++;
+        } else if (callback == callback3) {
+            notify3++;
+        }
+        deepNotifyCount[callback]++;
+    }
+
+    @Test
+    public void testAddListener() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg, Integer arg2) {
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        Integer callback = 0;
+
+        assertNotNull(registry.copyListeners());
+        assertEquals(0, registry.copyListeners().size());
+
+        registry.add(callback);
+        ArrayList<Integer> callbacks = registry.copyListeners();
+        assertEquals(1, callbacks.size());
+        assertEquals(callback, callbacks.get(0));
+
+        registry.add(callback);
+        callbacks = registry.copyListeners();
+        assertEquals(1, callbacks.size());
+        assertEquals(callback, callbacks.get(0));
+
+        Integer otherListener = 1;
+        registry.add(otherListener);
+        callbacks = registry.copyListeners();
+        assertEquals(2, callbacks.size());
+        assertEquals(callback, callbacks.get(0));
+        assertEquals(otherListener, callbacks.get(1));
+
+        registry.remove(callback);
+        registry.add(callback);
+        callbacks = registry.copyListeners();
+        assertEquals(2, callbacks.size());
+        assertEquals(callback, callbacks.get(1));
+        assertEquals(otherListener, callbacks.get(0));
+    }
+
+    @Test
+    public void testSimpleNotify() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        assertEquals(arg1, (int) arg);
+                        addNotifyCount(callback);
+                        argValue = arg;
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        registry.add(callback2);
+        Integer arg = 1;
+        registry.notifyCallbacks(this, arg, arg);
+        assertEquals(arg, argValue);
+        assertEquals(1, notify2);
+    }
+
+    @Test
+    public void testRemoveWhileNotifying() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                        if (callback == callback1) {
+                            registry.remove(callback1);
+                            registry.remove(callback2);
+                        }
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        registry.add(callback1);
+        registry.add(callback2);
+        registry.add(callback3);
+        registry.notifyCallbacks(this, 0, null);
+        assertEquals(1, notify1);
+        assertEquals(1, notify2);
+        assertEquals(1, notify3);
+
+        ArrayList<Integer> callbacks = registry.copyListeners();
+        assertEquals(1, callbacks.size());
+        assertEquals(callback3, callbacks.get(0));
+    }
+
+    @Test
+    public void testDeepRemoveWhileNotifying() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                        registry.remove(callback);
+                        registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        registry.add(callback1);
+        registry.add(callback2);
+        registry.add(callback3);
+        registry.notifyCallbacks(this, 0, null);
+        assertEquals(1, notify1);
+        assertEquals(2, notify2);
+        assertEquals(3, notify3);
+
+        ArrayList<Integer> callbacks = registry.copyListeners();
+        assertEquals(0, callbacks.size());
+    }
+
+    @Test
+    public void testAddRemovedListener() {
+
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                        if (callback == callback1) {
+                            registry.remove(callback2);
+                        } else if (callback == callback3) {
+                            registry.add(callback2);
+                        }
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+        registry.add(callback1);
+        registry.add(callback2);
+        registry.add(callback3);
+        registry.notifyCallbacks(this, 0, null);
+
+        ArrayList<Integer> callbacks = registry.copyListeners();
+        assertEquals(3, callbacks.size());
+        assertEquals(callback1, callbacks.get(0));
+        assertEquals(callback3, callbacks.get(1));
+        assertEquals(callback2, callbacks.get(2));
+        assertEquals(1, notify1);
+        assertEquals(1, notify2);
+        assertEquals(1, notify3);
+    }
+
+    @Test
+    public void testVeryDeepRemoveWhileNotifying() {
+        final Integer[] callbacks = new Integer[deepNotifyCount.length];
+        for (int i = 0; i < callbacks.length; i++) {
+            callbacks[i] = i;
+        }
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                        registry.remove(callback);
+                        registry.remove(callbacks[callbacks.length - callback - 1]);
+                        registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        for (int i = 0; i < callbacks.length; i++) {
+            registry.add(callbacks[i]);
+        }
+        registry.notifyCallbacks(this, 0, null);
+        for (int i = 0; i < deepNotifyCount.length; i++) {
+            int expectedCount = Math.min(i + 1, deepNotifyCount.length - i);
+            assertEquals(expectedCount, deepNotifyCount[i]);
+        }
+
+        ArrayList<Integer> callbackList = registry.copyListeners();
+        assertEquals(0, callbackList.size());
+    }
+
+    @Test
+    public void testClear() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        for (int i = 0; i < deepNotifyCount.length; i++) {
+            registry.add(i);
+        }
+        registry.clear();
+
+        ArrayList<Integer> callbackList = registry.copyListeners();
+        assertEquals(0, callbackList.size());
+
+        registry.notifyCallbacks(this, 0, null);
+        for (int i = 0; i < deepNotifyCount.length; i++) {
+            assertEquals(0, deepNotifyCount[i]);
+        }
+    }
+
+    @Test
+    public void testNestedClear() {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg1, Integer arg) {
+                        addNotifyCount(callback);
+                        registry.clear();
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        for (int i = 0; i < deepNotifyCount.length; i++) {
+            registry.add(i);
+        }
+        registry.notifyCallbacks(this, 0, null);
+        for (int i = 0; i < deepNotifyCount.length; i++) {
+            assertEquals(1, deepNotifyCount[i]);
+        }
+
+        ArrayList<Integer> callbackList = registry.copyListeners();
+        assertEquals(0, callbackList.size());
+    }
+
+    @Test
+    public void testIsEmpty() throws Exception {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg, Integer arg2) {
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+        Integer callback = 0;
+
+        assertTrue(registry.isEmpty());
+        registry.add(callback);
+        assertFalse(registry.isEmpty());
+    }
+
+    @Test
+    public void testClone() throws Exception {
+        CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+                new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+                    @Override
+                    public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+                            int arg, Integer arg2) {
+                    }
+                };
+        registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+        assertTrue(registry.isEmpty());
+        CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry2 = registry.clone();
+        Integer callback = 0;
+        registry.add(callback);
+        assertFalse(registry.isEmpty());
+        assertTrue(registry2.isEmpty());
+        registry2 = registry.clone();
+        assertFalse(registry2.isEmpty());
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java
new file mode 100644
index 0000000..e3f3345
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.databinding.tool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import android.databinding.tool.expr.ComparisonExpr;
+import android.databinding.tool.expr.Dependency;
+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.expr.MethodCallExpr;
+import android.databinding.tool.expr.SymbolExpr;
+import android.databinding.tool.expr.TernaryExpr;
+import android.databinding.tool.reflection.Callable;
+import android.databinding.tool.reflection.java.JavaAnalyzer;
+import android.databinding.tool.reflection.java.JavaClass;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class ExpressionVisitorTest {
+    ExpressionParser mParser = new ExpressionParser(new ExprModel());
+
+    @Before
+    public void setUp() throws Exception {
+        JavaAnalyzer.initForTests();
+    }
+
+    private <T extends Expr> T parse(String input, Class<T> klass) {
+        final Expr parsed = mParser.parse(input);
+        assertSame(klass, parsed.getClass());
+        return (T) parsed;
+    }
+
+    @Test
+    public void testSymbol() {
+        final SymbolExpr res = parse("null", SymbolExpr.class);
+        assertEquals(1, mParser.getModel().size());
+        assertEquals("null", res.getText());
+        assertEquals(new JavaClass(Object.class),res.getResolvedType());
+        assertEquals(0, res.getDependencies().size());
+    }
+
+
+    @RunWith(Parameterized.class)
+    public static class ComparisonExprTests {
+        ExpressionParser mParser = new ExpressionParser(new ExprModel());
+        private final String mOp;
+
+        @Before
+        public void setUp() throws Exception {
+            JavaAnalyzer.initForTests();
+        }
+
+        @Parameterized.Parameters
+        public static List<Object[]> data() {
+            return Arrays.asList(new Object[][] {
+                    {"=="}, {"<="}, {">="}, {">"}, {"<"}
+            });
+        }
+
+        public ComparisonExprTests(String op) {
+            mOp = op;
+        }
+
+        @Test
+        public void testComparison() {
+            final Expr res = mParser.parse("3 " + mOp + " 5");
+            assertEquals(3, mParser.getModel().size());
+            assertTrue(res instanceof ComparisonExpr);
+            // 0 because they are both static
+            assertEquals(0, res.getDependencies().size());
+        }
+    }
+
+
+
+    @Test
+    public void testSimpleFieldAccess() {
+        final FieldAccessExpr expr = parse("a.b", FieldAccessExpr.class);
+        assertEquals(2, mParser.mModel.size());
+        assertEquals("b", expr.getName());
+        assertEquals(1, expr.getChildren().size());
+        final Expr parent = expr.getChildren().get(0);
+        assertTrue(parent instanceof IdentifierExpr);
+        final IdentifierExpr id = (IdentifierExpr) parent;
+        assertEquals("a", id.getName());
+        assertEquals(0, id.getDependencies().size());
+        assertEquals(1, expr.getDependencies().size());
+    }
+
+    @Test
+    public void testIdentifier() {
+        final IdentifierExpr id = parse("myStr", IdentifierExpr.class);
+        assertEquals(1, mParser.mModel.size());
+        assertEquals("myStr", id.getName());
+        id.setUserDefinedType("java.lang.String");
+        assertEquals(new JavaClass(String.class), id.getResolvedType());
+    }
+
+    @Test
+    public void testTernary() {
+        final TernaryExpr parsed = parse("a > b ? 5 : 4", TernaryExpr.class);
+        assertEquals(6, mParser.getModel().size());
+        assertTrue(parsed.getPred() instanceof ComparisonExpr);
+        assertTrue(parsed.getIfTrue() instanceof SymbolExpr);
+        assertTrue(parsed.getIfFalse() instanceof SymbolExpr);
+        ComparisonExpr pred = (ComparisonExpr) parsed.getPred();
+        SymbolExpr ifTrue = (SymbolExpr) parsed.getIfTrue();
+        SymbolExpr ifFalse = (SymbolExpr) parsed.getIfFalse();
+        assertEquals("5", ifTrue.getText());
+        assertEquals("4", ifFalse.getText());
+        assertEquals(1, parsed.getDependencies().size());
+        for (Dependency dependency : parsed.getDependencies()) {
+            assertEquals(dependency.getOther() != pred, dependency.isConditional());
+        }
+    }
+
+    @Test
+    public void testInheritedFieldResolution() {
+        final FieldAccessExpr parsed = parse("myStr.length", FieldAccessExpr.class);
+        assertTrue(parsed.getChild() instanceof IdentifierExpr);
+        final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
+        id.setUserDefinedType("java.lang.String");
+        assertEquals(new JavaClass(int.class), parsed.getResolvedType());
+        Callable getter = parsed.getGetter();
+        assertEquals(Callable.Type.METHOD, getter.type);
+        assertEquals("length", getter.name);
+        assertEquals(1, parsed.getDependencies().size());
+        final Dependency dep = parsed.getDependencies().get(0);
+        assertSame(id, dep.getOther());
+        assertFalse(dep.isConditional());
+    }
+
+    @Test
+    public void testGetterResolution() {
+        final FieldAccessExpr parsed = parse("myStr.bytes", FieldAccessExpr.class);
+        assertTrue(parsed.getChild() instanceof IdentifierExpr);
+        final IdentifierExpr id = (IdentifierExpr) parsed.getChild();
+        id.setUserDefinedType("java.lang.String");
+        assertEquals(new JavaClass(byte[].class), parsed.getResolvedType());
+        Callable getter = parsed.getGetter();
+        assertEquals(Callable.Type.METHOD, getter.type);
+        assertEquals("getBytes", getter.name);
+        assertEquals(1, parsed.getDependencies().size());
+        final Dependency dep = parsed.getDependencies().get(0);
+        assertSame(id, dep.getOther());
+        assertFalse(dep.isConditional());
+    }
+
+    @Test
+    public void testMethodCall() {
+        final MethodCallExpr parsed = parse("user.getName()", MethodCallExpr.class);
+        assertTrue(parsed.getTarget() instanceof IdentifierExpr);
+        assertEquals("getName", parsed.getName());
+        assertEquals(0, parsed.getArgs().size());
+        assertEquals(1, parsed.getDependencies().size());
+        final Dependency dep = parsed.getDependencies().get(0);
+        assertSame(mParser.parse("user"), dep.getOther());
+        assertFalse(dep.isConditional());
+    }
+
+    @Test
+    public void testMethodCallWithArgs() {
+        final MethodCallExpr parsed = parse("str.substring(1, a)", MethodCallExpr.class);
+        assertTrue(parsed.getTarget() instanceof IdentifierExpr);
+        assertEquals("substring", parsed.getName());
+        final List<Expr> args = parsed.getArgs();
+        assertEquals(2, args.size());
+        assertTrue(args.get(0) instanceof SymbolExpr);
+        assertTrue(args.get(1) instanceof IdentifierExpr);
+        final List<Dependency> deps = parsed.getDependencies();
+        assertEquals(2, deps.size());
+    }
+
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
new file mode 100644
index 0000000..4219647
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.databinding.tool;
+
+
+import org.junit.Before;
+import org.junit.Test;
+
+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.expr.StaticIdentifierExpr;
+import android.databinding.tool.reflection.Callable;
+import android.databinding.tool.reflection.java.JavaClass;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class LayoutBinderTest {
+    LayoutBinder mLayoutBinder;
+    ExprModel mExprModel;
+    @Before
+    public void setUp() throws Exception {
+        mLayoutBinder = new MockLayoutBinder();
+        mExprModel = mLayoutBinder.getModel();
+    }
+
+    @Test
+    public void testRegisterId() {
+        mLayoutBinder.addVariable("test", "java.lang.String");
+        assertEquals(1, mExprModel.size());
+        final Map.Entry<String, Expr> entry = mExprModel.getExprMap().entrySet().iterator().next();
+        final Expr value = entry.getValue();
+        assertEquals(value.getClass(), IdentifierExpr.class);
+        final IdentifierExpr id = (IdentifierExpr) value;
+        assertEquals("test", id.getName());
+        assertEquals(new JavaClass(String.class), id.getResolvedType());
+        assertTrue(id.isDynamic());
+    }
+
+    @Test
+    public void testRegisterImport() {
+        mExprModel.addImport("test", "java.lang.String");
+        assertEquals(1, mExprModel.size());
+        final Map.Entry<String, Expr> entry = mExprModel.getExprMap().entrySet().iterator().next();
+        final Expr value = entry.getValue();
+        assertEquals(value.getClass(), StaticIdentifierExpr.class);
+        final IdentifierExpr id = (IdentifierExpr) value;
+        assertEquals("test", id.getName());
+        assertEquals(new JavaClass(String.class), id.getResolvedType());
+        assertFalse(id.isDynamic());
+    }
+
+    @Test
+    public void testParse() {
+        mLayoutBinder.addVariable("user", "android.databinding.tool2.LayoutBinderTest.TestUser");
+        mLayoutBinder.parse("user.name");
+        mLayoutBinder.parse("user.lastName");
+        assertEquals(3, mExprModel.size());
+        final List<Expr> bindingExprs = mExprModel.getBindingExpressions();
+        assertEquals(2, bindingExprs.size());
+        IdentifierExpr id = mExprModel.identifier("user");
+        assertTrue(bindingExprs.get(0) instanceof FieldAccessExpr);
+        assertTrue(bindingExprs.get(1) instanceof FieldAccessExpr);
+        assertEquals(2, id.getParents().size());
+        assertTrue(bindingExprs.get(0).getChildren().contains(id));
+        assertTrue(bindingExprs.get(1).getChildren().contains(id));
+    }
+
+    @Test
+    public void testParseWithMethods() {
+        mLayoutBinder.addVariable("user", "android.databinding.tool.LayoutBinderTest.TestUser");
+        mLayoutBinder.parse("user.fullName");
+        Expr item = mExprModel.getBindingExpressions().get(0);
+        assertTrue(item instanceof FieldAccessExpr);
+        IdentifierExpr id = mExprModel.identifier("user");
+        FieldAccessExpr fa = (FieldAccessExpr) item;
+        fa.getResolvedType();
+        final Callable getter = fa.getGetter();
+        assertTrue(getter.type == Callable.Type.METHOD);
+        assertSame(id, fa.getChild());
+        assertTrue(fa.isDynamic());
+    }
+
+    static class TestUser {
+        public String name;
+        public String lastName;
+
+        public String fullName() {
+            return name + " " + lastName;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java
new file mode 100644
index 0000000..068de85
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java
@@ -0,0 +1,24 @@
+/*
+ * 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 android.databinding.tool;
+
+import android.databinding.tool.store.ResourceBundle;
+
+public class MockLayoutBinder extends LayoutBinder {
+
+    public MockLayoutBinder() {
+        super(new ResourceBundle("com.test"),
+                new ResourceBundle.LayoutFileBundle("blah.xml", 1, ".", "com.test.submodule"));
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java
new file mode 100644
index 0000000..70db3a7
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.databinding.tool;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.SdkUtil;
+import android.databinding.tool.reflection.java.JavaAnalyzer;
+
+import static org.junit.Assert.assertEquals;
+
+public class SdkVersionTest {
+
+    @Before
+    public void setUp() throws Exception {
+        JavaAnalyzer.initForTests();
+    }
+
+    @Test
+    public void testNewApiMethod() {
+        ModelClass view = ModelAnalyzer.getInstance().findClass("android.view.View", null);
+        ModelMethod setElevation = view.getMethods("setElevation", 1)[0];
+        assertEquals(21, SdkUtil.getMinApi(setElevation));
+    }
+
+    @Test
+    public void testCustomCode() {
+        ModelClass view = ModelAnalyzer.getInstance()
+                .findClass("android.databinding.tool.SdkVersionTest", null);
+        ModelMethod setElevation = view.getMethods("testCustomCode", 0)[0];
+        assertEquals(1, SdkUtil.getMinApi(setElevation));
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
new file mode 100644
index 0000000..7bccd6e
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
@@ -0,0 +1,564 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.NotImplementedException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import android.databinding.tool.LayoutBinder;
+import android.databinding.tool.MockLayoutBinder;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.java.JavaAnalyzer;
+import android.databinding.tool.util.L;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class ExprModelTest {
+
+    private static class DummyExpr extends Expr {
+
+        String mKey;
+
+        public DummyExpr(String key, DummyExpr... children) {
+            super(children);
+            mKey = key;
+        }
+
+        @Override
+        protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+            return modelAnalyzer.findClass(Integer.class);
+        }
+
+        @Override
+        protected List<Dependency> constructDependencies() {
+            return constructDynamicChildrenDependencies();
+        }
+
+        @Override
+        protected String computeUniqueKey() {
+            return mKey + super.computeUniqueKey();
+        }
+    }
+
+    ExprModel mExprModel;
+
+    @Rule
+    public TestWatcher mTestWatcher = new TestWatcher() {
+        @Override
+        protected void failed(Throwable e, Description description) {
+            if (mExprModel != null && mExprModel.getFlagMapping() != null) {
+                final String[] mapping = mExprModel.getFlagMapping();
+                for (int i = 0; i < mapping.length; i++) {
+                    L.d("flag %d: %s", i, mapping[i]);
+                }
+            }
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        JavaAnalyzer.initForTests();
+        mExprModel = new ExprModel();
+    }
+
+    @Test
+    public void testAddNormal() {
+        final DummyExpr d = new DummyExpr("a");
+        assertSame(d, mExprModel.register(d));
+        assertSame(d, mExprModel.register(d));
+        assertEquals(1, mExprModel.mExprMap.size());
+    }
+
+    @Test
+    public void testAddDupe1() {
+        final DummyExpr d = new DummyExpr("a");
+        assertSame(d, mExprModel.register(d));
+        assertSame(d, mExprModel.register(new DummyExpr("a")));
+        assertEquals(1, mExprModel.mExprMap.size());
+    }
+
+    @Test
+    public void testAddMultiple() {
+        mExprModel.register(new DummyExpr("a"));
+        mExprModel.register(new DummyExpr("b"));
+        assertEquals(2, mExprModel.mExprMap.size());
+    }
+
+
+    @Test
+    public void testAddWithChildren() {
+        DummyExpr a = new DummyExpr("a");
+        DummyExpr b = new DummyExpr("b");
+        DummyExpr c = new DummyExpr("c", a, b);
+        mExprModel.register(c);
+        DummyExpr a2 = new DummyExpr("a");
+        DummyExpr b2 = new DummyExpr("b");
+        DummyExpr c2 = new DummyExpr("c", a, b);
+        assertEquals(c, mExprModel.register(c2));
+    }
+
+    @Test
+    public void testShouldRead() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr a = lb.addVariable("a", "java.lang.String");
+        IdentifierExpr b = lb.addVariable("b", "java.lang.String");
+        IdentifierExpr c = lb.addVariable("c", "java.lang.String");
+        lb.parse("a == null ? b : c");
+        mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class));
+        lb.getModel().seal();
+        Iterable<Expr> shouldRead = getShouldRead();
+        // a and a == null
+        assertEquals(2, Iterables.size(shouldRead));
+        final Iterable<Expr> readFirst = getReadFirst(shouldRead, null);
+        assertEquals(1, Iterables.size(readFirst));
+        final Expr first = Iterables.getFirst(readFirst, null);
+        assertSame(a, first);
+        // now , assume we've read this
+        final BitSet shouldReadFlags = first.getShouldReadFlags();
+        assertNotNull(shouldReadFlags);
+    }
+
+    @Test
+    public void testTernaryWithPlus() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr user = lb
+                .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User");
+        MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class);
+        mExprModel.seal();
+        Iterable<Expr> toRead = getShouldRead();
+        Iterable<Expr> readNow = getReadFirst(toRead);
+        assertEquals(1, Iterables.size(readNow));
+        assertSame(user, Iterables.getFirst(readNow, null));
+        List<Expr> justRead = new ArrayList<Expr>();
+        justRead.add(user);
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName
+        Iterables.addAll(justRead, readNow);
+        // user.lastname (T, F), user.name + " "
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName
+        Iterables.addAll(justRead, readNow);
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(0, Iterables.size(readNow));
+        mExprModel.markBitsRead();
+
+        toRead = getShouldRead();
+        assertEquals(2, Iterables.size(toRead));
+        justRead.clear();
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(1, Iterables.size(readNow));
+        assertSame(parsed.getRight(), Iterables.getFirst(readNow, null));
+        Iterables.addAll(justRead, readNow);
+
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(1, Iterables.size(readNow));
+        assertSame(parsed, Iterables.getFirst(readNow, null));
+        Iterables.addAll(justRead, readNow);
+
+        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
+        assertEquals(0, Iterables.size(readNow));
+        mExprModel.markBitsRead();
+        assertEquals(0, Iterables.size(getShouldRead()));
+    }
+
+    private List<Expr> filterOut(Iterable itr, final Iterable exclude) {
+        return Arrays.asList(Iterables.toArray(Iterables.filter(itr, new Predicate() {
+            @Override
+            public boolean apply(Object input) {
+                return !Iterables.contains(exclude, input);
+            }
+        }), Expr.class));
+    }
+
+    @Test
+    public void testTernaryInsideTernary() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr cond1 = lb.addVariable("cond1", "boolean");
+        IdentifierExpr cond2 = lb.addVariable("cond2", "boolean");
+
+        IdentifierExpr a = lb.addVariable("a", "boolean");
+        IdentifierExpr b = lb.addVariable("b", "boolean");
+        IdentifierExpr c = lb.addVariable("c", "boolean");
+
+        final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class);
+        final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue();
+        mExprModel.seal();
+
+        Iterable<Expr> toRead = getShouldRead();
+        assertEquals(1, Iterables.size(toRead));
+        assertEquals(ternaryExpr.getPred(), Iterables.getFirst(toRead, null));
+
+        Iterable<Expr> readNow = getReadFirst(toRead);
+        assertEquals(1, Iterables.size(readNow));
+        assertEquals(ternaryExpr.getPred(), Iterables.getFirst(readNow, null));
+        int cond1True = ternaryExpr.getRequirementFlagIndex(true);
+        int cond1False = ternaryExpr.getRequirementFlagIndex(false);
+        // ok, it is read now.
+        mExprModel.markBitsRead();
+
+        // now it should read cond2 or c, depending on the flag from first
+        toRead = getShouldRead();
+        assertEquals(2, Iterables.size(toRead));
+        assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred());
+        assertFlags(ternaryExpr.getIfFalse(), cond1False);
+        assertFlags(ternaryExpr.getIfTrue(), cond1True);
+
+        mExprModel.markBitsRead();
+
+        // now it should read a or b, innerTernary, outerTernary
+        toRead = getShouldRead();
+        assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr,
+                innerTernary);
+        assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true));
+        assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false));
+        assertFalse(mExprModel.markBitsRead());
+    }
+
+    @Test
+    public void testRequirementFlags() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr a = lb.addVariable("a", "java.lang.String");
+        IdentifierExpr b = lb.addVariable("b", "java.lang.String");
+        IdentifierExpr c = lb.addVariable("c", "java.lang.String");
+        IdentifierExpr d = lb.addVariable("d", "java.lang.String");
+        IdentifierExpr e = lb.addVariable("e", "java.lang.String");
+        final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e");
+        assertTrue(aTernary instanceof TernaryExpr);
+        final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue();
+        assertTrue(bTernary instanceof TernaryExpr);
+        final Expr aIsNull = mExprModel
+                .comparison("==", a, mExprModel.symbol("null", Object.class));
+        final Expr bIsNull = mExprModel
+                .comparison("==", b, mExprModel.symbol("null", Object.class));
+        lb.getModel().seal();
+        Iterable<Expr> shouldRead = getShouldRead();
+        // a and a == null
+        assertEquals(2, Iterables.size(shouldRead));
+        assertFalse(a.getShouldReadFlags().isEmpty());
+        assertTrue(a.getShouldReadFlags().get(a.getId()));
+        assertTrue(b.getShouldReadFlags().isEmpty());
+        assertTrue(c.getShouldReadFlags().isEmpty());
+        assertTrue(d.getShouldReadFlags().isEmpty());
+        assertTrue(e.getShouldReadFlags().isEmpty());
+
+        Iterable<Expr> readFirst = getReadFirst(shouldRead, null);
+        assertEquals(1, Iterables.size(readFirst));
+        final Expr first = Iterables.getFirst(readFirst, null);
+        assertSame(a, first);
+        assertTrue(mExprModel.markBitsRead());
+        for (Expr expr : mExprModel.getPendingExpressions()) {
+            assertNull(expr.mShouldReadFlags);
+        }
+        shouldRead = getShouldRead();
+        assertExactMatch(shouldRead, e, b, bIsNull);
+
+        assertFlags(e, aTernary.getRequirementFlagIndex(false));
+
+        assertFlags(b, aTernary.getRequirementFlagIndex(true));
+        assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true));
+        assertTrue(mExprModel.markBitsRead());
+        shouldRead = getShouldRead();
+        assertEquals(4, Iterables.size(shouldRead));
+        assertTrue(Iterables.contains(shouldRead, c));
+        assertTrue(Iterables.contains(shouldRead, d));
+        assertTrue(Iterables.contains(shouldRead, aTernary));
+        assertTrue(Iterables.contains(shouldRead, bTernary));
+
+        assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true)));
+        assertEquals(1, c.getShouldReadFlags().cardinality());
+
+        assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false)));
+        assertEquals(1, d.getShouldReadFlags().cardinality());
+
+        assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true)));
+        assertEquals(1, bTernary.getShouldReadFlags().cardinality());
+
+        assertEquals(5, aTernary.getShouldReadFlags().cardinality());
+        for (Expr expr : new Expr[]{a, b, c, d, e}) {
+            assertTrue(aTernary.getShouldReadFlags().get(expr.getId()));
+        }
+
+        readFirst = getReadFirst(shouldRead);
+        assertEquals(2, Iterables.size(readFirst));
+        assertTrue(Iterables.contains(readFirst, c));
+        assertTrue(Iterables.contains(readFirst, d));
+        assertFalse(mExprModel.markBitsRead());
+    }
+
+    @Test
+    public void testPostConditionalDependencies() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+
+        IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName());
+        IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName());
+        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
+        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
+        IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName());
+        IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName());
+        IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName());
+        TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class);
+        TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`"
+                + " + u2.getCond(e) ", TernaryExpr.class);
+        Expr abCmp = abTernary.getPred();
+        Expr bcCmp = bcTernary.getPred();
+        Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred();
+        final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse();
+        Expr u2GetCondE = xxPlusU2getCondE.getRight();
+        Expr u1Name = abTernary.getIfTrue();
+        Expr u2Name = abTernary.getIfFalse();
+        Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue();
+        Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse();
+
+        mExprModel.seal();
+        Iterable<Expr> shouldRead = getShouldRead();
+
+        assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp);
+
+        Iterable<Expr> firstRead = getReadFirst(shouldRead);
+
+        assertExactMatch(firstRead, a, b, c);
+
+        assertFlags(a, a, b, u1, u2, u1Name, u2Name);
+        assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e);
+        assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e);
+        assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name);
+        assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e);
+
+        assertTrue(mExprModel.markBitsRead());
+
+        shouldRead = getShouldRead();
+        Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary,
+                abTernary.getIfTrue(), abTernary.getIfFalse()};
+        assertExactMatch(shouldRead, batch);
+        firstRead = getReadFirst(shouldRead);
+        assertExactMatch(firstRead, d, e, u1, u2);
+
+        assertFlags(d, bcTernary.getRequirementFlagIndex(true));
+        assertFlags(e, bcTernary.getRequirementFlagIndex(false));
+        assertFlags(u1, bcTernary.getRequirementFlagIndex(true),
+                abTernary.getRequirementFlagIndex(true));
+        assertFlags(u2, bcTernary.getRequirementFlagIndex(false),
+                abTernary.getRequirementFlagIndex(false));
+
+        assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true));
+        assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false));
+        assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false));
+        assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name);
+        assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true));
+        assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false));
+
+        assertTrue(mExprModel.markBitsRead());
+
+        shouldRead = getShouldRead();
+        // actually, there is no real case to read u1 anymore because if b>c was not true,
+        // u1.getCond(d) will never be set. Right now, we don't have mechanism to figure this out
+        // and also it does not affect correctness (just an unnecessary if stmt)
+        assertExactMatch(shouldRead, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary);
+        firstRead = getReadFirst(shouldRead);
+        assertExactMatch(firstRead, u1LastName, u2);
+
+        assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true));
+        assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false));
+        assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false));
+
+        assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true));
+        assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e);
+    }
+
+    @Test
+    public void testCircularDependency() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
+        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
+        final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class);
+        mExprModel.seal();
+        Iterable<Expr> shouldRead = getShouldRead();
+        assertExactMatch(shouldRead, a, abTernary.getPred());
+        assertTrue(mExprModel.markBitsRead());
+        shouldRead = getShouldRead();
+        assertExactMatch(shouldRead, b, abTernary);
+        assertFalse(mExprModel.markBitsRead());
+    }
+
+    @Test
+    public void testNestedCircularDependency() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
+        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
+        IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName());
+        final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class);
+        final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue();
+        mExprModel.seal();
+        Iterable<Expr> shouldRead = getShouldRead();
+        assertExactMatch(shouldRead, a, a3Ternary.getPred());
+        assertTrue(mExprModel.markBitsRead());
+        shouldRead = getShouldRead();
+        assertExactMatch(shouldRead, c, c4Ternary.getPred());
+        assertFlags(c, a3Ternary.getRequirementFlagIndex(true),
+                a3Ternary.getRequirementFlagIndex(false));
+        assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true));
+    }
+
+    @Test
+    public void testNoFlagsForNonBindingStatic() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        lb.addVariable("a", int.class.getCanonicalName());
+        final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
+        mExprModel.seal();
+        assertTrue(parsed.getRight().getInvalidFlags().isEmpty());
+        assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality());
+        assertEquals(1, mExprModel.getInvalidateableFieldLimit());
+    }
+
+    @Test
+    public void testFlagsForBindingStatic() {
+        LayoutBinder lb = new MockLayoutBinder();
+        mExprModel = lb.getModel();
+        lb.addVariable("a", int.class.getCanonicalName());
+        final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class);
+        final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
+        mExprModel.seal();
+        assertTrue(staticParsed.isBindingExpression());
+        assertEquals(1, staticParsed.getInvalidFlags().cardinality());
+        assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags());
+        assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality());
+        assertEquals(2, mExprModel.getInvalidateableFieldLimit());
+    }
+
+    @Test
+    public void testPartialNeededRead() {
+        throw new NotImplementedException("create a test that has a variable which can be read for "
+                + "some flags and also may be read for some condition. Try both must match and"
+                + " partial match and none-match in conditionals");
+    }
+
+    private void assertFlags(Expr a, int... flags) {
+        BitSet bitset = new BitSet();
+        for (int flag : flags) {
+            bitset.set(flag);
+        }
+        assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags());
+    }
+
+    private void assertFlags(Expr a, Expr... exprs) {
+        BitSet bitSet = a.getShouldReadFlags();
+        for (Expr expr : exprs) {
+            BitSet clone = (BitSet) bitSet.clone();
+            clone.and(expr.getInvalidFlags());
+            assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr
+                    .getUniqueKey(), expr.getInvalidFlags(), clone);
+        }
+
+        BitSet composite = new BitSet();
+        for (Expr expr : exprs) {
+            composite.or(expr.getInvalidFlags());
+        }
+        assertEquals("composite flags should match", composite, bitSet);
+    }
+
+    private void assertExactMatch(Iterable<Expr> iterable, Expr... exprs) {
+        int i = 0;
+        log("list", iterable);
+        for (Expr expr : exprs) {
+            assertTrue((i++) + ":must contain " + expr.getUniqueKey(),
+                    Iterables.contains(iterable, expr));
+        }
+        i = 0;
+        for (Expr expr : iterable) {
+            assertTrue((i++) + ":must be expected " + expr.getUniqueKey(),
+                    ArrayUtils.contains(exprs, expr));
+        }
+    }
+
+    private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) {
+        final Expr parsed = binder.parse(input);
+        assertTrue(klass.isAssignableFrom(parsed.getClass()));
+        return (T) parsed;
+    }
+
+    private void log(String s, Iterable<Expr> iterable) {
+        L.d(s);
+        for (Expr e : iterable) {
+            L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(),
+                    e.getShouldReadFlagsWithConditionals(), e.getReadSoFar());
+        }
+        L.d("end of %s", s);
+    }
+
+    private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead) {
+        return getReadFirst(shouldRead, null);
+    }
+
+    private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead, final Iterable<Expr> justRead) {
+        return Iterables.filter(shouldRead, new Predicate<Expr>() {
+            @Override
+            public boolean apply(Expr input) {
+                return input.shouldReadNow(justRead);
+            }
+        });
+    }
+
+    private Iterable<Expr> getShouldRead() {
+        return mExprModel.filterShouldRead(mExprModel.getPendingExpressions());
+    }
+
+    public static class User {
+
+        String name;
+
+        String lastName;
+
+        public String getName() {
+            return name;
+        }
+
+        public String getLastName() {
+            return lastName;
+        }
+
+        public boolean getCond(int i) {
+            return true;
+        }
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
new file mode 100644
index 0000000..f051fde
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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 android.databinding.tool.expr;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.java.JavaAnalyzer;
+
+import java.util.BitSet;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ExprTest{
+    private static class DummyExpr extends Expr {
+        String mKey;
+        public DummyExpr(String key, DummyExpr... children) {
+            super(children);
+            mKey = key;
+        }
+
+        @Override
+        protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+            return modelAnalyzer.findClass(Integer.class);
+        }
+
+        @Override
+        protected List<Dependency> constructDependencies() {
+            return constructDynamicChildrenDependencies();
+        }
+
+        @Override
+        protected String computeUniqueKey() {
+            return mKey + super.computeUniqueKey();
+        }
+
+        @Override
+        public boolean isDynamic() {
+            return true;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        JavaAnalyzer.initForTests();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testBadExpr() {
+        Expr expr = new Expr() {
+            @Override
+            protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+                return modelAnalyzer.findClass(Integer.class);
+            }
+
+            @Override
+            protected List<Dependency> constructDependencies() {
+                return constructDynamicChildrenDependencies();
+            }
+        };
+        expr.getUniqueKey();
+    }
+
+    @Test
+    public void testBasicInvalidationFlag() {
+        DummyExpr d = new DummyExpr("a");
+        d.setId(3);
+        d.enableDirectInvalidation();
+        assertTrue(d.getInvalidFlags().get(3));
+    }
+
+    @Test
+    public void testCannotBeInvalidated() {
+        DummyExpr d = new DummyExpr("a");
+        d.setId(3);
+        assertTrue(d.getInvalidFlags().isEmpty());
+    }
+
+    @Test
+    public void testInvalidationInheritance() {
+        ExprModel model = new ExprModel();
+        DummyExpr a = model.register(new DummyExpr("a"));
+        DummyExpr b = model.register(new DummyExpr("b"));
+        DummyExpr c = model.register(new DummyExpr("c", a, b));
+        a.enableDirectInvalidation();
+        b.enableDirectInvalidation();
+        c.setBindingExpression(true);
+        model.seal();
+        assertFlags(c, a, b);
+    }
+
+    @Test
+    public void testInvalidationInheritance2() {
+        ExprModel model = new ExprModel();
+        DummyExpr a = model.register(new DummyExpr("a"));
+        DummyExpr b = model.register(new DummyExpr("b", a));
+        DummyExpr c = model.register(new DummyExpr("c", b));
+        a.enableDirectInvalidation();
+        b.enableDirectInvalidation();
+        c.setBindingExpression(true);
+        model.seal();
+        assertFlags(c, a, b);
+    }
+
+    @Test
+    public void testShouldReadFlags() {
+        ExprModel model = new ExprModel();
+        DummyExpr a = model.register(new DummyExpr("a"));
+        a.enableDirectInvalidation();
+        a.setBindingExpression(true);
+        model.seal();
+        assertFlags(a, a);
+    }
+
+    @Test
+    public void testShouldReadDependencyFlags() {
+        ExprModel model = new ExprModel();
+        DummyExpr a = model.register(new DummyExpr("a"));
+        DummyExpr b = model.register(new DummyExpr("b", a));
+        DummyExpr c = model.register(new DummyExpr("c", b));
+        a.enableDirectInvalidation();
+        b.enableDirectInvalidation();
+        b.setBindingExpression(true);
+        c.setBindingExpression(true);
+        model.seal();
+        assertFlags(b, a, b);
+        assertFlags(c, a, b);
+    }
+
+    private void assertFlags(Expr a, Expr... exprs) {
+        BitSet bitSet = a.getShouldReadFlags();
+        for (Expr expr : exprs) {
+            BitSet clone = (BitSet) bitSet.clone();
+            clone.and(expr.getInvalidFlags());
+            assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr
+                    .getUniqueKey(), expr.getInvalidFlags(), clone);
+        }
+
+        BitSet composite = new BitSet();
+        for (Expr expr : exprs) {
+            composite.or(expr.getInvalidFlags());
+        }
+        assertEquals("composite flags should match", composite, bitSet);
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
new file mode 100644
index 0000000..19b14b4
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.databinding.tool.reflection.java;
+
+import com.google.common.collect.ImmutableMap;
+
+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 java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JavaAnalyzer extends ModelAnalyzer {
+    public static final Map<String, Class> PRIMITIVE_TYPES =
+            new ImmutableMap.Builder<String, Class>()
+                    .put("boolean", boolean.class)
+                    .put("byte", byte.class)
+                    .put("short", short.class)
+                    .put("char", char.class)
+                    .put("int", int.class)
+                    .put("long", long.class)
+                    .put("float", float.class)
+                    .put("double", double.class)
+                    .build();
+
+    private HashMap<String, JavaClass> mClassCache = new HashMap<String, JavaClass>();
+
+    private final ClassLoader mClassLoader;
+
+    public JavaAnalyzer(ClassLoader classLoader) {
+        setInstance(this);
+        mClassLoader = classLoader;
+    }
+
+    @Override
+    public JavaClass loadPrimitive(String className) {
+        Class clazz = PRIMITIVE_TYPES.get(className);
+        if (clazz == null) {
+            return null;
+        } else {
+            return new JavaClass(clazz);
+        }
+    }
+
+    @Override
+    public ModelClass findClass(String className, Map<String, String> imports) {
+        // TODO handle imports
+        JavaClass loaded = mClassCache.get(className);
+        if (loaded != null) {
+            return loaded;
+        }
+        L.d("trying to load class %s from %s", className, mClassLoader.toString());
+        loaded = loadPrimitive(className);
+        if (loaded == null) {
+            try {
+                if (className.startsWith("[") && className.contains("L")) {
+                    int indexOfL = className.indexOf('L');
+                    JavaClass baseClass = (JavaClass) findClass(
+                            className.substring(indexOfL + 1, className.length() - 1), null);
+                    String realClassName = className.substring(0, indexOfL + 1) +
+                            baseClass.mClass.getCanonicalName() + ';';
+                    loaded = new JavaClass(Class.forName(realClassName, false, mClassLoader));
+                    mClassCache.put(className, loaded);
+                } else {
+                    loaded = loadRecursively(className);
+                    mClassCache.put(className, loaded);
+                }
+
+            } catch (Throwable t) {
+//                L.e(t, "cannot load class " + className);
+            }
+        }
+        // expr visitor may call this to resolve statics. Sometimes, it is OK not to find a class.
+        if (loaded == null) {
+            return null;
+        }
+        L.d("loaded class %s", loaded.mClass.getCanonicalName());
+        return loaded;
+    }
+
+    @Override
+    public ModelClass findClass(Class classType) {
+        return new JavaClass(classType);
+    }
+
+    @Override
+    public TypeUtil createTypeUtil() {
+        return new JavaTypeUtil();
+    }
+
+    private JavaClass loadRecursively(String className) throws ClassNotFoundException {
+        try {
+            L.d("recursively checking %s", className);
+            return new JavaClass(mClassLoader.loadClass(className));
+        } catch (ClassNotFoundException ex) {
+            int lastIndexOfDot = className.lastIndexOf(".");
+            if (lastIndexOfDot == -1) {
+                throw ex;
+            }
+            return loadRecursively(className.substring(0, lastIndexOfDot) + "$" + className
+                    .substring(lastIndexOfDot + 1));
+        }
+    }
+
+    public static void initForTests() {
+        Map<String, String> env = System.getenv();
+        for (Map.Entry<String, String> entry : env.entrySet()) {
+            L.d("%s %s", entry.getKey(), entry.getValue());
+        }
+        String androidHome = env.get("ANDROID_HOME");
+        if (androidHome == null) {
+            throw new IllegalStateException(
+                    "you need to have ANDROID_HOME set in your environment"
+                            + " to run compiler tests");
+        }
+        File androidJar = new File(androidHome + "/platforms/android-21/android.jar");
+        if (!androidJar.exists() || !androidJar.canRead()) {
+            throw new IllegalStateException(
+                    "cannot find android jar at " + androidJar.getAbsolutePath());
+        }
+        // now load android data binding library as well
+
+        try {
+            ClassLoader classLoader = new URLClassLoader(new URL[]{androidJar.toURI().toURL()},
+                    ModelAnalyzer.class.getClassLoader());
+            new JavaAnalyzer(classLoader);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("cannot create class loader", e);
+        }
+
+        SdkUtil.initialize(8, new File(androidHome));
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java
new file mode 100644
index 0000000..121a569
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.databinding.tool.reflection.java;
+
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelField;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.TypeUtil;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class JavaClass extends ModelClass {
+    public final Class mClass;
+
+    public JavaClass(Class clazz) {
+        mClass = clazz;
+    }
+
+    @Override
+    public String toJavaCode() {
+        return toJavaCode(mClass);
+    }
+
+    private static String toJavaCode(Class aClass) {
+        if (aClass.isArray()) {
+            Class component = aClass.getComponentType();
+            return toJavaCode(component) + "[]";
+        } else {
+            return aClass.getCanonicalName().replace('$', '.');
+        }
+    }
+
+    @Override
+    public boolean isArray() {
+        return mClass.isArray();
+    }
+
+    @Override
+    public ModelClass getComponentType() {
+        if (mClass.isArray()) {
+            return new JavaClass(mClass.getComponentType());
+        } else if (isList() || isMap()) {
+            return new JavaClass(Object.class);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean isNullable() {
+        return Object.class.isAssignableFrom(mClass);
+    }
+
+    @Override
+    public boolean isPrimitive() {
+        return mClass.isPrimitive();
+    }
+
+    @Override
+    public boolean isBoolean() {
+        return boolean.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isChar() {
+        return char.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isByte() {
+        return byte.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isShort() {
+        return short.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isInt() {
+        return int.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isLong() {
+        return long.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isFloat() {
+        return float.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isDouble() {
+        return double.class.equals(mClass);
+    }
+
+    @Override
+    public boolean isVoid() {
+        return void.class.equals(mClass);
+    }
+
+    @Override
+    public ModelClass unbox() {
+        if (mClass.isPrimitive()) {
+            return this;
+        }
+        if (Integer.class.equals(mClass)) {
+            return new JavaClass(int.class);
+        } else if (Long.class.equals(mClass)) {
+            return new JavaClass(long.class);
+        } else if (Short.class.equals(mClass)) {
+            return new JavaClass(short.class);
+        } else if (Byte.class.equals(mClass)) {
+            return new JavaClass(byte.class);
+        } else if (Character.class.equals(mClass)) {
+            return new JavaClass(char.class);
+        } else if (Double.class.equals(mClass)) {
+            return new JavaClass(double.class);
+        } else if (Float.class.equals(mClass)) {
+            return new JavaClass(float.class);
+        } else if (Boolean.class.equals(mClass)) {
+            return new JavaClass(boolean.class);
+        } else {
+            // not a boxed type
+            return this;
+        }
+
+    }
+
+    @Override
+    public JavaClass box() {
+        if (!mClass.isPrimitive()) {
+            return this;
+        }
+        if (int.class.equals(mClass)) {
+            return new JavaClass(Integer.class);
+        } else if (long.class.equals(mClass)) {
+            return new JavaClass(Long.class);
+        } else if (short.class.equals(mClass)) {
+            return new JavaClass(Short.class);
+        } else if (byte.class.equals(mClass)) {
+            return new JavaClass(Byte.class);
+        } else if (char.class.equals(mClass)) {
+            return new JavaClass(Character.class);
+        } else if (double.class.equals(mClass)) {
+            return new JavaClass(Double.class);
+        } else if (float.class.equals(mClass)) {
+            return new JavaClass(Float.class);
+        } else if (boolean.class.equals(mClass)) {
+            return new JavaClass(Boolean.class);
+        } else {
+            // not a valid type?
+            return this;
+        }
+    }
+
+    @Override
+    public boolean isAssignableFrom(ModelClass that) {
+        Class thatClass = ((JavaClass) that).mClass;
+        return mClass.isAssignableFrom(thatClass);
+    }
+
+    @Override
+    public ModelClass getSuperclass() {
+        if (mClass.getSuperclass() == null) {
+            return null;
+        }
+        return new JavaClass(mClass.getSuperclass());
+    }
+
+    @Override
+    public String getCanonicalName() {
+        return mClass.getCanonicalName();
+    }
+
+    @Override
+    public ModelClass erasure() {
+        return this;
+    }
+
+    @Override
+    public String getJniDescription() {
+        return TypeUtil.getInstance().getDescription(this);
+    }
+
+    @Override
+    protected ModelField[] getDeclaredFields() {
+        Field[] fields = mClass.getDeclaredFields();
+        ModelField[] modelFields;
+        if (fields == null) {
+            modelFields = new ModelField[0];
+        } else {
+            modelFields = new ModelField[fields.length];
+            for (int i = 0; i < fields.length; i++) {
+                modelFields[i] = new JavaField(fields[i]);
+            }
+        }
+        return modelFields;
+    }
+
+    @Override
+    protected ModelMethod[] getDeclaredMethods() {
+        Method[] methods = mClass.getDeclaredMethods();
+        if (methods == null) {
+            return new ModelMethod[0];
+        } else {
+            ModelMethod[] classMethods = new ModelMethod[methods.length];
+            for (int i = 0; i < methods.length; i++) {
+                classMethods[i] = new JavaMethod(methods[i]);
+            }
+            return classMethods;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof JavaClass) {
+            return mClass.equals(((JavaClass) obj).mClass);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return mClass.hashCode();
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java
new file mode 100644
index 0000000..6821f16
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.databinding.tool.reflection.java;
+
+import android.databinding.Bindable;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelField;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class JavaField extends ModelField {
+    public final Field mField;
+
+    public JavaField(Field field) {
+        mField = field;
+    }
+
+    @Override
+    public boolean isBindable() {
+        return mField.getAnnotation(Bindable.class) != null;
+    }
+
+    @Override
+    public String getName() {
+        return mField.getName();
+    }
+
+    @Override
+    public boolean isPublic() {
+        return Modifier.isPublic(mField.getModifiers());
+    }
+
+    @Override
+    public boolean isStatic() {
+        return Modifier.isStatic(mField.getModifiers());
+    }
+
+    @Override
+    public boolean isFinal() {
+        return Modifier.isFinal(mField.getModifiers());
+    }
+
+    @Override
+    public ModelClass getFieldType() {
+        return new JavaClass(mField.getType());
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java
new file mode 100644
index 0000000..4ef566f
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.databinding.tool.reflection.java;
+
+
+import android.databinding.Bindable;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.SdkUtil;
+import android.databinding.tool.reflection.TypeUtil;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+public class JavaMethod extends ModelMethod {
+    public final Method mMethod;
+
+    public JavaMethod(Method method) {
+        mMethod = method;
+    }
+
+
+    @Override
+    public ModelClass getDeclaringClass() {
+        return new JavaClass(mMethod.getDeclaringClass());
+    }
+
+    @Override
+    public ModelClass[] getParameterTypes() {
+        Class[] parameterTypes = mMethod.getParameterTypes();
+        ModelClass[] parameterClasses = new ModelClass[parameterTypes.length];
+        for (int i = 0; i < parameterTypes.length; i++) {
+            parameterClasses[i] = new JavaClass(parameterTypes[i]);
+        }
+        return parameterClasses;
+    }
+
+    @Override
+    public String getName() {
+        return mMethod.getName();
+    }
+
+    @Override
+    public ModelClass getReturnType(List<ModelClass> args) {
+        return new JavaClass(mMethod.getReturnType());
+    }
+
+    @Override
+    public boolean isVoid() {
+        return void.class.equals(mMethod.getReturnType());
+    }
+
+    @Override
+    public boolean isPublic() {
+        return Modifier.isPublic(mMethod.getModifiers());
+    }
+
+    @Override
+    public boolean isStatic() {
+        return Modifier.isStatic(mMethod.getModifiers());
+    }
+
+    @Override
+    public boolean isBindable() {
+        return mMethod.getAnnotation(Bindable.class) != null;
+    }
+
+    @Override
+    public int getMinApi() {
+        return SdkUtil.getMinApi(this);
+    }
+
+    @Override
+    public String getJniDescription() {
+        return TypeUtil.getInstance().getDescription(this);
+    }
+
+    @Override
+    public boolean isVarArgs() {
+        return false;
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java
new file mode 100644
index 0000000..33bff3b
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java
@@ -0,0 +1,89 @@
+/*
+ * 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 android.databinding.tool.reflection.java;
+
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.reflection.TypeUtil;
+import android.databinding.tool.util.L;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+
+public class JavaTypeUtil extends TypeUtil {
+
+    @Override
+    public String getDescription(ModelClass modelClass) {
+        return modelClass.getCanonicalName().replace('.', '/');
+    }
+
+    @Override
+    public String getDescription(ModelMethod modelMethod) {
+        Method method = ((JavaMethod) modelMethod).mMethod;
+        StringBuilder sb  = new StringBuilder();
+        sb.append(method.getName());
+        sb.append("(");
+        for (Class param : method.getParameterTypes()) {
+            sb.append(getDescription(param));
+        }
+        sb.append(")");
+        sb.append(getDescription(method.getReturnType()));
+        return sb.toString();
+    }
+
+    private String getDescription(Class klass) {
+        if (klass == null) {
+            throw new UnsupportedOperationException();
+        }
+        if (boolean.class.equals(klass)) {
+            return BOOLEAN;
+        }
+        if (byte.class.equals(klass)) {
+            return BYTE;
+        }
+        if (short.class.equals(klass)) {
+            return SHORT;
+        }
+        if (int.class.equals(klass)) {
+            return INT;
+        }
+        if (long.class.equals(klass)) {
+            return LONG;
+        }
+        if (char.class.equals(klass)) {
+            return CHAR;
+        }
+        if (float.class.equals(klass)) {
+            return FLOAT;
+        }
+        if (double.class.equals(klass)) {
+            return DOUBLE;
+        }
+        if (void.class.equals(klass)) {
+            return VOID;
+        }
+        if (Object.class.isAssignableFrom(klass)) {
+            return CLASS_PREFIX + klass.getCanonicalName().replace('.', '/') + CLASS_SUFFIX;
+        }
+        if (Array.class.isAssignableFrom(klass)) {
+            return ARRAY + getDescription(klass.getComponentType());
+        }
+
+        UnsupportedOperationException ex
+                = new UnsupportedOperationException("cannot understand type "
+                + klass.toString() + ", kind:");
+        L.e(ex, "cannot create JNI type for %s", klass.getCanonicalName());
+        throw ex;
+    }
+}
diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java
new file mode 100644
index 0000000..327593a
--- /dev/null
+++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.databinding.tool.writer;
+
+import org.junit.Test;
+
+import java.util.BitSet;
+
+import static org.junit.Assert.assertEquals;
+
+public class FlagSetTest {
+    @Test
+    public void testSimple1Level() {
+        BitSet bs = new BitSet();
+        bs.set(7);
+        FlagSet flagSet = new FlagSet(bs, 3);
+        assertEquals(3, flagSet.buckets.length);
+        assertEquals(1 << 7, flagSet.buckets[0]);
+        assertEquals(0, flagSet.buckets[1]);
+        assertEquals(0, flagSet.buckets[2]);
+    }
+
+    @Test
+    public void testSimple2Level() {
+        BitSet bs = new BitSet();
+        bs.set(FlagSet.sBucketSize + 2);
+        FlagSet flagSet = new FlagSet(bs, 3);
+        assertEquals(3, flagSet.buckets.length);
+        assertEquals(0, flagSet.buckets[0]);
+        assertEquals(1 << 2, flagSet.buckets[1]);
+        assertEquals(0, flagSet.buckets[2]);
+    }
+
+    @Test
+    public void testSimple3Level() {
+        BitSet bs = new BitSet();
+        bs.set(5);
+        bs.set(FlagSet.sBucketSize + 2);
+        bs.set(FlagSet.sBucketSize * 2 + 10);
+        FlagSet flagSet = new FlagSet(bs, 3);
+        assertEquals(3, flagSet.buckets.length);
+        assertEquals(1 << 5, flagSet.buckets[0]);
+        assertEquals(1 << 2, flagSet.buckets[1]);
+        assertEquals(1 << 10, flagSet.buckets[2]);
+    }
+}
diff --git a/tools/data-binding/databinding.properties b/tools/data-binding/databinding.properties
new file mode 100644
index 0000000..0bda1db
--- /dev/null
+++ b/tools/data-binding/databinding.properties
@@ -0,0 +1,11 @@
+# global settings for projects
+kotlinVersion = 0.11.91
+releaseVersion = 0.3
+snapshotVersion = 0.3-SNAPSHOT
+androidPluginVersion = 1.0.1
+javaTargetCompatibility = 1.6
+javaSourceCompatibility = 1.6
+mavenRepoName=maven-repo
+group=com.android.databinding
+testGroup=com.android.databinding.test
+
diff --git a/tools/data-binding/extensions/baseAdapters/build.gradle b/tools/data-binding/extensions/baseAdapters/build.gradle
new file mode 100644
index 0000000..34ca4b9
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/build.gradle
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+
+apply plugin: 'maven'
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile "com.android.databinding:baseLibrary:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+    compile 'com.android.support:support-v4:+'
+    compile 'com.android.support:cardview-v7:+'
+    compile 'com.android.support:appcompat-v7:+'
+}
+
+configurations {
+    jarArchives
+}
+
+
+//create jar tasks
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+
+    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
+        return; // Skip debug builds.
+    }
+    // @Jar version is needed to run compiler tests
+    def task = project.tasks.create "jar${name.capitalize()}", Jar
+    task.dependsOn variant.javaCompile
+    task.from variant.javaCompile.destinationDir
+    def packageName = "com.android.databinding.library.baseAdapters"
+    def appPkgAsClass = packageName.replace('.', '/')
+    task.exclude("android/databinding/layouts/*.*")
+    task.exclude("$appPkgAsClass/databinding/*")
+    task.exclude("$appPkgAsClass/BR.*")
+    artifacts.add('jarArchives', task);
+}
+
+uploadArchives {
+}
+
+uploadJarArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://${config.mavenRepoDir}")
+            pom.artifactId = "adapters"
+            pom.whenConfigured {
+                println("configured pom, $it")
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'support-v4' }.optional = true
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'cardview-v7' }.optional = true
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'appcompat-v7' }.optional = true
+            }
+        }
+    }
+}
+
+uploadArchives.dependsOn uploadJarArchives
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml b/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..38cf779
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.databinding.library.baseAdapters">
+</manifest>
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java
new file mode 100644
index 0000000..e645bff
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.AbsListView", attribute = "android:listSelector", method = "setSelector"),
+        @BindingMethod(type = "android.widget.AbsListView", attribute = "android:scrollingCache", method = "setScrollingCacheEnabled"),
+        @BindingMethod(type = "android.widget.AbsListView", attribute = "android:smoothScrollbar", method = "setSmoothScrollbarEnabled"),
+})
+public class AbsListViewBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java
new file mode 100644
index 0000000..4494ec7
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.AbsSeekBar", attribute = "android:thumbTint", method = "setThumbTintList"),
+
+})
+public class AbsSeekBarBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java
new file mode 100644
index 0000000..adf84b2
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.widget.AbsSpinner;
+import android.widget.ArrayAdapter;
+import android.widget.SpinnerAdapter;
+
+public class AbsSpinnerBindingAdapter {
+
+    @BindingAdapter("android:entries")
+    public static void setEntries(AbsSpinner view, CharSequence[] entries) {
+        if (entries != null) {
+            SpinnerAdapter oldAdapter = view.getAdapter();
+            boolean changed = true;
+            if (oldAdapter != null && oldAdapter.getCount() == entries.length) {
+                changed = false;
+                for (int i = 0; i < entries.length; i++) {
+                    if (!entries[i].equals(oldAdapter.getItem(i))) {
+                        changed = true;
+                        break;
+                    }
+                }
+            }
+            if (changed) {
+                ArrayAdapter<CharSequence> adapter =
+                        new ArrayAdapter<CharSequence>(view.getContext(),
+                                android.R.layout.simple_spinner_item, entries);
+                adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+                view.setAdapter(adapter);
+            }
+        } else {
+            view.setAdapter(null);
+        }
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java
new file mode 100644
index 0000000..334d818
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.AutoCompleteTextView", attribute = "android:completionThreshold", method = "setThreshold"),
+        @BindingMethod(type = "android.widget.AutoCompleteTextView", attribute = "android:popupBackground", method = "setDropDownBackgroundDrawable"),
+})
+public class AutoCompleteTextViewBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java
new file mode 100644
index 0000000..0545141
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.support.v7.widget.CardView;
+
+@BindingMethods({
+        @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardCornerRadius", method = "setRadius"),
+        @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardMaxElevation", method = "setMaxCardElevation"),
+        @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardPreventCornerOverlap", method = "setPreventCornerOverlap"),
+        @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardUseCompatPadding", method = "setUseCompatPadding"),
+})
+public class CardViewBindingAdapter {
+
+    @BindingAdapter("contentPadding")
+    public static void setContentPadding(CardView view, int padding) {
+        view.setContentPadding(padding, padding, padding, padding);
+    }
+
+    @BindingAdapter("contentPaddingLeft")
+    public static void setContentPaddingLeft(CardView view, int left) {
+        int top = view.getContentPaddingTop();
+        int right = view.getContentPaddingRight();
+        int bottom = view.getContentPaddingBottom();
+        view.setContentPadding(left, top, right, bottom);
+    }
+
+    @BindingAdapter("contentPaddingTop")
+    public static void setContentPaddingTop(CardView view, int top) {
+        int left = view.getContentPaddingLeft();
+        int right = view.getContentPaddingRight();
+        int bottom = view.getContentPaddingBottom();
+        view.setContentPadding(left, top, right, bottom);
+    }
+
+    @BindingAdapter("contentPaddingRight")
+    public static void setContentPaddingRight(CardView view, int right) {
+        int left = view.getContentPaddingLeft();
+        int top = view.getContentPaddingTop();
+        int bottom = view.getContentPaddingBottom();
+        view.setContentPadding(left, top, right, bottom);
+    }
+
+    @BindingAdapter("contentPaddingBottom")
+    public static void setContentPaddingBottom(CardView view, int bottom) {
+        int left = view.getContentPaddingLeft();
+        int top = view.getContentPaddingTop();
+        int right = view.getContentPaddingRight();
+        view.setContentPadding(left, top, right, bottom);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java
new file mode 100644
index 0000000..aa16057
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.CheckedTextView", attribute = "android:checkMark", method = "setCheckMarkDrawable"),
+        @BindingMethod(type = "android.widget.CheckedTextView", attribute = "android:checkMarkTint", method = "setCheckMarkTintList"),
+})
+public class CheckedTextViewBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java
new file mode 100644
index 0000000..9ed5dd7
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.CompoundButton", attribute = "android:buttonTint", method = "setButtonTintList"),
+})
+public class CompoundButtonBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java
new file mode 100644
index 0000000..44eda4d
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingConversion;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.ColorDrawable;
+
+public class Converters {
+    @BindingConversion
+    public static ColorDrawable convertColorToDrawable(int color) {
+        return new ColorDrawable(color);
+    }
+
+    @BindingConversion
+    public static ColorStateList convertColorToColorStateList(int color) {
+        return ColorStateList.valueOf(color);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java
new file mode 100644
index 0000000..82641a6
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.FrameLayout", attribute = "android:foregroundTint", method = "setForegroundTintList"),
+})
+public class FrameLayoutBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java
new file mode 100644
index 0000000..9be1a14
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.ImageView", attribute = "android:src", method = "setImageDrawable"),
+        @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"),
+        @BindingMethod(type = "android.widget.ImageView", attribute = "android:tintMode", method = "setImageTintMode"),
+})
+public class ImageViewBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java
new file mode 100644
index 0000000..7bb85e9
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.LinearLayout", attribute = "android:divider", method = "setDividerDrawable"),
+        @BindingMethod(type = "android.widget.LinearLayout", attribute = "android:measureWithLargestChild", method = "setMeasureWithLargestChildEnabled"),
+})
+public class LinearLayoutBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java
new file mode 100644
index 0000000..fdbab8f
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:indeterminateTint", method = "setIndeterminateTintList"),
+        @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:progressTint", method = "setProgressTintList"),
+        @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:secondaryProgressTint", method = "setSecondaryProgressTintList"),
+})
+public class ProgressBarBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java
new file mode 100644
index 0000000..727fecf
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.RadioGroup", attribute = "android:checkedButton", method = "check"),
+})
+public class RadioGroupBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java
new file mode 100644
index 0000000..eb98629
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.Spinner", attribute = "android:popupBackground", method = "setPopupBackgroundDrawable"),
+})
+public class SpinnerBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java
new file mode 100644
index 0000000..05307c7
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.annotation.TargetApi;
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.os.Build;
+import android.widget.Switch;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.Switch", attribute = "android:thumb", method = "setThumbDrawable"),
+        @BindingMethod(type = "android.widget.Switch", attribute = "android:track", method = "setTrackDrawable"),
+})
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class SwitchBindingAdapter {
+
+    @BindingAdapter("android:switchTextAppearance")
+    public static void setSwitchTextAppearance(Switch view, int value) {
+        view.setSwitchTextAppearance(null, value);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java
new file mode 100644
index 0000000..8dca9ac
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.support.v7.widget.SwitchCompat;
+
+@BindingMethods({
+        @BindingMethod(type = "android.support.v7.widget.SwitchCompat", attribute = "android:thumb", method = "setThumbDrawable"),
+        @BindingMethod(type = "android.support.v7.widget.SwitchCompat", attribute = "android:track", method = "setTrackDrawable"),
+})
+public class SwitchCompatBindingAdapter {
+
+    @BindingAdapter("android:switchTextAppearance")
+    public static void setSwitchTextAppearance(SwitchCompat view, int value) {
+        view.setSwitchTextAppearance(null, value);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java
new file mode 100644
index 0000000..c5fec7f
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.TabWidget", attribute = "android:divider", method = "setDividerDrawable"),
+        @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripEnabled", method = "setStripEnabled"),
+        @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripLeft", method = "setLeftStripDrawable"),
+        @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripRight", method = "setRightStripDrawable"),
+})
+public class TabWidgetBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java
new file mode 100644
index 0000000..e0c7591
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.util.SparseBooleanArray;
+import android.widget.TableLayout;
+
+import java.util.regex.Pattern;
+
+public class TableLayoutBindingAdapter {
+
+    private static Pattern sColumnPattern = Pattern.compile("\\s*,\\s*");
+
+    private static final int MAX_COLUMNS = 20;
+
+    @BindingAdapter("android:collapseColumns")
+    public static void setCollapseColumns(TableLayout view, CharSequence columnsStr) {
+        SparseBooleanArray columns = parseColumns(columnsStr);
+        for (int i = 0; i < MAX_COLUMNS; i++) {
+            boolean isCollapsed = columns.get(i, false);
+            if (isCollapsed != view.isColumnCollapsed(i)) {
+                view.setColumnCollapsed(i, isCollapsed);
+            }
+        }
+    }
+
+    @BindingAdapter("android:shrinkColumns")
+    public static void setShrinkColumns(TableLayout view, CharSequence columnsStr) {
+        if (columnsStr != null && columnsStr.length() > 0 && columnsStr.charAt(0) == '*') {
+            view.setShrinkAllColumns(true);
+        } else {
+            view.setShrinkAllColumns(false);
+            SparseBooleanArray columns = parseColumns(columnsStr);
+            int columnCount = columns.size();
+            for (int i = 0; i < columnCount; i++) {
+                int column = columns.keyAt(i);
+                boolean shrinkable = columns.valueAt(i);
+                if (shrinkable) {
+                    view.setColumnShrinkable(column, shrinkable);
+                }
+            }
+        }
+    }
+
+    @BindingAdapter("android:stretchColumns")
+    public static void setStretchColumns(TableLayout view, CharSequence columnsStr) {
+        if (columnsStr != null && columnsStr.length() > 0 && columnsStr.charAt(0) == '*') {
+            view.setStretchAllColumns(true);
+        } else {
+            view.setStretchAllColumns(false);
+            SparseBooleanArray columns = parseColumns(columnsStr);
+            int columnCount = columns.size();
+            for (int i = 0; i < columnCount; i++) {
+                int column = columns.keyAt(i);
+                boolean stretchable = columns.valueAt(i);
+                if (stretchable) {
+                    view.setColumnStretchable(column, stretchable);
+                }
+            }
+        }
+    }
+
+    private static SparseBooleanArray parseColumns(CharSequence sequence) {
+        SparseBooleanArray columns = new SparseBooleanArray();
+        if (sequence == null) {
+            return columns;
+        }
+        String[] columnDefs = sColumnPattern.split(sequence);
+
+        for (String columnIdentifier : columnDefs) {
+            try {
+                int columnIndex = Integer.parseInt(columnIdentifier);
+                // only valid, i.e. positive, columns indexes are handled
+                if (columnIndex >= 0) {
+                    // putting true in this sparse array indicates that the
+                    // column index was defined in the XML file
+                    columns.put(columnIndex, true);
+                }
+            } catch (NumberFormatException e) {
+                // we just ignore columns that don't exist
+            }
+        }
+
+        return columns;
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java
new file mode 100644
index 0000000..3683916
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java
@@ -0,0 +1,283 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.method.DialerKeyListener;
+import android.text.method.DigitsKeyListener;
+import android.text.method.KeyListener;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TextKeyListener;
+import android.util.Log;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+@BindingMethods({
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:autoLink", method = "setAutoLinkMask"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:editorExtras", method = "setInputExtras"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:inputType", method = "setRawInputType"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:textAllCaps", method = "setAllCaps"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorHighlight", method = "setHighlightColor"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorHint", method = "setHintTextColor"),
+        @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorLink", method = "setLinkTextColor"),
+})
+public class TextViewBindingAdapter {
+
+    private static final String TAG = "TextViewBindingAdapters";
+
+    public static final int INTEGER = 0x01;
+
+    public static final int SIGNED = 0x03;
+
+    public static final int DECIMAL = 0x05;
+
+    @BindingAdapter("android:autoText")
+    public static void setAutoText(TextView view, boolean autoText) {
+        KeyListener listener = view.getKeyListener();
+
+        TextKeyListener.Capitalize capitalize = TextKeyListener.Capitalize.NONE;
+
+        int inputType = listener != null ? listener.getInputType() : 0;
+        if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
+            capitalize = TextKeyListener.Capitalize.CHARACTERS;
+        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) {
+            capitalize = TextKeyListener.Capitalize.WORDS;
+        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) {
+            capitalize = TextKeyListener.Capitalize.SENTENCES;
+        }
+        view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize));
+    }
+
+    @BindingAdapter("android:capitalize")
+    public static void setCapitalize(TextView view, TextKeyListener.Capitalize capitalize) {
+        KeyListener listener = view.getKeyListener();
+
+        int inputType = listener.getInputType();
+        boolean autoText = (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0;
+        view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize));
+    }
+
+    @BindingAdapter("android:bufferType")
+    public static void setBufferType(TextView view, TextView.BufferType bufferType) {
+        view.setText(view.getText(), bufferType);
+    }
+
+    @BindingAdapter("android:digits")
+    public static void setDigits(TextView view, CharSequence digits) {
+        if (digits != null) {
+            view.setKeyListener(DigitsKeyListener.getInstance(digits.toString()));
+        } else if (view.getKeyListener() instanceof DigitsKeyListener) {
+            view.setKeyListener(null);
+        }
+    }
+
+    @BindingAdapter("android:numeric")
+    public static void setNumeric(TextView view, int numeric) {
+        view.setKeyListener(DigitsKeyListener.getInstance((numeric & SIGNED) != 0,
+                (numeric & DECIMAL) != 0));
+    }
+
+    @BindingAdapter("android:phoneNumber")
+    public static void setPhoneNumber(TextView view, boolean phoneNumber) {
+        if (phoneNumber) {
+            view.setKeyListener(DialerKeyListener.getInstance());
+        } else if (view.getKeyListener() instanceof DialerKeyListener) {
+            view.setKeyListener(null);
+        }
+    }
+
+    @BindingAdapter("android:drawableBottom")
+    public static void setDrawableBottom(TextView view, Drawable drawable) {
+        Drawable[] drawables = view.getCompoundDrawables();
+        view.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawable);
+    }
+
+    @BindingAdapter("android:drawableLeft")
+    public static void setDrawableLeft(TextView view, Drawable drawable) {
+        Drawable[] drawables = view.getCompoundDrawables();
+        view.setCompoundDrawables(drawable, drawables[1], drawables[2], drawables[3]);
+    }
+
+    @BindingAdapter("android:drawableRight")
+    public static void setDrawableRight(TextView view, Drawable drawable) {
+        Drawable[] drawables = view.getCompoundDrawables();
+        view.setCompoundDrawables(drawables[0], drawables[1], drawable, drawables[3]);
+    }
+
+    @BindingAdapter("android:drawableTop")
+    public static void setDrawableTop(TextView view, Drawable drawable) {
+        Drawable[] drawables = view.getCompoundDrawables();
+        view.setCompoundDrawables(drawables[0], drawable, drawables[2], drawables[3]);
+    }
+
+    @BindingAdapter("android:drawableStart")
+    public static void setDrawableStart(TextView view, Drawable drawable) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            setDrawableLeft(view, drawable);
+        } else {
+            Drawable[] drawables = view.getCompoundDrawablesRelative();
+            view.setCompoundDrawablesRelative(drawable, drawables[1], drawables[2], drawables[3]);
+        }
+    }
+
+    @BindingAdapter("android:drawableEnd")
+    public static void setDrawableEnd(TextView view, Drawable drawable) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            setDrawableRight(view, drawable);
+        } else {
+            Drawable[] drawables = view.getCompoundDrawablesRelative();
+            view.setCompoundDrawablesRelative(drawables[0], drawables[1], drawable, drawables[3]);
+        }
+    }
+
+    @BindingAdapter("android:imeActionLabel")
+    public static void setImeActionLabel(TextView view, CharSequence value) {
+        view.setImeActionLabel(value, view.getImeActionId());
+    }
+
+    @BindingAdapter("android:imeActionId")
+    public static void setImeActionLabel(TextView view, int value) {
+        view.setImeActionLabel(view.getImeActionLabel(), value);
+    }
+
+    @BindingAdapter("android:inputMethod")
+    public static void setInputMethod(TextView view, CharSequence inputMethod) {
+        try {
+            Class<?> c = Class.forName(inputMethod.toString());
+            view.setKeyListener((KeyListener) c.newInstance());
+        } catch (ClassNotFoundException e) {
+            Log.e(TAG, "Could not create input method: " + inputMethod, e);
+        } catch (InstantiationException e) {
+            Log.e(TAG, "Could not create input method: " + inputMethod, e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Could not create input method: " + inputMethod, e);
+        }
+    }
+
+    @BindingAdapter("android:lineSpacingExtra")
+    public static void setLineSpacingExtra(TextView view, float value) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            view.setLineSpacing(value, view.getLineSpacingMultiplier());
+        } else {
+            view.setLineSpacing(value, 1);
+        }
+    }
+
+    @BindingAdapter("android:lineSpacingMultiplier")
+    public static void setLineSpacingMultiplier(TextView view, float value) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            view.setLineSpacing(view.getLineSpacingExtra(), value);
+        } else {
+            view.setLineSpacing(0, value);
+        }
+    }
+
+    @BindingAdapter("android:maxLength")
+    public static void setMaxLength(TextView view, int value) {
+        InputFilter[] filters = view.getFilters();
+        if (filters == null) {
+            filters = new InputFilter[]{
+                    new InputFilter.LengthFilter(value)
+            };
+        } else {
+            boolean foundMaxLength = false;
+            for (int i = 0; i < filters.length; i++) {
+                InputFilter filter = filters[i];
+                if (filter instanceof InputFilter.LengthFilter) {
+                    foundMaxLength = true;
+                    boolean replace = true;
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                        replace = ((InputFilter.LengthFilter) filter).getMax() != value;
+                    }
+                    if (replace) {
+                        filters[i] = new InputFilter.LengthFilter(value);
+                    }
+                    break;
+                }
+            }
+            if (!foundMaxLength) {
+                // can't use Arrays.copyOf -- it shows up in API 9
+                InputFilter[] oldFilters = filters;
+                filters = new InputFilter[oldFilters.length + 1];
+                System.arraycopy(oldFilters, 0, filters, 0, oldFilters.length);
+                filters[filters.length - 1] = new InputFilter.LengthFilter(value);
+            }
+        }
+        view.setFilters(filters);
+    }
+
+    @BindingAdapter("android:password")
+    public static void setPassword(TextView view, boolean password) {
+        if (password) {
+            view.setTransformationMethod(PasswordTransformationMethod.getInstance());
+        } else if (view.getTransformationMethod() instanceof PasswordTransformationMethod) {
+            view.setTransformationMethod(null);
+        }
+    }
+
+    @BindingAdapter("android:shadowColor")
+    public static void setShadowColor(TextView view, int color) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            float dx = view.getShadowDx();
+            float dy = view.getShadowDy();
+            float r = view.getShadowRadius();
+            view.setShadowLayer(r, dx, dy, color);
+        }
+    }
+
+    @BindingAdapter("android:shadowDx")
+    public static void setShadowDx(TextView view, float dx) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            int color = view.getShadowColor();
+            float dy = view.getShadowDy();
+            float r = view.getShadowRadius();
+            view.setShadowLayer(r, dx, dy, color);
+        }
+    }
+
+    @BindingAdapter("android:shadowDy")
+    public static void setShadowDy(TextView view, float dy) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            int color = view.getShadowColor();
+            float dx = view.getShadowDx();
+            float r = view.getShadowRadius();
+            view.setShadowLayer(r, dx, dy, color);
+        }
+    }
+
+    @BindingAdapter("android:shadowRadius")
+    public static void setShadowRadius(TextView view, float r) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            int color = view.getShadowColor();
+            float dx = view.getShadowDx();
+            float dy = view.getShadowDy();
+            view.setShadowLayer(r, dx, dy, color);
+        }
+    }
+
+    @BindingAdapter("android:textSize")
+    public static void setTextSize(TextView view, float size) {
+        view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java
new file mode 100644
index 0000000..e8b9e43
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java
@@ -0,0 +1,103 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.os.Build;
+import android.view.View;
+
+@BindingMethods({
+        @BindingMethod(type = "android.view.View", attribute = "android:backgroundTint", method = "setBackgroundTintList"),
+        @BindingMethod(type = "android.view.View", attribute = "android:fadeScrollbars", method = "setScrollbarFadingEnabled"),
+        @BindingMethod(type = "android.view.View", attribute = "android:nextFocusForward", method = "setNextFocusForwardId"),
+        @BindingMethod(type = "android.view.View", attribute = "android:nextFocusLeft", method = "setNextFocusLeftId"),
+        @BindingMethod(type = "android.view.View", attribute = "android:nextFocusRight", method = "setNextFocusRightId"),
+        @BindingMethod(type = "android.view.View", attribute = "android:nextFocusUp", method = "setNextFocusUpId"),
+        @BindingMethod(type = "android.view.View", attribute = "android:nextFocusDown", method = "setNextFocusDownId"),
+        @BindingMethod(type = "android.view.View", attribute = "android:requiresFadingEdge", method = "setVerticalFadingEdgeEnabled"),
+        @BindingMethod(type = "android.view.View", attribute = "android:scrollbarDefaultDelayBeforeFade", method = "setScrollBarDefaultDelayBeforeFade"),
+        @BindingMethod(type = "android.view.View", attribute = "android:scrollbarFadeDuration", method = "setScrollBarFadeDuration"),
+        @BindingMethod(type = "android.view.View", attribute = "android:scrollbarSize", method = "setScrollBarSize"),
+        @BindingMethod(type = "android.view.View", attribute = "android:scrollbarStyle", method = "setScrollBarStyle"),
+        @BindingMethod(type = "android.view.View", attribute = "android:transformPivotX", method = "setPivotX"),
+        @BindingMethod(type = "android.view.View", attribute = "android:transformPivotY", method = "setPivotY"),
+})
+public class ViewBindingAdapter {
+    public static int FADING_EDGE_NONE = 0;
+    public static int FADING_EDGE_HORIZONTAL = 1;
+    public static int FADING_EDGE_VERTICAL = 2;
+
+    @BindingAdapter("android:padding")
+    public static void setPadding(View view, int padding) {
+        view.setPadding(padding, padding, padding, padding);
+    }
+
+    @BindingAdapter("android:paddingBottom")
+    public static void setPaddingBottom(View view, int padding) {
+        view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(),
+                padding);
+    }
+
+    @BindingAdapter("android:paddingEnd")
+    public static void setPaddingEnd(View view, int padding) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), padding,
+                    view.getPaddingBottom());
+        } else {
+            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
+                    view.getPaddingBottom());
+        }
+    }
+
+    @BindingAdapter("android:paddingLeft")
+    public static void setPaddingLeft(View view, int padding) {
+        view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
+                view.getPaddingBottom());
+    }
+
+    @BindingAdapter("android:paddingRight")
+    public static void setPaddingRight(View view, int padding) {
+        view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
+                view.getPaddingBottom());
+    }
+
+    @BindingAdapter("android:paddingStart")
+    public static void setPaddingStart(View view, int padding) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            view.setPaddingRelative(padding, view.getPaddingTop(), view.getPaddingEnd(),
+                    view.getPaddingBottom());
+        } else {
+            view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
+                    view.getPaddingBottom());
+        }
+    }
+
+    @BindingAdapter("android:paddingTop")
+    public static void setPaddingTop(View view, int padding) {
+        view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(),
+                view.getPaddingBottom());
+    }
+
+    @BindingAdapter("android:requiresFadingEdge")
+    public static void setRequiresFadingEdge(View view, int value) {
+        final boolean vertical = (value & FADING_EDGE_VERTICAL) != 0;
+        final boolean horizontal = (value & FADING_EDGE_HORIZONTAL) != 0;
+        view.setVerticalFadingEdgeEnabled(vertical);
+        view.setHorizontalFadingEdgeEnabled(horizontal);
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java
new file mode 100644
index 0000000..aaad4d1
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.animation.LayoutTransition;
+import android.annotation.TargetApi;
+import android.databinding.BindingAdapter;
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.os.Build;
+import android.view.ViewGroup;
+
+@BindingMethods({
+        @BindingMethod(type = "android.view.ViewGroup", attribute = "android:alwaysDrawnWithCache", method = "setAlwaysDrawnWithCacheEnabled"),
+        @BindingMethod(type = "android.view.ViewGroup", attribute = "android:animationCache", method = "setAnimationCacheEnabled"),
+        @BindingMethod(type = "android.view.ViewGroup", attribute = "android:splitMotionEvents", method = "setMotionEventSplittingEnabled"),
+})
+public class ViewGroupBindingAdapter {
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @BindingAdapter("android:animateLayoutChanges")
+    public static void setAnimateLayoutChanges(ViewGroup view, boolean animate) {
+        if (animate) {
+            view.setLayoutTransition(new LayoutTransition());
+        } else {
+            view.setLayoutTransition(null);
+        }
+    }
+}
diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java
new file mode 100644
index 0000000..37864cc
--- /dev/null
+++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.databinding.adapters;
+
+import android.databinding.BindingMethod;
+import android.databinding.BindingMethods;
+import android.databinding.Untaggable;
+
+@Untaggable({"android.view.ViewStub"})
+@BindingMethods({
+        @BindingMethod(type = "android.view.ViewStub", attribute = "android:layout", method = "setLayoutResource")
+})
+public class ViewStubBindingAdapter {
+
+}
diff --git a/tools/data-binding/extensions/build.gradle b/tools/data-binding/extensions/build.gradle
new file mode 100644
index 0000000..cfa2697
--- /dev/null
+++ b/tools/data-binding/extensions/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+    }
+}
+
+subprojects {
+    apply plugin: 'maven'
+    group = config.group
+    version = config.snapshotVersion
+    repositories {
+        mavenCentral()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e5fd879
--- /dev/null
+++ b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Mar 12 15:27:48 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/extensions/gradlew b/tools/data-binding/extensions/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/extensions/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/extensions/settings.gradle b/tools/data-binding/extensions/settings.gradle
new file mode 100644
index 0000000..80ebcc8
--- /dev/null
+++ b/tools/data-binding/extensions/settings.gradle
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * These are projects that requires a compiled version of data binding.
+ */
+include ':baseAdapters'
diff --git a/tools/data-binding/gradle.properties b/tools/data-binding/gradle.properties
new file mode 100644
index 0000000..24f728c
--- /dev/null
+++ b/tools/data-binding/gradle.properties
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2014 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=1024m -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+#org.gradle.parallel=true
+org.gradle.daemon=true
diff --git a/tools/data-binding/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e5fd879
--- /dev/null
+++ b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Mar 12 15:27:48 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/gradlePlugin/build.gradle b/tools/data-binding/gradlePlugin/build.gradle
new file mode 100644
index 0000000..bdfdf79
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+apply plugin: "kotlin"
+apply plugin: 'maven'
+
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}"
+    }
+}
+
+dependencies {
+    compile "com.android.tools.build:gradle:${config.androidPluginVersion}"
+    compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}"
+    compile gradleApi()
+    compile 'commons-io:commons-io:2.4'
+    compile 'commons-codec:commons-codec:1.10'
+    compile project(":compiler")
+}
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'dataBinder'
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3d0dee6
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..8b80a06
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Dec 11 16:01:54 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
diff --git a/tools/data-binding/gradlePlugin/gradlew b/tools/data-binding/gradlePlugin/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/gradlePlugin/gradlew.bat b/tools/data-binding/gradlePlugin/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt b/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt
new file mode 100644
index 0000000..f3e1f1d
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt
@@ -0,0 +1,39 @@
+/*
+ * 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 android.databinding.tool
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.TaskAction
+import kotlin.properties.Delegates
+import android.databinding.tool.util.Log
+import java.io.File
+
+open class DataBindingProcessLayoutsTask : DefaultTask() {
+    {
+        Log.d {"created data binding task"}
+    }
+    var xmlProcessor: LayoutXmlProcessor by Delegates.notNull()
+    var sdkDir : File by Delegates.notNull()
+    [TaskAction]
+    public fun doIt() {
+        Log.d {"running process layouts task"}
+        xmlProcessor.processResources()
+    }
+
+    public fun writeFiles(xmlOutFolder : File) {
+        xmlProcessor.writeIntermediateFile(sdkDir, xmlOutFolder)
+    }
+}
\ No newline at end of file
diff --git a/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt b/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt
new file mode 100644
index 0000000..5515adf
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 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
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.internal.api.ApplicationVariantImpl
+import com.android.build.gradle.internal.variant.ApplicationVariantData
+import java.io.File
+import org.gradle.api.file.FileCollection
+import android.databinding.tool.writer.JavaFileWriter
+import android.databinding.tool.util.Log
+import org.gradle.api.Action
+import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.api.LibraryVariant
+import com.android.build.gradle.api.ApplicationVariant
+import com.android.build.gradle.internal.variant.BaseVariantData
+import com.android.build.gradle.internal.variant.LibraryVariantData
+import com.android.build.gradle.internal.api.LibraryVariantImpl
+import com.android.build.gradle.api.TestVariant
+import com.android.build.gradle.internal.variant.TestVariantData
+import com.android.build.gradle.internal.api.TestVariantImpl
+
+class DataBinderPlugin : Plugin<Project> {
+
+    inner class GradleFileWriter(var outputBase: String) : JavaFileWriter() {
+        override fun writeToFile(canonicalName: String, contents: String) {
+            val f = File("$outputBase/${canonicalName.replaceAll("\\.", "/")}.java")
+            log("Asked to write to ${canonicalName}. outputting to:${f.getAbsolutePath()}")
+            f.getParentFile().mkdirs()
+            f.writeText(contents, "utf-8")
+        }
+    }
+
+    override fun apply(project: Project?) {
+        if (project == null) return
+        project.afterEvaluate {
+            createXmlProcessor(project)
+        }
+    }
+
+    fun log(s: String) {
+        System.out.println("[qwqw data binding]: $s")
+    }
+
+    fun createXmlProcessor(p: Project) {
+        val androidExt = p.getExtensions().getByName("android")
+        if (androidExt !is BaseExtension) {
+            return
+        }
+        log("project build dir:${p.getBuildDir()}")
+        // TODO this will differ per flavor
+
+        if (androidExt is AppExtension) {
+            createXmlProcessorForApp(p, androidExt)
+        } else if (androidExt is LibraryExtension) {
+            createXmlProcessorForLibrary(p, androidExt)
+        } else {
+            throw RuntimeException("cannot understand android extension. What is it? ${androidExt}")
+        }
+    }
+
+    fun createXmlProcessorForLibrary(project : Project, lib : LibraryExtension) {
+        val sdkDir = lib.getSdkDirectory()
+        lib.getTestVariants().forEach { variant ->
+            log("test variant $variant. dir name ${variant.getDirName()}")
+            val variantData = getVariantData(variant)
+            attachXmlProcessor(project, variantData, sdkDir, false)//tests extend apk variant
+        }
+        lib.getLibraryVariants().forEach { variant ->
+            log("lib variant $variant . dir name ${variant.getDirName()}")
+            val variantData = getVariantData(variant)
+            attachXmlProcessor(project, variantData, sdkDir, true)
+        }
+    }
+
+    fun getVariantData(appVariant : LibraryVariant) : LibraryVariantData {
+        val clazz = javaClass<LibraryVariantImpl>()
+        val field = clazz.getDeclaredField("variantData")
+        field.setAccessible(true)
+        return field.get(appVariant) as LibraryVariantData
+    }
+
+    fun getVariantData(testVariant : TestVariant) : TestVariantData {
+        val clazz = javaClass<TestVariantImpl>()
+        val field = clazz.getDeclaredField("variantData")
+        field.setAccessible(true)
+        return field.get(testVariant) as TestVariantData
+    }
+
+    fun getVariantData(appVariant : ApplicationVariant) : ApplicationVariantData {
+        val clazz = javaClass<ApplicationVariantImpl>()
+        val field = clazz.getDeclaredField("variantData")
+        field.setAccessible(true)
+        return field.get(appVariant) as ApplicationVariantData
+    }
+
+    fun createXmlProcessorForApp(project : Project, appExt: AppExtension) {
+        val sdkDir = appExt.getSdkDirectory()
+        appExt.getTestVariants().forEach { testVariant ->
+            val variantData = getVariantData(testVariant)
+            attachXmlProcessor(project, variantData, sdkDir, false)
+        }
+        appExt.getApplicationVariants().forEach { appVariant ->
+            val variantData = getVariantData(appVariant)
+            attachXmlProcessor(project, variantData, sdkDir, false)
+        }
+    }
+
+    fun attachXmlProcessor(project : Project, variantData : BaseVariantData<*>, sdkDir : File,
+            isLibrary : Boolean) {
+        val configuration = variantData.getVariantConfiguration()
+        val minSdkVersion = configuration.getMinSdkVersion()
+        val generateRTask = variantData.generateRClassTask
+        val packageName = generateRTask.getPackageForR()
+        log("r task name $generateRTask . text symbols output dir: ${generateRTask.getTextSymbolOutputDir()}")
+        val fullName = configuration.getFullName()
+        val sources = variantData.getJavaSources()
+        sources.forEach({
+            if (it is FileCollection) {
+                it.forEach {
+                    log("sources for ${variantData} ${it}}")
+                }
+            } else {
+                log("sources for ${variantData}: ${it}");
+            }
+        })
+        val resourceFolders = arrayListOf(variantData.mergeResourcesTask.getOutputDir())
+        log("MERGE RES OUTPUT ${variantData.mergeResourcesTask.getOutputDir()}")
+        val codeGenTargetFolder = generateRTask.getSourceOutputDir()
+        // TODO unnecessary?
+
+        // TODO attach to test module as well!
+
+        variantData.addJavaSourceFoldersToModel(codeGenTargetFolder)
+        val writerOutBase = codeGenTargetFolder.getAbsolutePath();
+        val fileWriter = GradleFileWriter(writerOutBase)
+        val xmlProcessor = LayoutXmlProcessor(packageName, resourceFolders, fileWriter,
+                minSdkVersion.getApiLevel(), isLibrary)
+        val processResTask = generateRTask
+
+        val xmlOutDir = "${project.getBuildDir()}/layout-info/${configuration.getDirName()}";
+        log("xml output for ${variantData} is ${xmlOutDir}")
+        val dataBindingTaskName = "dataBinding${processResTask.getName().capitalize()}"
+        log("created task $dataBindingTaskName")
+        project.getTasks().create(dataBindingTaskName,
+                javaClass<DataBindingProcessLayoutsTask>(),
+                object : Action<DataBindingProcessLayoutsTask> {
+                    override fun execute(task: DataBindingProcessLayoutsTask) {
+                        task.xmlProcessor = xmlProcessor
+                        task.sdkDir = sdkDir
+                        Log.d { "TASK adding dependency on ${task} for ${processResTask}" }
+                        processResTask.dependsOn(task)
+                        processResTask.getDependsOn().filterNot { it == task }.forEach {
+                            Log.d { "adding dependency on ${it} for ${task}" }
+                            task.dependsOn(it)
+                        }
+                        processResTask.doLast {
+                            task.writeFiles(File(xmlOutDir))
+                        }
+                    }
+                });
+
+        if (isLibrary) {
+            val packageJarTaskName = "package${fullName.capitalize()}Jar"
+            val packageTask = project.getTasks().findByName(packageJarTaskName)
+            if (packageTask !is org.gradle.api.tasks.bundling.Jar) {
+                throw RuntimeException("cannot find package task in $project $variantData project $packageJarTaskName")
+            }
+            val excludePattern = "android/databinding/layouts/*.*"
+            val appPkgAsClass = packageName.replace('.', '/')
+            packageTask.exclude(excludePattern)
+            packageTask.exclude("$appPkgAsClass/databinding/*")
+            packageTask.exclude("$appPkgAsClass/BR.*")
+            packageTask.exclude(xmlProcessor.getInfoClassFullName().replace('.', '/') + ".class")
+            log("excludes ${packageTask.getExcludes()}")
+        }
+    }
+}
diff --git a/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties b/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties
new file mode 100644
index 0000000..2e04d00
--- /dev/null
+++ b/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2014 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.
+#
+
+implementation-class=android.databinding.tool.DataBinderPlugin
\ No newline at end of file
diff --git a/tools/data-binding/gradlew b/tools/data-binding/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/grammarBuilder/BindingExpression.g4 b/tools/data-binding/grammarBuilder/BindingExpression.g4
new file mode 100644
index 0000000..3142507
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/BindingExpression.g4
@@ -0,0 +1,488 @@
+/*
+ [The "BSD licence"]
+ Copyright (c) 2013 Terence Parr, Sam Harwell
+ All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+grammar BindingExpression;
+
+bindingSyntax
+    :   expression defaults?
+    ;
+
+defaults
+    :   ',' 'default' '=' constantValue
+    ;
+constantValue
+    :   literal
+    |   ResourceReference
+    |   identifier
+    ;
+
+expression
+    :   '(' expression ')'                              # Grouping
+// this isn't allowed yet.
+//    |   THIS                                            # Primary
+    |   literal                                         # Primary
+    |   identifier                                      # Primary
+    |   classExtraction                                 # Primary
+    |   resources                                       # Resource
+//    |   typeArguments (explicitGenericInvocationSuffix | 'this' arguments) # GenericCall
+    |   expression '.' Identifier                       # DotOp
+//    |   expression '.' 'this'                           # ThisReference
+//    |   expression '.' explicitGenericInvocation        # ExplicitGenericInvocationOp
+    |   expression '[' expression ']'                   # BracketOp
+    |   target=expression '.' methodName=Identifier '(' args=expressionList? ')' # MethodInvocation
+    |   '(' type ')' expression                         # CastOp
+    |   op=('+'|'-') expression                         # UnaryOp
+    |   op=('~'|'!') expression                         # UnaryOp
+    |   left=expression op=('*'|'/'|'%') right=expression             # MathOp
+    |   left=expression op=('+'|'-') right=expression                 # MathOp
+    |   left=expression op=('<<' | '>>>' | '>>') right=expression     # BitShiftOp
+    |   left=expression op=('<=' | '>=' | '>' | '<') right=expression # ComparisonOp
+    |   expression 'instanceof' type                    # InstanceOfOp
+    |   left=expression op=('==' | '!=') right=expression             # ComparisonOp
+    |   left=expression op='&' right=expression                       # BinaryOp
+    |   left=expression op='^' right=expression                       # BinaryOp
+    |   left=expression op='|' right=expression                       # BinaryOp
+    |   left=expression op='&&' right=expression                      # AndOrOp
+    |   left=expression op='||' right=expression                      # AndOrOp
+    |   left=expression op='?' iftrue=expression ':' iffalse=expression        # TernaryOp
+    |   left=expression op='??' right=expression                      # QuestionQuestionOp
+    ;
+
+THIS
+    :   'this'
+    ;
+
+classExtraction
+    :   type '.' 'class'
+    |   'void' '.' 'class'
+    ;
+
+expressionList
+    :   expression (',' expression)*
+    ;
+
+literal
+    :   javaLiteral
+    |   stringLiteral
+    ;
+
+identifier
+    :   Identifier
+    ;
+
+javaLiteral
+    :   IntegerLiteral
+    |   FloatingPointLiteral
+    |   BooleanLiteral
+    |   NullLiteral
+    |   CharacterLiteral
+    ;
+
+stringLiteral
+    :   SingleQuoteString
+    |   DoubleQuoteString
+    ;
+
+explicitGenericInvocation
+    :   typeArguments explicitGenericInvocationSuffix
+    ;
+
+typeArguments
+    :   '<' type (',' type)* '>'
+    ;
+
+type
+    :   classOrInterfaceType ('[' ']')*
+    |   primitiveType ('[' ']')*
+    ;
+
+explicitGenericInvocationSuffix
+    :   Identifier arguments
+    ;
+
+arguments
+    :   '(' expressionList? ')'
+    ;
+
+classOrInterfaceType
+    :   identifier typeArguments? ('.' Identifier typeArguments? )*
+    ;
+
+primitiveType
+    :   'boolean'
+    |   'char'
+    |   'byte'
+    |   'short'
+    |   'int'
+    |   'long'
+    |   'float'
+    |   'double'
+    ;
+
+resources
+    :   ResourceReference resourceParameters?
+    ;
+
+resourceParameters
+    :   '(' expressionList ')'
+    ;
+
+// LEXER
+
+// §3.10.1 Integer Literals
+
+IntegerLiteral
+    :   DecimalIntegerLiteral
+    |   HexIntegerLiteral
+    |   OctalIntegerLiteral
+    |   BinaryIntegerLiteral
+    ;
+
+fragment
+DecimalIntegerLiteral
+    :   DecimalNumeral IntegerTypeSuffix?
+    ;
+
+fragment
+HexIntegerLiteral
+    :   HexNumeral IntegerTypeSuffix?
+    ;
+
+fragment
+OctalIntegerLiteral
+    :   OctalNumeral IntegerTypeSuffix?
+    ;
+
+fragment
+BinaryIntegerLiteral
+    :   BinaryNumeral IntegerTypeSuffix?
+    ;
+
+fragment
+IntegerTypeSuffix
+    :   [lL]
+    ;
+
+fragment
+DecimalNumeral
+    :   '0'
+    |   NonZeroDigit (Digits? | Underscores Digits)
+    ;
+
+fragment
+Digits
+    :   Digit (DigitOrUnderscore* Digit)?
+    ;
+
+fragment
+Digit
+    :   '0'
+    |   NonZeroDigit
+    ;
+
+fragment
+NonZeroDigit
+    :   [1-9]
+    ;
+
+fragment
+DigitOrUnderscore
+    :   Digit
+    |   '_'
+    ;
+
+fragment
+Underscores
+    :   '_'+
+    ;
+
+fragment
+HexNumeral
+    :   '0' [xX] HexDigits
+    ;
+
+fragment
+HexDigits
+    :   HexDigit (HexDigitOrUnderscore* HexDigit)?
+    ;
+
+fragment
+HexDigit
+    :   [0-9a-fA-F]
+    ;
+
+fragment
+HexDigitOrUnderscore
+    :   HexDigit
+    |   '_'
+    ;
+
+fragment
+OctalNumeral
+    :   '0' Underscores? OctalDigits
+    ;
+
+fragment
+OctalDigits
+    :   OctalDigit (OctalDigitOrUnderscore* OctalDigit)?
+    ;
+
+fragment
+OctalDigit
+    :   [0-7]
+    ;
+
+fragment
+OctalDigitOrUnderscore
+    :   OctalDigit
+    |   '_'
+    ;
+
+fragment
+BinaryNumeral
+    :   '0' [bB] BinaryDigits
+    ;
+
+fragment
+BinaryDigits
+    :   BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)?
+    ;
+
+fragment
+BinaryDigit
+    :   [01]
+    ;
+
+fragment
+BinaryDigitOrUnderscore
+    :   BinaryDigit
+    |   '_'
+    ;
+
+// §3.10.2 Floating-Point Literals
+
+FloatingPointLiteral
+    :   DecimalFloatingPointLiteral
+    |   HexadecimalFloatingPointLiteral
+    ;
+
+fragment
+DecimalFloatingPointLiteral
+    :   Digits '.' Digits? ExponentPart? FloatTypeSuffix?
+    |   '.' Digits ExponentPart? FloatTypeSuffix?
+    |   Digits ExponentPart FloatTypeSuffix?
+    |   Digits FloatTypeSuffix
+    ;
+
+fragment
+ExponentPart
+    :   ExponentIndicator SignedInteger
+    ;
+
+fragment
+ExponentIndicator
+    :   [eE]
+    ;
+
+fragment
+SignedInteger
+    :   Sign? Digits
+    ;
+
+fragment
+Sign
+    :   [+-]
+    ;
+
+fragment
+FloatTypeSuffix
+    :   [fFdD]
+    ;
+
+fragment
+HexadecimalFloatingPointLiteral
+    :   HexSignificand BinaryExponent FloatTypeSuffix?
+    ;
+
+fragment
+HexSignificand
+    :   HexNumeral '.'?
+    |   '0' [xX] HexDigits? '.' HexDigits
+    ;
+
+fragment
+BinaryExponent
+    :   BinaryExponentIndicator SignedInteger
+    ;
+
+fragment
+BinaryExponentIndicator
+    :   [pP]
+    ;
+
+// §3.10.3 Boolean Literals
+
+BooleanLiteral
+    :   'true'
+    |   'false'
+    ;
+
+// §3.10.4 Character Literals
+
+CharacterLiteral
+    :   '\'' SingleCharacter '\''
+    |   '\'' EscapeSequence '\''
+    ;
+
+fragment
+SingleCharacter
+    :   ~['\\]
+    ;
+// §3.10.5 String Literals
+SingleQuoteString
+    :   '`' SingleQuoteStringCharacter* '`'
+    ;
+
+DoubleQuoteString
+    :   '"' StringCharacters? '"'
+    ;
+
+fragment
+StringCharacters
+    :   StringCharacter+
+    ;
+fragment
+StringCharacter
+    :   ~["\\]
+    |   EscapeSequence
+    ;
+fragment
+SingleQuoteStringCharacter
+    :   ~[`\\]
+    |   EscapeSequence
+    ;
+
+// §3.10.6 Escape Sequences for Character and String Literals
+fragment
+EscapeSequence
+    :   '\\' [btnfr"'`\\]
+    |   OctalEscape
+    |   UnicodeEscape
+    ;
+
+fragment
+OctalEscape
+    :   '\\' OctalDigit
+    |   '\\' OctalDigit OctalDigit
+    |   '\\' ZeroToThree OctalDigit OctalDigit
+    ;
+
+fragment
+UnicodeEscape
+    :   '\\' 'u' HexDigit HexDigit HexDigit HexDigit
+    ;
+
+fragment
+ZeroToThree
+    :   [0-3]
+    ;
+
+// §3.10.7 The Null Literal
+
+NullLiteral
+    :   'null'
+    ;
+
+// §3.8 Identifiers (must appear after all keywords in the grammar)
+
+Identifier
+    :   JavaLetter JavaLetterOrDigit*
+    ;
+
+fragment
+JavaLetter
+    :   [a-zA-Z$_] // these are the "java letters" below 0xFF
+    |   // covers all characters above 0xFF which are not a surrogate
+        ~[\u0000-\u00FF\uD800-\uDBFF]
+        {Character.isJavaIdentifierStart(_input.LA(-1))}?
+    |   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
+        [\uD800-\uDBFF] [\uDC00-\uDFFF]
+        {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
+    ;
+
+fragment
+JavaLetterOrDigit
+    :   [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF
+    |   // covers all characters above 0xFF which are not a surrogate
+        ~[\u0000-\u00FF\uD800-\uDBFF]
+        {Character.isJavaIdentifierPart(_input.LA(-1))}?
+    |   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
+        [\uD800-\uDBFF] [\uDC00-\uDFFF]
+        {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
+    ;
+
+//
+// Whitespace and comments
+//
+
+WS  :  [ \t\r\n\u000C]+ -> skip
+    ;
+
+//
+// Resource references
+//
+
+ResourceReference
+    :   '@' (PackageName ':')? ResourceType '/' Identifier
+    ;
+
+PackageName
+    :   'android'
+    |   Identifier
+    ;
+
+ResourceType
+    :   'anim'
+    |   'animator'
+    |   'bool'
+    |   'color'
+    |   'colorStateList'
+    |   'dimen'
+    |   'dimenOffset'
+    |   'dimenSize'
+    |   'drawable'
+    |   'fraction'
+    |   'id'
+    |   'integer'
+    |   'intArray'
+    |   'interpolator'
+    |   'layout'
+    |   'plurals'
+    |   'stateListAnimator'
+    |   'string'
+    |   'stringArray'
+    |   'transition'
+    |   'typedArray'
+    ;
diff --git a/tools/data-binding/grammarBuilder/build.gradle b/tools/data-binding/grammarBuilder/build.gradle
new file mode 100644
index 0000000..b5f85a1
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'java'
+apply plugin: 'application'
+apply plugin: 'maven'
+
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+
+mainClassName = "org.antlr.v4.Tool"
+
+run {
+    args "BindingExpression.g4", "-visitor", "-o", "src/main/java-gen/android/databinding/parser", "-package", "android.databinding.parser"
+}
+
+sourceSets {
+    main {
+        java {
+            srcDir 'src/main/java'
+            srcDir 'src/main/java-gen'
+        }
+    }
+    test {
+        java {
+            srcDir 'src/test/java'
+        }
+    }
+}
+
+dependencies {
+    compile 'com.tunnelvisionlabs:antlr4:4.4'
+    testCompile 'junit:junit:4.11'
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'grammarBuilder'
+        }
+    }
+}
diff --git a/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3d0dee6
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..b8e77ce
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Dec 11 16:05:17 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
diff --git a/tools/data-binding/grammarBuilder/gradlew b/tools/data-binding/grammarBuilder/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/grammarBuilder/gradlew.bat b/tools/data-binding/grammarBuilder/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens
new file mode 100644
index 0000000..d379280
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens
@@ -0,0 +1,101 @@
+NullLiteral=51
+T__29=14
+T__28=15
+T__27=16
+T__26=17
+T__25=18
+T__24=19
+T__23=20
+T__22=21
+CharacterLiteral=48
+T__21=22
+T__20=23
+SingleQuoteString=49
+T__9=34
+T__8=35
+Identifier=52
+T__7=36
+T__6=37
+T__5=38
+T__4=39
+T__19=24
+T__16=27
+T__15=28
+T__18=25
+T__17=26
+T__12=31
+T__11=32
+T__14=29
+T__13=30
+T__10=33
+THIS=44
+PackageName=55
+DoubleQuoteString=50
+T__42=1
+T__40=3
+T__41=2
+ResourceType=56
+T__30=13
+T__31=12
+T__32=11
+WS=53
+T__33=10
+T__34=9
+T__35=8
+T__36=7
+T__37=6
+T__38=5
+T__39=4
+T__1=42
+T__0=43
+FloatingPointLiteral=46
+T__3=40
+T__2=41
+IntegerLiteral=45
+ResourceReference=54
+BooleanLiteral=47
+'!'=43
+'instanceof'=42
+'|'=41
+'class'=40
+'>='=39
+'~'=38
+'/'=37
+'=='=36
+'??'=35
+'null'=51
+'>'=34
+'||'=33
+'this'=44
+'&&'=32
+'='=31
+'+'=30
+'.'=29
+')'=28
+'byte'=27
+'^'=26
+'%'=25
+'>>'=23
+'char'=24
+'float'=22
+'boolean'=21
+'double'=20
+'<<'=18
+'void'=19
+'?'=17
+'<='=16
+'!='=15
+'<'=13
+'int'=14
+':'=12
+'('=11
+'-'=10
+'['=9
+'*'=8
+','=7
+'default'=6
+'&'=5
+'short'=4
+']'=3
+'>>>'=2
+'long'=1
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java
new file mode 100644
index 0000000..db87538
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java
@@ -0,0 +1,495 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ * This class provides an empty implementation of {@link BindingExpressionListener},
+ * which can be extended to create a listener which only needs to handle a subset
+ * of the available methods.
+ */
+public class BindingExpressionBaseListener implements BindingExpressionListener {
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterType(@NotNull BindingExpressionParser.TypeContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitType(@NotNull BindingExpressionParser.TypeContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterResource(@NotNull BindingExpressionParser.ResourceContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitResource(@NotNull BindingExpressionParser.ResourceContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterEveryRule(@NotNull ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitEveryRule(@NotNull ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitTerminal(@NotNull TerminalNode node) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitErrorNode(@NotNull ErrorNode node) { }
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
new file mode 100644
index 0000000..d7d426e
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
@@ -0,0 +1,295 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
+
+/**
+ * This class provides an empty implementation of {@link BindingExpressionVisitor},
+ * which can be extended to create a visitor which only needs to handle a subset
+ * of the available methods.
+ *
+ * @param <Result> The return type of the visit operation. Use {@link Void} for
+ * operations with no return type.
+ */
+public class BindingExpressionBaseVisitor<Result> extends AbstractParseTreeVisitor<Result> implements BindingExpressionVisitor<Result> {
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitType(@NotNull BindingExpressionParser.TypeContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitResource(@NotNull BindingExpressionParser.ResourceContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { return visitChildren(ctx); }
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java
new file mode 100644
index 0000000..47eb769
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java
@@ -0,0 +1,413 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.atn.*;
+import org.antlr.v4.runtime.dfa.DFA;
+import org.antlr.v4.runtime.misc.*;
+
+public class BindingExpressionLexer extends Lexer {
+	public static final int
+		T__42=1, T__41=2, T__40=3, T__39=4, T__38=5, T__37=6, T__36=7, T__35=8, 
+		T__34=9, T__33=10, T__32=11, T__31=12, T__30=13, T__29=14, T__28=15, T__27=16, 
+		T__26=17, T__25=18, T__24=19, T__23=20, T__22=21, T__21=22, T__20=23, 
+		T__19=24, T__18=25, T__17=26, T__16=27, T__15=28, T__14=29, T__13=30, 
+		T__12=31, T__11=32, T__10=33, T__9=34, T__8=35, T__7=36, T__6=37, T__5=38, 
+		T__4=39, T__3=40, T__2=41, T__1=42, T__0=43, THIS=44, IntegerLiteral=45, 
+		FloatingPointLiteral=46, BooleanLiteral=47, CharacterLiteral=48, SingleQuoteString=49, 
+		DoubleQuoteString=50, NullLiteral=51, Identifier=52, WS=53, ResourceReference=54, 
+		PackageName=55, ResourceType=56;
+	public static String[] modeNames = {
+		"DEFAULT_MODE"
+	};
+
+	public static final String[] tokenNames = {
+		"'\\u0000'", "'\\u0001'", "'\\u0002'", "'\\u0003'", "'\\u0004'", "'\\u0005'", 
+		"'\\u0006'", "'\\u0007'", "'\b'", "'\t'", "'\n'", "'\\u000B'", "'\f'", 
+		"'\r'", "'\\u000E'", "'\\u000F'", "'\\u0010'", "'\\u0011'", "'\\u0012'", 
+		"'\\u0013'", "'\\u0014'", "'\\u0015'", "'\\u0016'", "'\\u0017'", "'\\u0018'", 
+		"'\\u0019'", "'\\u001A'", "'\\u001B'", "'\\u001C'", "'\\u001D'", "'\\u001E'", 
+		"'\\u001F'", "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'''", 
+		"'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'", "'0'", "'1'", 
+		"'2'", "'3'", "'4'", "'5'", "'6'", "'7'", "'8'"
+	};
+	public static final String[] ruleNames = {
+		"T__42", "T__41", "T__40", "T__39", "T__38", "T__37", "T__36", "T__35", 
+		"T__34", "T__33", "T__32", "T__31", "T__30", "T__29", "T__28", "T__27", 
+		"T__26", "T__25", "T__24", "T__23", "T__22", "T__21", "T__20", "T__19", 
+		"T__18", "T__17", "T__16", "T__15", "T__14", "T__13", "T__12", "T__11", 
+		"T__10", "T__9", "T__8", "T__7", "T__6", "T__5", "T__4", "T__3", "T__2", 
+		"T__1", "T__0", "THIS", "IntegerLiteral", "DecimalIntegerLiteral", "HexIntegerLiteral", 
+		"OctalIntegerLiteral", "BinaryIntegerLiteral", "IntegerTypeSuffix", "DecimalNumeral", 
+		"Digits", "Digit", "NonZeroDigit", "DigitOrUnderscore", "Underscores", 
+		"HexNumeral", "HexDigits", "HexDigit", "HexDigitOrUnderscore", "OctalNumeral", 
+		"OctalDigits", "OctalDigit", "OctalDigitOrUnderscore", "BinaryNumeral", 
+		"BinaryDigits", "BinaryDigit", "BinaryDigitOrUnderscore", "FloatingPointLiteral", 
+		"DecimalFloatingPointLiteral", "ExponentPart", "ExponentIndicator", "SignedInteger", 
+		"Sign", "FloatTypeSuffix", "HexadecimalFloatingPointLiteral", "HexSignificand", 
+		"BinaryExponent", "BinaryExponentIndicator", "BooleanLiteral", "CharacterLiteral", 
+		"SingleCharacter", "SingleQuoteString", "DoubleQuoteString", "StringCharacters", 
+		"StringCharacter", "SingleQuoteStringCharacter", "EscapeSequence", "OctalEscape", 
+		"UnicodeEscape", "ZeroToThree", "NullLiteral", "Identifier", "JavaLetter", 
+		"JavaLetterOrDigit", "WS", "ResourceReference", "PackageName", "ResourceType"
+	};
+
+
+	public BindingExpressionLexer(CharStream input) {
+		super(input);
+		_interp = new LexerATNSimulator(this,_ATN);
+	}
+
+	@Override
+	public String getGrammarFileName() { return "BindingExpression.g4"; }
+
+	@Override
+	public String[] getTokenNames() { return tokenNames; }
+
+	@Override
+	public String[] getRuleNames() { return ruleNames; }
+
+	@Override
+	public String getSerializedATN() { return _serializedATN; }
+
+	@Override
+	public String[] getModeNames() { return modeNames; }
+
+	@Override
+	public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
+		switch (ruleIndex) {
+		case 93 : return JavaLetter_sempred(_localctx, predIndex);
+
+		case 94 : return JavaLetterOrDigit_sempred(_localctx, predIndex);
+		}
+		return true;
+	}
+	private boolean JavaLetterOrDigit_sempred(RuleContext _localctx, int predIndex) {
+		switch (predIndex) {
+		case 2: return Character.isJavaIdentifierPart(_input.LA(-1));
+
+		case 3: return Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)));
+		}
+		return true;
+	}
+	private boolean JavaLetter_sempred(RuleContext _localctx, int predIndex) {
+		switch (predIndex) {
+		case 0: return Character.isJavaIdentifierStart(_input.LA(-1));
+
+		case 1: return Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)));
+		}
+		return true;
+	}
+
+	public static final String _serializedATN =
+		"\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\2:\u0358\b\1\4\2\t"+
+		"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
+		"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
+		"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
+		"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+
+		"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
+		",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
+		"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
+		"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
+		"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
+		"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
+		"`\t`\4a\ta\4b\tb\4c\tc\4d\td\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\4\3"+
+		"\4\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b"+
+		"\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16\3\17\3\17\3\17"+
+		"\3\17\3\20\3\20\3\20\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\23\3\24\3\24"+
+		"\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26"+
+		"\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\31"+
+		"\3\31\3\31\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\34\3\34\3\35"+
+		"\3\35\3\36\3\36\3\37\3\37\3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3$\3$\3$\3"+
+		"%\3%\3%\3&\3&\3\'\3\'\3(\3(\3(\3)\3)\3)\3)\3)\3)\3*\3*\3+\3+\3+\3+\3+"+
+		"\3+\3+\3+\3+\3+\3+\3,\3,\3-\3-\3-\3-\3-\3.\3.\3.\3.\5.\u0168\n.\3/\3/"+
+		"\5/\u016c\n/\3\60\3\60\5\60\u0170\n\60\3\61\3\61\5\61\u0174\n\61\3\62"+
+		"\3\62\5\62\u0178\n\62\3\63\3\63\3\64\3\64\3\64\5\64\u017f\n\64\3\64\3"+
+		"\64\3\64\5\64\u0184\n\64\5\64\u0186\n\64\3\65\3\65\7\65\u018a\n\65\f\65"+
+		"\16\65\u018d\13\65\3\65\5\65\u0190\n\65\3\66\3\66\5\66\u0194\n\66\3\67"+
+		"\3\67\38\38\58\u019a\n8\39\69\u019d\n9\r9\169\u019e\3:\3:\3:\3:\3;\3;"+
+		"\7;\u01a7\n;\f;\16;\u01aa\13;\3;\5;\u01ad\n;\3<\3<\3=\3=\5=\u01b3\n=\3"+
+		">\3>\5>\u01b7\n>\3>\3>\3?\3?\7?\u01bd\n?\f?\16?\u01c0\13?\3?\5?\u01c3"+
+		"\n?\3@\3@\3A\3A\5A\u01c9\nA\3B\3B\3B\3B\3C\3C\7C\u01d1\nC\fC\16C\u01d4"+
+		"\13C\3C\5C\u01d7\nC\3D\3D\3E\3E\5E\u01dd\nE\3F\3F\5F\u01e1\nF\3G\3G\3"+
+		"G\5G\u01e6\nG\3G\5G\u01e9\nG\3G\5G\u01ec\nG\3G\3G\3G\5G\u01f1\nG\3G\5"+
+		"G\u01f4\nG\3G\3G\3G\5G\u01f9\nG\3G\3G\3G\5G\u01fe\nG\3H\3H\3H\3I\3I\3"+
+		"J\5J\u0206\nJ\3J\3J\3K\3K\3L\3L\3M\3M\3M\5M\u0211\nM\3N\3N\5N\u0215\n"+
+		"N\3N\3N\3N\5N\u021a\nN\3N\3N\5N\u021e\nN\3O\3O\3O\3P\3P\3Q\3Q\3Q\3Q\3"+
+		"Q\3Q\3Q\3Q\3Q\5Q\u022e\nQ\3R\3R\3R\3R\3R\3R\3R\3R\5R\u0238\nR\3S\3S\3"+
+		"T\3T\7T\u023e\nT\fT\16T\u0241\13T\3T\3T\3U\3U\5U\u0247\nU\3U\3U\3V\6V"+
+		"\u024c\nV\rV\16V\u024d\3W\3W\5W\u0252\nW\3X\3X\5X\u0256\nX\3Y\3Y\3Y\3"+
+		"Y\5Y\u025c\nY\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\5Z\u0269\nZ\3[\3[\3[\3"+
+		"[\3[\3[\3[\3\\\3\\\3]\3]\3]\3]\3]\3^\3^\7^\u027b\n^\f^\16^\u027e\13^\3"+
+		"_\3_\3_\3_\3_\3_\5_\u0286\n_\3`\3`\3`\3`\3`\3`\5`\u028e\n`\3a\6a\u0291"+
+		"\na\ra\16a\u0292\3a\3a\3b\3b\3b\3b\5b\u029b\nb\3b\3b\3b\3b\3c\3c\3c\3"+
+		"c\3c\3c\3c\3c\5c\u02a9\nc\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+
+		"d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\5d\u0357\n"+
+		"d\2\2\2e\3\2\3\5\2\4\7\2\5\t\2\6\13\2\7\r\2\b\17\2\t\21\2\n\23\2\13\25"+
+		"\2\f\27\2\r\31\2\16\33\2\17\35\2\20\37\2\21!\2\22#\2\23%\2\24\'\2\25)"+
+		"\2\26+\2\27-\2\30/\2\31\61\2\32\63\2\33\65\2\34\67\2\359\2\36;\2\37=\2"+
+		" ?\2!A\2\"C\2#E\2$G\2%I\2&K\2\'M\2(O\2)Q\2*S\2+U\2,W\2-Y\2.[\2/]\2\2_"+
+		"\2\2a\2\2c\2\2e\2\2g\2\2i\2\2k\2\2m\2\2o\2\2q\2\2s\2\2u\2\2w\2\2y\2\2"+
+		"{\2\2}\2\2\177\2\2\u0081\2\2\u0083\2\2\u0085\2\2\u0087\2\2\u0089\2\2\u008b"+
+		"\2\60\u008d\2\2\u008f\2\2\u0091\2\2\u0093\2\2\u0095\2\2\u0097\2\2\u0099"+
+		"\2\2\u009b\2\2\u009d\2\2\u009f\2\2\u00a1\2\61\u00a3\2\62\u00a5\2\2\u00a7"+
+		"\2\63\u00a9\2\64\u00ab\2\2\u00ad\2\2\u00af\2\2\u00b1\2\2\u00b3\2\2\u00b5"+
+		"\2\2\u00b7\2\2\u00b9\2\65\u00bb\2\66\u00bd\2\2\u00bf\2\2\u00c1\2\67\u00c3"+
+		"\28\u00c5\29\u00c7\2:\3\2\30\4\2NNnn\3\2\63;\4\2ZZzz\5\2\62;CHch\3\2\62"+
+		"9\4\2DDdd\3\2\62\63\4\2GGgg\4\2--//\6\2FFHHffhh\4\2RRrr\4\2))^^\4\2$$"+
+		"^^\4\2^^bb\13\2$$))^^bbddhhppttvv\3\2\62\65\6\2&&C\\aac|\4\2\2\u0101\ud802"+
+		"\udc01\3\2\ud802\udc01\3\2\udc02\ue001\7\2&&\62;C\\aac|\5\2\13\f\16\17"+
+		"\"\"\u037b\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2"+
+		"\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27"+
+		"\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2"+
+		"\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2"+
+		"\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2"+
+		"\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2"+
+		"\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S"+
+		"\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2\u008b\3\2\2\2\2"+
+		"\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00b9"+
+		"\3\2\2\2\2\u00bb\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2"+
+		"\2\2\u00c7\3\2\2\2\3\u00c9\3\2\2\2\5\u00ce\3\2\2\2\7\u00d2\3\2\2\2\t\u00d4"+
+		"\3\2\2\2\13\u00da\3\2\2\2\r\u00dc\3\2\2\2\17\u00e4\3\2\2\2\21\u00e6\3"+
+		"\2\2\2\23\u00e8\3\2\2\2\25\u00ea\3\2\2\2\27\u00ec\3\2\2\2\31\u00ee\3\2"+
+		"\2\2\33\u00f0\3\2\2\2\35\u00f2\3\2\2\2\37\u00f6\3\2\2\2!\u00f9\3\2\2\2"+
+		"#\u00fc\3\2\2\2%\u00fe\3\2\2\2\'\u0101\3\2\2\2)\u0106\3\2\2\2+\u010d\3"+
+		"\2\2\2-\u0115\3\2\2\2/\u011b\3\2\2\2\61\u011e\3\2\2\2\63\u0123\3\2\2\2"+
+		"\65\u0125\3\2\2\2\67\u0127\3\2\2\29\u012c\3\2\2\2;\u012e\3\2\2\2=\u0130"+
+		"\3\2\2\2?\u0132\3\2\2\2A\u0134\3\2\2\2C\u0137\3\2\2\2E\u013a\3\2\2\2G"+
+		"\u013c\3\2\2\2I\u013f\3\2\2\2K\u0142\3\2\2\2M\u0144\3\2\2\2O\u0146\3\2"+
+		"\2\2Q\u0149\3\2\2\2S\u014f\3\2\2\2U\u0151\3\2\2\2W\u015c\3\2\2\2Y\u015e"+
+		"\3\2\2\2[\u0167\3\2\2\2]\u0169\3\2\2\2_\u016d\3\2\2\2a\u0171\3\2\2\2c"+
+		"\u0175\3\2\2\2e\u0179\3\2\2\2g\u0185\3\2\2\2i\u0187\3\2\2\2k\u0193\3\2"+
+		"\2\2m\u0195\3\2\2\2o\u0199\3\2\2\2q\u019c\3\2\2\2s\u01a0\3\2\2\2u\u01a4"+
+		"\3\2\2\2w\u01ae\3\2\2\2y\u01b2\3\2\2\2{\u01b4\3\2\2\2}\u01ba\3\2\2\2\177"+
+		"\u01c4\3\2\2\2\u0081\u01c8\3\2\2\2\u0083\u01ca\3\2\2\2\u0085\u01ce\3\2"+
+		"\2\2\u0087\u01d8\3\2\2\2\u0089\u01dc\3\2\2\2\u008b\u01e0\3\2\2\2\u008d"+
+		"\u01fd\3\2\2\2\u008f\u01ff\3\2\2\2\u0091\u0202\3\2\2\2\u0093\u0205\3\2"+
+		"\2\2\u0095\u0209\3\2\2\2\u0097\u020b\3\2\2\2\u0099\u020d\3\2\2\2\u009b"+
+		"\u021d\3\2\2\2\u009d\u021f\3\2\2\2\u009f\u0222\3\2\2\2\u00a1\u022d\3\2"+
+		"\2\2\u00a3\u0237\3\2\2\2\u00a5\u0239\3\2\2\2\u00a7\u023b\3\2\2\2\u00a9"+
+		"\u0244\3\2\2\2\u00ab\u024b\3\2\2\2\u00ad\u0251\3\2\2\2\u00af\u0255\3\2"+
+		"\2\2\u00b1\u025b\3\2\2\2\u00b3\u0268\3\2\2\2\u00b5\u026a\3\2\2\2\u00b7"+
+		"\u0271\3\2\2\2\u00b9\u0273\3\2\2\2\u00bb\u0278\3\2\2\2\u00bd\u0285\3\2"+
+		"\2\2\u00bf\u028d\3\2\2\2\u00c1\u0290\3\2\2\2\u00c3\u0296\3\2\2\2\u00c5"+
+		"\u02a8\3\2\2\2\u00c7\u0356\3\2\2\2\u00c9\u00ca\7n\2\2\u00ca\u00cb\7q\2"+
+		"\2\u00cb\u00cc\7p\2\2\u00cc\u00cd\7i\2\2\u00cd\4\3\2\2\2\u00ce\u00cf\7"+
+		"@\2\2\u00cf\u00d0\7@\2\2\u00d0\u00d1\7@\2\2\u00d1\6\3\2\2\2\u00d2\u00d3"+
+		"\7_\2\2\u00d3\b\3\2\2\2\u00d4\u00d5\7u\2\2\u00d5\u00d6\7j\2\2\u00d6\u00d7"+
+		"\7q\2\2\u00d7\u00d8\7t\2\2\u00d8\u00d9\7v\2\2\u00d9\n\3\2\2\2\u00da\u00db"+
+		"\7(\2\2\u00db\f\3\2\2\2\u00dc\u00dd\7f\2\2\u00dd\u00de\7g\2\2\u00de\u00df"+
+		"\7h\2\2\u00df\u00e0\7c\2\2\u00e0\u00e1\7w\2\2\u00e1\u00e2\7n\2\2\u00e2"+
+		"\u00e3\7v\2\2\u00e3\16\3\2\2\2\u00e4\u00e5\7.\2\2\u00e5\20\3\2\2\2\u00e6"+
+		"\u00e7\7,\2\2\u00e7\22\3\2\2\2\u00e8\u00e9\7]\2\2\u00e9\24\3\2\2\2\u00ea"+
+		"\u00eb\7/\2\2\u00eb\26\3\2\2\2\u00ec\u00ed\7*\2\2\u00ed\30\3\2\2\2\u00ee"+
+		"\u00ef\7<\2\2\u00ef\32\3\2\2\2\u00f0\u00f1\7>\2\2\u00f1\34\3\2\2\2\u00f2"+
+		"\u00f3\7k\2\2\u00f3\u00f4\7p\2\2\u00f4\u00f5\7v\2\2\u00f5\36\3\2\2\2\u00f6"+
+		"\u00f7\7#\2\2\u00f7\u00f8\7?\2\2\u00f8 \3\2\2\2\u00f9\u00fa\7>\2\2\u00fa"+
+		"\u00fb\7?\2\2\u00fb\"\3\2\2\2\u00fc\u00fd\7A\2\2\u00fd$\3\2\2\2\u00fe"+
+		"\u00ff\7>\2\2\u00ff\u0100\7>\2\2\u0100&\3\2\2\2\u0101\u0102\7x\2\2\u0102"+
+		"\u0103\7q\2\2\u0103\u0104\7k\2\2\u0104\u0105\7f\2\2\u0105(\3\2\2\2\u0106"+
+		"\u0107\7f\2\2\u0107\u0108\7q\2\2\u0108\u0109\7w\2\2\u0109\u010a\7d\2\2"+
+		"\u010a\u010b\7n\2\2\u010b\u010c\7g\2\2\u010c*\3\2\2\2\u010d\u010e\7d\2"+
+		"\2\u010e\u010f\7q\2\2\u010f\u0110\7q\2\2\u0110\u0111\7n\2\2\u0111\u0112"+
+		"\7g\2\2\u0112\u0113\7c\2\2\u0113\u0114\7p\2\2\u0114,\3\2\2\2\u0115\u0116"+
+		"\7h\2\2\u0116\u0117\7n\2\2\u0117\u0118\7q\2\2\u0118\u0119\7c\2\2\u0119"+
+		"\u011a\7v\2\2\u011a.\3\2\2\2\u011b\u011c\7@\2\2\u011c\u011d\7@\2\2\u011d"+
+		"\60\3\2\2\2\u011e\u011f\7e\2\2\u011f\u0120\7j\2\2\u0120\u0121\7c\2\2\u0121"+
+		"\u0122\7t\2\2\u0122\62\3\2\2\2\u0123\u0124\7\'\2\2\u0124\64\3\2\2\2\u0125"+
+		"\u0126\7`\2\2\u0126\66\3\2\2\2\u0127\u0128\7d\2\2\u0128\u0129\7{\2\2\u0129"+
+		"\u012a\7v\2\2\u012a\u012b\7g\2\2\u012b8\3\2\2\2\u012c\u012d\7+\2\2\u012d"+
+		":\3\2\2\2\u012e\u012f\7\60\2\2\u012f<\3\2\2\2\u0130\u0131\7-\2\2\u0131"+
+		">\3\2\2\2\u0132\u0133\7?\2\2\u0133@\3\2\2\2\u0134\u0135\7(\2\2\u0135\u0136"+
+		"\7(\2\2\u0136B\3\2\2\2\u0137\u0138\7~\2\2\u0138\u0139\7~\2\2\u0139D\3"+
+		"\2\2\2\u013a\u013b\7@\2\2\u013bF\3\2\2\2\u013c\u013d\7A\2\2\u013d\u013e"+
+		"\7A\2\2\u013eH\3\2\2\2\u013f\u0140\7?\2\2\u0140\u0141\7?\2\2\u0141J\3"+
+		"\2\2\2\u0142\u0143\7\61\2\2\u0143L\3\2\2\2\u0144\u0145\7\u0080\2\2\u0145"+
+		"N\3\2\2\2\u0146\u0147\7@\2\2\u0147\u0148\7?\2\2\u0148P\3\2\2\2\u0149\u014a"+
+		"\7e\2\2\u014a\u014b\7n\2\2\u014b\u014c\7c\2\2\u014c\u014d\7u\2\2\u014d"+
+		"\u014e\7u\2\2\u014eR\3\2\2\2\u014f\u0150\7~\2\2\u0150T\3\2\2\2\u0151\u0152"+
+		"\7k\2\2\u0152\u0153\7p\2\2\u0153\u0154\7u\2\2\u0154\u0155\7v\2\2\u0155"+
+		"\u0156\7c\2\2\u0156\u0157\7p\2\2\u0157\u0158\7e\2\2\u0158\u0159\7g\2\2"+
+		"\u0159\u015a\7q\2\2\u015a\u015b\7h\2\2\u015bV\3\2\2\2\u015c\u015d\7#\2"+
+		"\2\u015dX\3\2\2\2\u015e\u015f\7v\2\2\u015f\u0160\7j\2\2\u0160\u0161\7"+
+		"k\2\2\u0161\u0162\7u\2\2\u0162Z\3\2\2\2\u0163\u0168\5]/\2\u0164\u0168"+
+		"\5_\60\2\u0165\u0168\5a\61\2\u0166\u0168\5c\62\2\u0167\u0163\3\2\2\2\u0167"+
+		"\u0164\3\2\2\2\u0167\u0165\3\2\2\2\u0167\u0166\3\2\2\2\u0168\\\3\2\2\2"+
+		"\u0169\u016b\5g\64\2\u016a\u016c\5e\63\2\u016b\u016a\3\2\2\2\u016b\u016c"+
+		"\3\2\2\2\u016c^\3\2\2\2\u016d\u016f\5s:\2\u016e\u0170\5e\63\2\u016f\u016e"+
+		"\3\2\2\2\u016f\u0170\3\2\2\2\u0170`\3\2\2\2\u0171\u0173\5{>\2\u0172\u0174"+
+		"\5e\63\2\u0173\u0172\3\2\2\2\u0173\u0174\3\2\2\2\u0174b\3\2\2\2\u0175"+
+		"\u0177\5\u0083B\2\u0176\u0178\5e\63\2\u0177\u0176\3\2\2\2\u0177\u0178"+
+		"\3\2\2\2\u0178d\3\2\2\2\u0179\u017a\t\2\2\2\u017af\3\2\2\2\u017b\u0186"+
+		"\7\62\2\2\u017c\u0183\5m\67\2\u017d\u017f\5i\65\2\u017e\u017d\3\2\2\2"+
+		"\u017e\u017f\3\2\2\2\u017f\u0184\3\2\2\2\u0180\u0181\5q9\2\u0181\u0182"+
+		"\5i\65\2\u0182\u0184\3\2\2\2\u0183\u017e\3\2\2\2\u0183\u0180\3\2\2\2\u0184"+
+		"\u0186\3\2\2\2\u0185\u017b\3\2\2\2\u0185\u017c\3\2\2\2\u0186h\3\2\2\2"+
+		"\u0187\u018f\5k\66\2\u0188\u018a\5o8\2\u0189\u0188\3\2\2\2\u018a\u018d"+
+		"\3\2\2\2\u018b\u0189\3\2\2\2\u018b\u018c\3\2\2\2\u018c\u018e\3\2\2\2\u018d"+
+		"\u018b\3\2\2\2\u018e\u0190\5k\66\2\u018f\u018b\3\2\2\2\u018f\u0190\3\2"+
+		"\2\2\u0190j\3\2\2\2\u0191\u0194\7\62\2\2\u0192\u0194\5m\67\2\u0193\u0191"+
+		"\3\2\2\2\u0193\u0192\3\2\2\2\u0194l\3\2\2\2\u0195\u0196\t\3\2\2\u0196"+
+		"n\3\2\2\2\u0197\u019a\5k\66\2\u0198\u019a\7a\2\2\u0199\u0197\3\2\2\2\u0199"+
+		"\u0198\3\2\2\2\u019ap\3\2\2\2\u019b\u019d\7a\2\2\u019c\u019b\3\2\2\2\u019d"+
+		"\u019e\3\2\2\2\u019e\u019c\3\2\2\2\u019e\u019f\3\2\2\2\u019fr\3\2\2\2"+
+		"\u01a0\u01a1\7\62\2\2\u01a1\u01a2\t\4\2\2\u01a2\u01a3\5u;\2\u01a3t\3\2"+
+		"\2\2\u01a4\u01ac\5w<\2\u01a5\u01a7\5y=\2\u01a6\u01a5\3\2\2\2\u01a7\u01aa"+
+		"\3\2\2\2\u01a8\u01a6\3\2\2\2\u01a8\u01a9\3\2\2\2\u01a9\u01ab\3\2\2\2\u01aa"+
+		"\u01a8\3\2\2\2\u01ab\u01ad\5w<\2\u01ac\u01a8\3\2\2\2\u01ac\u01ad\3\2\2"+
+		"\2\u01adv\3\2\2\2\u01ae\u01af\t\5\2\2\u01afx\3\2\2\2\u01b0\u01b3\5w<\2"+
+		"\u01b1\u01b3\7a\2\2\u01b2\u01b0\3\2\2\2\u01b2\u01b1\3\2\2\2\u01b3z\3\2"+
+		"\2\2\u01b4\u01b6\7\62\2\2\u01b5\u01b7\5q9\2\u01b6\u01b5\3\2\2\2\u01b6"+
+		"\u01b7\3\2\2\2\u01b7\u01b8\3\2\2\2\u01b8\u01b9\5}?\2\u01b9|\3\2\2\2\u01ba"+
+		"\u01c2\5\177@\2\u01bb\u01bd\5\u0081A\2\u01bc\u01bb\3\2\2\2\u01bd\u01c0"+
+		"\3\2\2\2\u01be\u01bc\3\2\2\2\u01be\u01bf\3\2\2\2\u01bf\u01c1\3\2\2\2\u01c0"+
+		"\u01be\3\2\2\2\u01c1\u01c3\5\177@\2\u01c2\u01be\3\2\2\2\u01c2\u01c3\3"+
+		"\2\2\2\u01c3~\3\2\2\2\u01c4\u01c5\t\6\2\2\u01c5\u0080\3\2\2\2\u01c6\u01c9"+
+		"\5\177@\2\u01c7\u01c9\7a\2\2\u01c8\u01c6\3\2\2\2\u01c8\u01c7\3\2\2\2\u01c9"+
+		"\u0082\3\2\2\2\u01ca\u01cb\7\62\2\2\u01cb\u01cc\t\7\2\2\u01cc\u01cd\5"+
+		"\u0085C\2\u01cd\u0084\3\2\2\2\u01ce\u01d6\5\u0087D\2\u01cf\u01d1\5\u0089"+
+		"E\2\u01d0\u01cf\3\2\2\2\u01d1\u01d4\3\2\2\2\u01d2\u01d0\3\2\2\2\u01d2"+
+		"\u01d3\3\2\2\2\u01d3\u01d5\3\2\2\2\u01d4\u01d2\3\2\2\2\u01d5\u01d7\5\u0087"+
+		"D\2\u01d6\u01d2\3\2\2\2\u01d6\u01d7\3\2\2\2\u01d7\u0086\3\2\2\2\u01d8"+
+		"\u01d9\t\b\2\2\u01d9\u0088\3\2\2\2\u01da\u01dd\5\u0087D\2\u01db\u01dd"+
+		"\7a\2\2\u01dc\u01da\3\2\2\2\u01dc\u01db\3\2\2\2\u01dd\u008a\3\2\2\2\u01de"+
+		"\u01e1\5\u008dG\2\u01df\u01e1\5\u0099M\2\u01e0\u01de\3\2\2\2\u01e0\u01df"+
+		"\3\2\2\2\u01e1\u008c\3\2\2\2\u01e2\u01e3\5i\65\2\u01e3\u01e5\7\60\2\2"+
+		"\u01e4\u01e6\5i\65\2\u01e5\u01e4\3\2\2\2\u01e5\u01e6\3\2\2\2\u01e6\u01e8"+
+		"\3\2\2\2\u01e7\u01e9\5\u008fH\2\u01e8\u01e7\3\2\2\2\u01e8\u01e9\3\2\2"+
+		"\2\u01e9\u01eb\3\2\2\2\u01ea\u01ec\5\u0097L\2\u01eb\u01ea\3\2\2\2\u01eb"+
+		"\u01ec\3\2\2\2\u01ec\u01fe\3\2\2\2\u01ed\u01ee\7\60\2\2\u01ee\u01f0\5"+
+		"i\65\2\u01ef\u01f1\5\u008fH\2\u01f0\u01ef\3\2\2\2\u01f0\u01f1\3\2\2\2"+
+		"\u01f1\u01f3\3\2\2\2\u01f2\u01f4\5\u0097L\2\u01f3\u01f2\3\2\2\2\u01f3"+
+		"\u01f4\3\2\2\2\u01f4\u01fe\3\2\2\2\u01f5\u01f6\5i\65\2\u01f6\u01f8\5\u008f"+
+		"H\2\u01f7\u01f9\5\u0097L\2\u01f8\u01f7\3\2\2\2\u01f8\u01f9\3\2\2\2\u01f9"+
+		"\u01fe\3\2\2\2\u01fa\u01fb\5i\65\2\u01fb\u01fc\5\u0097L\2\u01fc\u01fe"+
+		"\3\2\2\2\u01fd\u01e2\3\2\2\2\u01fd\u01ed\3\2\2\2\u01fd\u01f5\3\2\2\2\u01fd"+
+		"\u01fa\3\2\2\2\u01fe\u008e\3\2\2\2\u01ff\u0200\5\u0091I\2\u0200\u0201"+
+		"\5\u0093J\2\u0201\u0090\3\2\2\2\u0202\u0203\t\t\2\2\u0203\u0092\3\2\2"+
+		"\2\u0204\u0206\5\u0095K\2\u0205\u0204\3\2\2\2\u0205\u0206\3\2\2\2\u0206"+
+		"\u0207\3\2\2\2\u0207\u0208\5i\65\2\u0208\u0094\3\2\2\2\u0209\u020a\t\n"+
+		"\2\2\u020a\u0096\3\2\2\2\u020b\u020c\t\13\2\2\u020c\u0098\3\2\2\2\u020d"+
+		"\u020e\5\u009bN\2\u020e\u0210\5\u009dO\2\u020f\u0211\5\u0097L\2\u0210"+
+		"\u020f\3\2\2\2\u0210\u0211\3\2\2\2\u0211\u009a\3\2\2\2\u0212\u0214\5s"+
+		":\2\u0213\u0215\7\60\2\2\u0214\u0213\3\2\2\2\u0214\u0215\3\2\2\2\u0215"+
+		"\u021e\3\2\2\2\u0216\u0217\7\62\2\2\u0217\u0219\t\4\2\2\u0218\u021a\5"+
+		"u;\2\u0219\u0218\3\2\2\2\u0219\u021a\3\2\2\2\u021a\u021b\3\2\2\2\u021b"+
+		"\u021c\7\60\2\2\u021c\u021e\5u;\2\u021d\u0212\3\2\2\2\u021d\u0216\3\2"+
+		"\2\2\u021e\u009c\3\2\2\2\u021f\u0220\5\u009fP\2\u0220\u0221\5\u0093J\2"+
+		"\u0221\u009e\3\2\2\2\u0222\u0223\t\f\2\2\u0223\u00a0\3\2\2\2\u0224\u0225"+
+		"\7v\2\2\u0225\u0226\7t\2\2\u0226\u0227\7w\2\2\u0227\u022e\7g\2\2\u0228"+
+		"\u0229\7h\2\2\u0229\u022a\7c\2\2\u022a\u022b\7n\2\2\u022b\u022c\7u\2\2"+
+		"\u022c\u022e\7g\2\2\u022d\u0224\3\2\2\2\u022d\u0228\3\2\2\2\u022e\u00a2"+
+		"\3\2\2\2\u022f\u0230\7)\2\2\u0230\u0231\5\u00a5S\2\u0231\u0232\7)\2\2"+
+		"\u0232\u0238\3\2\2\2\u0233\u0234\7)\2\2\u0234\u0235\5\u00b1Y\2\u0235\u0236"+
+		"\7)\2\2\u0236\u0238\3\2\2\2\u0237\u022f\3\2\2\2\u0237\u0233\3\2\2\2\u0238"+
+		"\u00a4\3\2\2\2\u0239\u023a\n\r\2\2\u023a\u00a6\3\2\2\2\u023b\u023f\7b"+
+		"\2\2\u023c\u023e\5\u00afX\2\u023d\u023c\3\2\2\2\u023e\u0241\3\2\2\2\u023f"+
+		"\u023d\3\2\2\2\u023f\u0240\3\2\2\2\u0240\u0242\3\2\2\2\u0241\u023f\3\2"+
+		"\2\2\u0242\u0243\7b\2\2\u0243\u00a8\3\2\2\2\u0244\u0246\7$\2\2\u0245\u0247"+
+		"\5\u00abV\2\u0246\u0245\3\2\2\2\u0246\u0247\3\2\2\2\u0247\u0248\3\2\2"+
+		"\2\u0248\u0249\7$\2\2\u0249\u00aa\3\2\2\2\u024a\u024c\5\u00adW\2\u024b"+
+		"\u024a\3\2\2\2\u024c\u024d\3\2\2\2\u024d\u024b\3\2\2\2\u024d\u024e\3\2"+
+		"\2\2\u024e\u00ac\3\2\2\2\u024f\u0252\n\16\2\2\u0250\u0252\5\u00b1Y\2\u0251"+
+		"\u024f\3\2\2\2\u0251\u0250\3\2\2\2\u0252\u00ae\3\2\2\2\u0253\u0256\n\17"+
+		"\2\2\u0254\u0256\5\u00b1Y\2\u0255\u0253\3\2\2\2\u0255\u0254\3\2\2\2\u0256"+
+		"\u00b0\3\2\2\2\u0257\u0258\7^\2\2\u0258\u025c\t\20\2\2\u0259\u025c\5\u00b3"+
+		"Z\2\u025a\u025c\5\u00b5[\2\u025b\u0257\3\2\2\2\u025b\u0259\3\2\2\2\u025b"+
+		"\u025a\3\2\2\2\u025c\u00b2\3\2\2\2\u025d\u025e\7^\2\2\u025e\u0269\5\177"+
+		"@\2\u025f\u0260\7^\2\2\u0260\u0261\5\177@\2\u0261\u0262\5\177@\2\u0262"+
+		"\u0269\3\2\2\2\u0263\u0264\7^\2\2\u0264\u0265\5\u00b7\\\2\u0265\u0266"+
+		"\5\177@\2\u0266\u0267\5\177@\2\u0267\u0269\3\2\2\2\u0268\u025d\3\2\2\2"+
+		"\u0268\u025f\3\2\2\2\u0268\u0263\3\2\2\2\u0269\u00b4\3\2\2\2\u026a\u026b"+
+		"\7^\2\2\u026b\u026c\7w\2\2\u026c\u026d\5w<\2\u026d\u026e\5w<\2\u026e\u026f"+
+		"\5w<\2\u026f\u0270\5w<\2\u0270\u00b6\3\2\2\2\u0271\u0272\t\21\2\2\u0272"+
+		"\u00b8\3\2\2\2\u0273\u0274\7p\2\2\u0274\u0275\7w\2\2\u0275\u0276\7n\2"+
+		"\2\u0276\u0277\7n\2\2\u0277\u00ba\3\2\2\2\u0278\u027c\5\u00bd_\2\u0279"+
+		"\u027b\5\u00bf`\2\u027a\u0279\3\2\2\2\u027b\u027e\3\2\2\2\u027c\u027a"+
+		"\3\2\2\2\u027c\u027d\3\2\2\2\u027d\u00bc\3\2\2\2\u027e\u027c\3\2\2\2\u027f"+
+		"\u0286\t\22\2\2\u0280\u0281\n\23\2\2\u0281\u0286\6_\2\2\u0282\u0283\t"+
+		"\24\2\2\u0283\u0284\t\25\2\2\u0284\u0286\6_\3\2\u0285\u027f\3\2\2\2\u0285"+
+		"\u0280\3\2\2\2\u0285\u0282\3\2\2\2\u0286\u00be\3\2\2\2\u0287\u028e\t\26"+
+		"\2\2\u0288\u0289\n\23\2\2\u0289\u028e\6`\4\2\u028a\u028b\t\24\2\2\u028b"+
+		"\u028c\t\25\2\2\u028c\u028e\6`\5\2\u028d\u0287\3\2\2\2\u028d\u0288\3\2"+
+		"\2\2\u028d\u028a\3\2\2\2\u028e\u00c0\3\2\2\2\u028f\u0291\t\27\2\2\u0290"+
+		"\u028f\3\2\2\2\u0291\u0292\3\2\2\2\u0292\u0290\3\2\2\2\u0292\u0293\3\2"+
+		"\2\2\u0293\u0294\3\2\2\2\u0294\u0295\ba\2\2\u0295\u00c2\3\2\2\2\u0296"+
+		"\u029a\7B\2\2\u0297\u0298\5\u00c5c\2\u0298\u0299\7<\2\2\u0299\u029b\3"+
+		"\2\2\2\u029a\u0297\3\2\2\2\u029a\u029b\3\2\2\2\u029b\u029c\3\2\2\2\u029c"+
+		"\u029d\5\u00c7d\2\u029d\u029e\7\61\2\2\u029e\u029f\5\u00bb^\2\u029f\u00c4"+
+		"\3\2\2\2\u02a0\u02a1\7c\2\2\u02a1\u02a2\7p\2\2\u02a2\u02a3\7f\2\2\u02a3"+
+		"\u02a4\7t\2\2\u02a4\u02a5\7q\2\2\u02a5\u02a6\7k\2\2\u02a6\u02a9\7f\2\2"+
+		"\u02a7\u02a9\5\u00bb^\2\u02a8\u02a0\3\2\2\2\u02a8\u02a7\3\2\2\2\u02a9"+
+		"\u00c6\3\2\2\2\u02aa\u02ab\7c\2\2\u02ab\u02ac\7p\2\2\u02ac\u02ad\7k\2"+
+		"\2\u02ad\u0357\7o\2\2\u02ae\u02af\7c\2\2\u02af\u02b0\7p\2\2\u02b0\u02b1"+
+		"\7k\2\2\u02b1\u02b2\7o\2\2\u02b2\u02b3\7c\2\2\u02b3\u02b4\7v\2\2\u02b4"+
+		"\u02b5\7q\2\2\u02b5\u0357\7t\2\2\u02b6\u02b7\7d\2\2\u02b7\u02b8\7q\2\2"+
+		"\u02b8\u02b9\7q\2\2\u02b9\u0357\7n\2\2\u02ba\u02bb\7e\2\2\u02bb\u02bc"+
+		"\7q\2\2\u02bc\u02bd\7n\2\2\u02bd\u02be\7q\2\2\u02be\u0357\7t\2\2\u02bf"+
+		"\u02c0\7e\2\2\u02c0\u02c1\7q\2\2\u02c1\u02c2\7n\2\2\u02c2\u02c3\7q\2\2"+
+		"\u02c3\u02c4\7t\2\2\u02c4\u02c5\7U\2\2\u02c5\u02c6\7v\2\2\u02c6\u02c7"+
+		"\7c\2\2\u02c7\u02c8\7v\2\2\u02c8\u02c9\7g\2\2\u02c9\u02ca\7N\2\2\u02ca"+
+		"\u02cb\7k\2\2\u02cb\u02cc\7u\2\2\u02cc\u0357\7v\2\2\u02cd\u02ce\7f\2\2"+
+		"\u02ce\u02cf\7k\2\2\u02cf\u02d0\7o\2\2\u02d0\u02d1\7g\2\2\u02d1\u0357"+
+		"\7p\2\2\u02d2\u02d3\7f\2\2\u02d3\u02d4\7k\2\2\u02d4\u02d5\7o\2\2\u02d5"+
+		"\u02d6\7g\2\2\u02d6\u02d7\7p\2\2\u02d7\u02d8\7Q\2\2\u02d8\u02d9\7h\2\2"+
+		"\u02d9\u02da\7h\2\2\u02da\u02db\7u\2\2\u02db\u02dc\7g\2\2\u02dc\u0357"+
+		"\7v\2\2\u02dd\u02de\7f\2\2\u02de\u02df\7k\2\2\u02df\u02e0\7o\2\2\u02e0"+
+		"\u02e1\7g\2\2\u02e1\u02e2\7p\2\2\u02e2\u02e3\7U\2\2\u02e3\u02e4\7k\2\2"+
+		"\u02e4\u02e5\7|\2\2\u02e5\u0357\7g\2\2\u02e6\u02e7\7f\2\2\u02e7\u02e8"+
+		"\7t\2\2\u02e8\u02e9\7c\2\2\u02e9\u02ea\7y\2\2\u02ea\u02eb\7c\2\2\u02eb"+
+		"\u02ec\7d\2\2\u02ec\u02ed\7n\2\2\u02ed\u0357\7g\2\2\u02ee\u02ef\7h\2\2"+
+		"\u02ef\u02f0\7t\2\2\u02f0\u02f1\7c\2\2\u02f1\u02f2\7e\2\2\u02f2\u02f3"+
+		"\7v\2\2\u02f3\u02f4\7k\2\2\u02f4\u02f5\7q\2\2\u02f5\u0357\7p\2\2\u02f6"+
+		"\u02f7\7k\2\2\u02f7\u0357\7f\2\2\u02f8\u02f9\7k\2\2\u02f9\u02fa\7p\2\2"+
+		"\u02fa\u02fb\7v\2\2\u02fb\u02fc\7g\2\2\u02fc\u02fd\7i\2\2\u02fd\u02fe"+
+		"\7g\2\2\u02fe\u0357\7t\2\2\u02ff\u0300\7k\2\2\u0300\u0301\7p\2\2\u0301"+
+		"\u0302\7v\2\2\u0302\u0303\7C\2\2\u0303\u0304\7t\2\2\u0304\u0305\7t\2\2"+
+		"\u0305\u0306\7c\2\2\u0306\u0357\7{\2\2\u0307\u0308\7k\2\2\u0308\u0309"+
+		"\7p\2\2\u0309\u030a\7v\2\2\u030a\u030b\7g\2\2\u030b\u030c\7t\2\2\u030c"+
+		"\u030d\7r\2\2\u030d\u030e\7q\2\2\u030e\u030f\7n\2\2\u030f\u0310\7c\2\2"+
+		"\u0310\u0311\7v\2\2\u0311\u0312\7q\2\2\u0312\u0357\7t\2\2\u0313\u0314"+
+		"\7n\2\2\u0314\u0315\7c\2\2\u0315\u0316\7{\2\2\u0316\u0317\7q\2\2\u0317"+
+		"\u0318\7w\2\2\u0318\u0357\7v\2\2\u0319\u031a\7r\2\2\u031a\u031b\7n\2\2"+
+		"\u031b\u031c\7w\2\2\u031c\u031d\7t\2\2\u031d\u031e\7c\2\2\u031e\u031f"+
+		"\7n\2\2\u031f\u0357\7u\2\2\u0320\u0321\7u\2\2\u0321\u0322\7v\2\2\u0322"+
+		"\u0323\7c\2\2\u0323\u0324\7v\2\2\u0324\u0325\7g\2\2\u0325\u0326\7N\2\2"+
+		"\u0326\u0327\7k\2\2\u0327\u0328\7u\2\2\u0328\u0329\7v\2\2\u0329\u032a"+
+		"\7C\2\2\u032a\u032b\7p\2\2\u032b\u032c\7k\2\2\u032c\u032d\7o\2\2\u032d"+
+		"\u032e\7c\2\2\u032e\u032f\7v\2\2\u032f\u0330\7q\2\2\u0330\u0357\7t\2\2"+
+		"\u0331\u0332\7u\2\2\u0332\u0333\7v\2\2\u0333\u0334\7t\2\2\u0334\u0335"+
+		"\7k\2\2\u0335\u0336\7p\2\2\u0336\u0357\7i\2\2\u0337\u0338\7u\2\2\u0338"+
+		"\u0339\7v\2\2\u0339\u033a\7t\2\2\u033a\u033b\7k\2\2\u033b\u033c\7p\2\2"+
+		"\u033c\u033d\7i\2\2\u033d\u033e\7C\2\2\u033e\u033f\7t\2\2\u033f\u0340"+
+		"\7t\2\2\u0340\u0341\7c\2\2\u0341\u0357\7{\2\2\u0342\u0343\7v\2\2\u0343"+
+		"\u0344\7t\2\2\u0344\u0345\7c\2\2\u0345\u0346\7p\2\2\u0346\u0347\7u\2\2"+
+		"\u0347\u0348\7k\2\2\u0348\u0349\7v\2\2\u0349\u034a\7k\2\2\u034a\u034b"+
+		"\7q\2\2\u034b\u0357\7p\2\2\u034c\u034d\7v\2\2\u034d\u034e\7{\2\2\u034e"+
+		"\u034f\7r\2\2\u034f\u0350\7g\2\2\u0350\u0351\7f\2\2\u0351\u0352\7C\2\2"+
+		"\u0352\u0353\7t\2\2\u0353\u0354\7t\2\2\u0354\u0355\7c\2\2\u0355\u0357"+
+		"\7{\2\2\u0356\u02aa\3\2\2\2\u0356\u02ae\3\2\2\2\u0356\u02b6\3\2\2\2\u0356"+
+		"\u02ba\3\2\2\2\u0356\u02bf\3\2\2\2\u0356\u02cd\3\2\2\2\u0356\u02d2\3\2"+
+		"\2\2\u0356\u02dd\3\2\2\2\u0356\u02e6\3\2\2\2\u0356\u02ee\3\2\2\2\u0356"+
+		"\u02f6\3\2\2\2\u0356\u02f8\3\2\2\2\u0356\u02ff\3\2\2\2\u0356\u0307\3\2"+
+		"\2\2\u0356\u0313\3\2\2\2\u0356\u0319\3\2\2\2\u0356\u0320\3\2\2\2\u0356"+
+		"\u0331\3\2\2\2\u0356\u0337\3\2\2\2\u0356\u0342\3\2\2\2\u0356\u034c\3\2"+
+		"\2\2\u0357\u00c8\3\2\2\2\67\2\u0167\u016b\u016f\u0173\u0177\u017e\u0183"+
+		"\u0185\u018b\u018f\u0193\u0199\u019e\u01a8\u01ac\u01b2\u01b6\u01be\u01c2"+
+		"\u01c8\u01d2\u01d6\u01dc\u01e0\u01e5\u01e8\u01eb\u01f0\u01f3\u01f8\u01fd"+
+		"\u0205\u0210\u0214\u0219\u021d\u022d\u0237\u023f\u0246\u024d\u0251\u0255"+
+		"\u025b\u0268\u027c\u0285\u028d\u0292\u029a\u02a8\u0356\3\b\2\2";
+	public static final ATN _ATN =
+		new ATNDeserializer().deserialize(_serializedATN.toCharArray());
+	static {
+	}
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens
new file mode 100644
index 0000000..d379280
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens
@@ -0,0 +1,101 @@
+NullLiteral=51
+T__29=14
+T__28=15
+T__27=16
+T__26=17
+T__25=18
+T__24=19
+T__23=20
+T__22=21
+CharacterLiteral=48
+T__21=22
+T__20=23
+SingleQuoteString=49
+T__9=34
+T__8=35
+Identifier=52
+T__7=36
+T__6=37
+T__5=38
+T__4=39
+T__19=24
+T__16=27
+T__15=28
+T__18=25
+T__17=26
+T__12=31
+T__11=32
+T__14=29
+T__13=30
+T__10=33
+THIS=44
+PackageName=55
+DoubleQuoteString=50
+T__42=1
+T__40=3
+T__41=2
+ResourceType=56
+T__30=13
+T__31=12
+T__32=11
+WS=53
+T__33=10
+T__34=9
+T__35=8
+T__36=7
+T__37=6
+T__38=5
+T__39=4
+T__1=42
+T__0=43
+FloatingPointLiteral=46
+T__3=40
+T__2=41
+IntegerLiteral=45
+ResourceReference=54
+BooleanLiteral=47
+'!'=43
+'instanceof'=42
+'|'=41
+'class'=40
+'>='=39
+'~'=38
+'/'=37
+'=='=36
+'??'=35
+'null'=51
+'>'=34
+'||'=33
+'this'=44
+'&&'=32
+'='=31
+'+'=30
+'.'=29
+')'=28
+'byte'=27
+'^'=26
+'%'=25
+'>>'=23
+'char'=24
+'float'=22
+'boolean'=21
+'double'=20
+'<<'=18
+'void'=19
+'?'=17
+'<='=16
+'!='=15
+'<'=13
+'int'=14
+':'=12
+'('=11
+'-'=10
+'['=9
+'*'=8
+','=7
+'default'=6
+'&'=5
+'short'=4
+']'=3
+'>>>'=2
+'long'=1
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java
new file mode 100644
index 0000000..020be83
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java
@@ -0,0 +1,428 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTreeListener;
+
+/**
+ * This interface defines a complete listener for a parse tree produced by
+ * {@link BindingExpressionParser}.
+ */
+public interface BindingExpressionListener extends ParseTreeListener {
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterExpression(@NotNull BindingExpressionParser.ExpressionContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#resources}.
+	 * @param ctx the parse tree
+	 */
+	void enterResources(@NotNull BindingExpressionParser.ResourcesContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#resources}.
+	 * @param ctx the parse tree
+	 */
+	void exitResources(@NotNull BindingExpressionParser.ResourcesContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code BracketOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code BracketOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code UnaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code UnaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code CastOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterCastOp(@NotNull BindingExpressionParser.CastOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code CastOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#resourceParameters}.
+	 * @param ctx the parse tree
+	 */
+	void enterResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#resourceParameters}.
+	 * @param ctx the parse tree
+	 */
+	void exitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code AndOrOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code AndOrOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code MethodInvocation}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code MethodInvocation}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#expressionList}.
+	 * @param ctx the parse tree
+	 */
+	void enterExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#expressionList}.
+	 * @param ctx the parse tree
+	 */
+	void exitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}.
+	 * @param ctx the parse tree
+	 */
+	void enterClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}.
+	 * @param ctx the parse tree
+	 */
+	void exitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#stringLiteral}.
+	 * @param ctx the parse tree
+	 */
+	void enterStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#stringLiteral}.
+	 * @param ctx the parse tree
+	 */
+	void exitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code Primary}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code Primary}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#type}.
+	 * @param ctx the parse tree
+	 */
+	void enterType(@NotNull BindingExpressionParser.TypeContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#type}.
+	 * @param ctx the parse tree
+	 */
+	void exitType(@NotNull BindingExpressionParser.TypeContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#bindingSyntax}.
+	 * @param ctx the parse tree
+	 */
+	void enterBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#bindingSyntax}.
+	 * @param ctx the parse tree
+	 */
+	void exitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code ComparisonOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code ComparisonOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code TernaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code TernaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#constantValue}.
+	 * @param ctx the parse tree
+	 */
+	void enterConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#constantValue}.
+	 * @param ctx the parse tree
+	 */
+	void exitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code DotOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterDotOp(@NotNull BindingExpressionParser.DotOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code DotOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#defaults}.
+	 * @param ctx the parse tree
+	 */
+	void enterDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#defaults}.
+	 * @param ctx the parse tree
+	 */
+	void exitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code BitShiftOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code BitShiftOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code InstanceOfOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code InstanceOfOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code BinaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code BinaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}.
+	 * @param ctx the parse tree
+	 */
+	void enterExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}.
+	 * @param ctx the parse tree
+	 */
+	void exitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code Resource}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterResource(@NotNull BindingExpressionParser.ResourceContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code Resource}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitResource(@NotNull BindingExpressionParser.ResourceContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#typeArguments}.
+	 * @param ctx the parse tree
+	 */
+	void enterTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#typeArguments}.
+	 * @param ctx the parse tree
+	 */
+	void exitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code Grouping}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterGrouping(@NotNull BindingExpressionParser.GroupingContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code Grouping}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code MathOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterMathOp(@NotNull BindingExpressionParser.MathOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code MathOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#classExtraction}.
+	 * @param ctx the parse tree
+	 */
+	void enterClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#classExtraction}.
+	 * @param ctx the parse tree
+	 */
+	void exitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#arguments}.
+	 * @param ctx the parse tree
+	 */
+	void enterArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#arguments}.
+	 * @param ctx the parse tree
+	 */
+	void exitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#primitiveType}.
+	 * @param ctx the parse tree
+	 */
+	void enterPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#primitiveType}.
+	 * @param ctx the parse tree
+	 */
+	void exitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx);
+
+	/**
+	 * Enter a parse tree produced by the {@code QuestionQuestionOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void enterQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx);
+	/**
+	 * Exit a parse tree produced by the {@code QuestionQuestionOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 */
+	void exitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#javaLiteral}.
+	 * @param ctx the parse tree
+	 */
+	void enterJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#javaLiteral}.
+	 * @param ctx the parse tree
+	 */
+	void exitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}.
+	 * @param ctx the parse tree
+	 */
+	void enterExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}.
+	 * @param ctx the parse tree
+	 */
+	void exitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#identifier}.
+	 * @param ctx the parse tree
+	 */
+	void enterIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#identifier}.
+	 * @param ctx the parse tree
+	 */
+	void exitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link BindingExpressionParser#literal}.
+	 * @param ctx the parse tree
+	 */
+	void enterLiteral(@NotNull BindingExpressionParser.LiteralContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link BindingExpressionParser#literal}.
+	 * @param ctx the parse tree
+	 */
+	void exitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx);
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java
new file mode 100644
index 0000000..0d41591
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java
@@ -0,0 +1,2005 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.atn.*;
+import org.antlr.v4.runtime.dfa.DFA;
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.misc.*;
+import org.antlr.v4.runtime.tree.*;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+public class BindingExpressionParser extends Parser {
+	public static final int
+		T__42=1, T__41=2, T__40=3, T__39=4, T__38=5, T__37=6, T__36=7, T__35=8, 
+		T__34=9, T__33=10, T__32=11, T__31=12, T__30=13, T__29=14, T__28=15, T__27=16, 
+		T__26=17, T__25=18, T__24=19, T__23=20, T__22=21, T__21=22, T__20=23, 
+		T__19=24, T__18=25, T__17=26, T__16=27, T__15=28, T__14=29, T__13=30, 
+		T__12=31, T__11=32, T__10=33, T__9=34, T__8=35, T__7=36, T__6=37, T__5=38, 
+		T__4=39, T__3=40, T__2=41, T__1=42, T__0=43, THIS=44, IntegerLiteral=45, 
+		FloatingPointLiteral=46, BooleanLiteral=47, CharacterLiteral=48, SingleQuoteString=49, 
+		DoubleQuoteString=50, NullLiteral=51, Identifier=52, WS=53, ResourceReference=54, 
+		PackageName=55, ResourceType=56;
+	public static final String[] tokenNames = {
+		"<INVALID>", "'long'", "'>>>'", "']'", "'short'", "'&'", "'default'", 
+		"','", "'*'", "'['", "'-'", "'('", "':'", "'<'", "'int'", "'!='", "'<='", 
+		"'?'", "'<<'", "'void'", "'double'", "'boolean'", "'float'", "'>>'", "'char'", 
+		"'%'", "'^'", "'byte'", "')'", "'.'", "'+'", "'='", "'&&'", "'||'", "'>'", 
+		"'??'", "'=='", "'/'", "'~'", "'>='", "'class'", "'|'", "'instanceof'", 
+		"'!'", "'this'", "IntegerLiteral", "FloatingPointLiteral", "BooleanLiteral", 
+		"CharacterLiteral", "SingleQuoteString", "DoubleQuoteString", "'null'", 
+		"Identifier", "WS", "ResourceReference", "PackageName", "ResourceType"
+	};
+	public static final int
+		RULE_bindingSyntax = 0, RULE_defaults = 1, RULE_constantValue = 2, RULE_expression = 3, 
+		RULE_classExtraction = 4, RULE_expressionList = 5, RULE_literal = 6, RULE_identifier = 7, 
+		RULE_javaLiteral = 8, RULE_stringLiteral = 9, RULE_explicitGenericInvocation = 10, 
+		RULE_typeArguments = 11, RULE_type = 12, RULE_explicitGenericInvocationSuffix = 13, 
+		RULE_arguments = 14, RULE_classOrInterfaceType = 15, RULE_primitiveType = 16, 
+		RULE_resources = 17, RULE_resourceParameters = 18;
+	public static final String[] ruleNames = {
+		"bindingSyntax", "defaults", "constantValue", "expression", "classExtraction", 
+		"expressionList", "literal", "identifier", "javaLiteral", "stringLiteral", 
+		"explicitGenericInvocation", "typeArguments", "type", "explicitGenericInvocationSuffix", 
+		"arguments", "classOrInterfaceType", "primitiveType", "resources", "resourceParameters"
+	};
+
+	@Override
+	public String getGrammarFileName() { return "BindingExpression.g4"; }
+
+	@Override
+	public String[] getTokenNames() { return tokenNames; }
+
+	@Override
+	public String[] getRuleNames() { return ruleNames; }
+
+	@Override
+	public String getSerializedATN() { return _serializedATN; }
+
+	public BindingExpressionParser(TokenStream input) {
+		super(input);
+		_interp = new ParserATNSimulator(this,_ATN);
+	}
+	public static class BindingSyntaxContext extends ParserRuleContext {
+		public DefaultsContext defaults() {
+			return getRuleContext(DefaultsContext.class,0);
+		}
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public BindingSyntaxContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_bindingSyntax; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBindingSyntax(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBindingSyntax(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBindingSyntax(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final BindingSyntaxContext bindingSyntax() throws RecognitionException {
+		BindingSyntaxContext _localctx = new BindingSyntaxContext(_ctx, getState());
+		enterRule(_localctx, 0, RULE_bindingSyntax);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(38); expression(0);
+			setState(40);
+			_la = _input.LA(1);
+			if (_la==T__36) {
+				{
+				setState(39); defaults();
+				}
+			}
+
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class DefaultsContext extends ParserRuleContext {
+		public ConstantValueContext constantValue() {
+			return getRuleContext(ConstantValueContext.class,0);
+		}
+		public DefaultsContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_defaults; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterDefaults(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitDefaults(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitDefaults(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final DefaultsContext defaults() throws RecognitionException {
+		DefaultsContext _localctx = new DefaultsContext(_ctx, getState());
+		enterRule(_localctx, 2, RULE_defaults);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(42); match(T__36);
+			setState(43); match(T__37);
+			setState(44); match(T__12);
+			setState(45); constantValue();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ConstantValueContext extends ParserRuleContext {
+		public LiteralContext literal() {
+			return getRuleContext(LiteralContext.class,0);
+		}
+		public IdentifierContext identifier() {
+			return getRuleContext(IdentifierContext.class,0);
+		}
+		public TerminalNode ResourceReference() { return getToken(BindingExpressionParser.ResourceReference, 0); }
+		public ConstantValueContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_constantValue; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterConstantValue(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitConstantValue(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitConstantValue(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ConstantValueContext constantValue() throws RecognitionException {
+		ConstantValueContext _localctx = new ConstantValueContext(_ctx, getState());
+		enterRule(_localctx, 4, RULE_constantValue);
+		try {
+			setState(50);
+			switch (_input.LA(1)) {
+			case IntegerLiteral:
+			case FloatingPointLiteral:
+			case BooleanLiteral:
+			case CharacterLiteral:
+			case SingleQuoteString:
+			case DoubleQuoteString:
+			case NullLiteral:
+				enterOuterAlt(_localctx, 1);
+				{
+				setState(47); literal();
+				}
+				break;
+			case ResourceReference:
+				enterOuterAlt(_localctx, 2);
+				{
+				setState(48); match(ResourceReference);
+				}
+				break;
+			case Identifier:
+				enterOuterAlt(_localctx, 3);
+				{
+				setState(49); identifier();
+				}
+				break;
+			default:
+				throw new NoViableAltException(this);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ExpressionContext extends ParserRuleContext {
+		public ExpressionContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_expression; }
+	 
+		public ExpressionContext() { }
+		public void copyFrom(ExpressionContext ctx) {
+			super.copyFrom(ctx);
+		}
+	}
+	public static class BracketOpContext extends ExpressionContext {
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public BracketOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBracketOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBracketOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBracketOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class ResourceContext extends ExpressionContext {
+		public ResourcesContext resources() {
+			return getRuleContext(ResourcesContext.class,0);
+		}
+		public ResourceContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResource(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResource(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResource(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class CastOpContext extends ExpressionContext {
+		public TypeContext type() {
+			return getRuleContext(TypeContext.class,0);
+		}
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public CastOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterCastOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitCastOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitCastOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class UnaryOpContext extends ExpressionContext {
+		public Token op;
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public UnaryOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterUnaryOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitUnaryOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitUnaryOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class AndOrOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public AndOrOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterAndOrOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitAndOrOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitAndOrOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class MethodInvocationContext extends ExpressionContext {
+		public ExpressionContext target;
+		public Token methodName;
+		public ExpressionListContext args;
+		public ExpressionListContext expressionList() {
+			return getRuleContext(ExpressionListContext.class,0);
+		}
+		public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); }
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public MethodInvocationContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterMethodInvocation(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitMethodInvocation(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitMethodInvocation(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class PrimaryContext extends ExpressionContext {
+		public ClassExtractionContext classExtraction() {
+			return getRuleContext(ClassExtractionContext.class,0);
+		}
+		public LiteralContext literal() {
+			return getRuleContext(LiteralContext.class,0);
+		}
+		public IdentifierContext identifier() {
+			return getRuleContext(IdentifierContext.class,0);
+		}
+		public PrimaryContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterPrimary(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitPrimary(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitPrimary(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class GroupingContext extends ExpressionContext {
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public GroupingContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterGrouping(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitGrouping(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitGrouping(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class TernaryOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext iftrue;
+		public ExpressionContext iffalse;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public TernaryOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterTernaryOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitTernaryOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitTernaryOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class ComparisonOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public ComparisonOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterComparisonOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitComparisonOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitComparisonOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class DotOpContext extends ExpressionContext {
+		public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); }
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public DotOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterDotOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitDotOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitDotOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class MathOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public MathOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterMathOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitMathOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitMathOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class BitShiftOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public BitShiftOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBitShiftOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBitShiftOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBitShiftOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class QuestionQuestionOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public QuestionQuestionOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterQuestionQuestionOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitQuestionQuestionOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitQuestionQuestionOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class InstanceOfOpContext extends ExpressionContext {
+		public TypeContext type() {
+			return getRuleContext(TypeContext.class,0);
+		}
+		public ExpressionContext expression() {
+			return getRuleContext(ExpressionContext.class,0);
+		}
+		public InstanceOfOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterInstanceOfOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitInstanceOfOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitInstanceOfOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+	public static class BinaryOpContext extends ExpressionContext {
+		public ExpressionContext left;
+		public Token op;
+		public ExpressionContext right;
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public BinaryOpContext(ExpressionContext ctx) { copyFrom(ctx); }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBinaryOp(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBinaryOp(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBinaryOp(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ExpressionContext expression() throws RecognitionException {
+		return expression(0);
+	}
+
+	private ExpressionContext expression(int _p) throws RecognitionException {
+		ParserRuleContext _parentctx = _ctx;
+		int _parentState = getState();
+		ExpressionContext _localctx = new ExpressionContext(_ctx, _parentState);
+		ExpressionContext _prevctx = _localctx;
+		int _startState = 6;
+		enterRecursionRule(_localctx, 6, RULE_expression, _p);
+		int _la;
+		try {
+			int _alt;
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(70);
+			switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) {
+			case 1:
+				{
+				_localctx = new CastOpContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+
+				setState(53); match(T__32);
+				setState(54); type();
+				setState(55); match(T__15);
+				setState(56); expression(16);
+				}
+				break;
+
+			case 2:
+				{
+				_localctx = new UnaryOpContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(58);
+				((UnaryOpContext)_localctx).op = _input.LT(1);
+				_la = _input.LA(1);
+				if ( !(_la==T__33 || _la==T__13) ) {
+					((UnaryOpContext)_localctx).op = _errHandler.recoverInline(this);
+				}
+				consume();
+				setState(59); expression(15);
+				}
+				break;
+
+			case 3:
+				{
+				_localctx = new UnaryOpContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(60);
+				((UnaryOpContext)_localctx).op = _input.LT(1);
+				_la = _input.LA(1);
+				if ( !(_la==T__5 || _la==T__0) ) {
+					((UnaryOpContext)_localctx).op = _errHandler.recoverInline(this);
+				}
+				consume();
+				setState(61); expression(14);
+				}
+				break;
+
+			case 4:
+				{
+				_localctx = new GroupingContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(62); match(T__32);
+				setState(63); expression(0);
+				setState(64); match(T__15);
+				}
+				break;
+
+			case 5:
+				{
+				_localctx = new PrimaryContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(66); literal();
+				}
+				break;
+
+			case 6:
+				{
+				_localctx = new PrimaryContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(67); identifier();
+				}
+				break;
+
+			case 7:
+				{
+				_localctx = new PrimaryContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(68); classExtraction();
+				}
+				break;
+
+			case 8:
+				{
+				_localctx = new ResourceContext(_localctx);
+				_ctx = _localctx;
+				_prevctx = _localctx;
+				setState(69); resources();
+				}
+				break;
+			}
+			_ctx.stop = _input.LT(-1);
+			setState(132);
+			_errHandler.sync(this);
+			_alt = getInterpreter().adaptivePredict(_input,5,_ctx);
+			while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
+				if ( _alt==1 ) {
+					if ( _parseListeners!=null ) triggerExitRuleEvent();
+					_prevctx = _localctx;
+					{
+					setState(130);
+					switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) {
+					case 1:
+						{
+						_localctx = new MathOpContext(new ExpressionContext(_parentctx, _parentState));
+						((MathOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(72);
+						if (!(precpred(_ctx, 13))) throw new FailedPredicateException(this, "precpred(_ctx, 13)");
+						setState(73);
+						((MathOpContext)_localctx).op = _input.LT(1);
+						_la = _input.LA(1);
+						if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__35) | (1L << T__18) | (1L << T__6))) != 0)) ) {
+							((MathOpContext)_localctx).op = _errHandler.recoverInline(this);
+						}
+						consume();
+						setState(74); ((MathOpContext)_localctx).right = expression(14);
+						}
+						break;
+
+					case 2:
+						{
+						_localctx = new MathOpContext(new ExpressionContext(_parentctx, _parentState));
+						((MathOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(75);
+						if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)");
+						setState(76);
+						((MathOpContext)_localctx).op = _input.LT(1);
+						_la = _input.LA(1);
+						if ( !(_la==T__33 || _la==T__13) ) {
+							((MathOpContext)_localctx).op = _errHandler.recoverInline(this);
+						}
+						consume();
+						setState(77); ((MathOpContext)_localctx).right = expression(13);
+						}
+						break;
+
+					case 3:
+						{
+						_localctx = new BitShiftOpContext(new ExpressionContext(_parentctx, _parentState));
+						((BitShiftOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(78);
+						if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)");
+						setState(79);
+						((BitShiftOpContext)_localctx).op = _input.LT(1);
+						_la = _input.LA(1);
+						if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__41) | (1L << T__25) | (1L << T__20))) != 0)) ) {
+							((BitShiftOpContext)_localctx).op = _errHandler.recoverInline(this);
+						}
+						consume();
+						setState(80); ((BitShiftOpContext)_localctx).right = expression(12);
+						}
+						break;
+
+					case 4:
+						{
+						_localctx = new ComparisonOpContext(new ExpressionContext(_parentctx, _parentState));
+						((ComparisonOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(81);
+						if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)");
+						setState(82);
+						((ComparisonOpContext)_localctx).op = _input.LT(1);
+						_la = _input.LA(1);
+						if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__30) | (1L << T__27) | (1L << T__9) | (1L << T__4))) != 0)) ) {
+							((ComparisonOpContext)_localctx).op = _errHandler.recoverInline(this);
+						}
+						consume();
+						setState(83); ((ComparisonOpContext)_localctx).right = expression(11);
+						}
+						break;
+
+					case 5:
+						{
+						_localctx = new ComparisonOpContext(new ExpressionContext(_parentctx, _parentState));
+						((ComparisonOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(84);
+						if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)");
+						setState(85);
+						((ComparisonOpContext)_localctx).op = _input.LT(1);
+						_la = _input.LA(1);
+						if ( !(_la==T__28 || _la==T__7) ) {
+							((ComparisonOpContext)_localctx).op = _errHandler.recoverInline(this);
+						}
+						consume();
+						setState(86); ((ComparisonOpContext)_localctx).right = expression(9);
+						}
+						break;
+
+					case 6:
+						{
+						_localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState));
+						((BinaryOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(87);
+						if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)");
+						setState(88); ((BinaryOpContext)_localctx).op = match(T__38);
+						setState(89); ((BinaryOpContext)_localctx).right = expression(8);
+						}
+						break;
+
+					case 7:
+						{
+						_localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState));
+						((BinaryOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(90);
+						if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)");
+						setState(91); ((BinaryOpContext)_localctx).op = match(T__17);
+						setState(92); ((BinaryOpContext)_localctx).right = expression(7);
+						}
+						break;
+
+					case 8:
+						{
+						_localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState));
+						((BinaryOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(93);
+						if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)");
+						setState(94); ((BinaryOpContext)_localctx).op = match(T__2);
+						setState(95); ((BinaryOpContext)_localctx).right = expression(6);
+						}
+						break;
+
+					case 9:
+						{
+						_localctx = new AndOrOpContext(new ExpressionContext(_parentctx, _parentState));
+						((AndOrOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(96);
+						if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
+						setState(97); ((AndOrOpContext)_localctx).op = match(T__11);
+						setState(98); ((AndOrOpContext)_localctx).right = expression(5);
+						}
+						break;
+
+					case 10:
+						{
+						_localctx = new AndOrOpContext(new ExpressionContext(_parentctx, _parentState));
+						((AndOrOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(99);
+						if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
+						setState(100); ((AndOrOpContext)_localctx).op = match(T__10);
+						setState(101); ((AndOrOpContext)_localctx).right = expression(4);
+						}
+						break;
+
+					case 11:
+						{
+						_localctx = new TernaryOpContext(new ExpressionContext(_parentctx, _parentState));
+						((TernaryOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(102);
+						if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
+						setState(103); ((TernaryOpContext)_localctx).op = match(T__26);
+						setState(104); ((TernaryOpContext)_localctx).iftrue = expression(0);
+						setState(105); match(T__31);
+						setState(106); ((TernaryOpContext)_localctx).iffalse = expression(3);
+						}
+						break;
+
+					case 12:
+						{
+						_localctx = new QuestionQuestionOpContext(new ExpressionContext(_parentctx, _parentState));
+						((QuestionQuestionOpContext)_localctx).left = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(108);
+						if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
+						setState(109); ((QuestionQuestionOpContext)_localctx).op = match(T__8);
+						setState(110); ((QuestionQuestionOpContext)_localctx).right = expression(2);
+						}
+						break;
+
+					case 13:
+						{
+						_localctx = new DotOpContext(new ExpressionContext(_parentctx, _parentState));
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(111);
+						if (!(precpred(_ctx, 19))) throw new FailedPredicateException(this, "precpred(_ctx, 19)");
+						setState(112); match(T__14);
+						setState(113); match(Identifier);
+						}
+						break;
+
+					case 14:
+						{
+						_localctx = new BracketOpContext(new ExpressionContext(_parentctx, _parentState));
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(114);
+						if (!(precpred(_ctx, 18))) throw new FailedPredicateException(this, "precpred(_ctx, 18)");
+						setState(115); match(T__34);
+						setState(116); expression(0);
+						setState(117); match(T__40);
+						}
+						break;
+
+					case 15:
+						{
+						_localctx = new MethodInvocationContext(new ExpressionContext(_parentctx, _parentState));
+						((MethodInvocationContext)_localctx).target = _prevctx;
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(119);
+						if (!(precpred(_ctx, 17))) throw new FailedPredicateException(this, "precpred(_ctx, 17)");
+						setState(120); match(T__14);
+						setState(121); ((MethodInvocationContext)_localctx).methodName = match(Identifier);
+						setState(122); match(T__32);
+						setState(124);
+						_la = _input.LA(1);
+						if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__33) | (1L << T__32) | (1L << T__29) | (1L << T__24) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16) | (1L << T__13) | (1L << T__5) | (1L << T__0) | (1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << SingleQuoteString) | (1L << DoubleQuoteString) | (1L << NullLiteral) | (1L << Identifier) | (1L << ResourceReference))) != 0)) {
+							{
+							setState(123); ((MethodInvocationContext)_localctx).args = expressionList();
+							}
+						}
+
+						setState(126); match(T__15);
+						}
+						break;
+
+					case 16:
+						{
+						_localctx = new InstanceOfOpContext(new ExpressionContext(_parentctx, _parentState));
+						pushNewRecursionContext(_localctx, _startState, RULE_expression);
+						setState(127);
+						if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)");
+						setState(128); match(T__1);
+						setState(129); type();
+						}
+						break;
+					}
+					} 
+				}
+				setState(134);
+				_errHandler.sync(this);
+				_alt = getInterpreter().adaptivePredict(_input,5,_ctx);
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			unrollRecursionContexts(_parentctx);
+		}
+		return _localctx;
+	}
+
+	public static class ClassExtractionContext extends ParserRuleContext {
+		public TypeContext type() {
+			return getRuleContext(TypeContext.class,0);
+		}
+		public ClassExtractionContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_classExtraction; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterClassExtraction(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitClassExtraction(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitClassExtraction(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ClassExtractionContext classExtraction() throws RecognitionException {
+		ClassExtractionContext _localctx = new ClassExtractionContext(_ctx, getState());
+		enterRule(_localctx, 8, RULE_classExtraction);
+		try {
+			setState(142);
+			switch (_input.LA(1)) {
+			case T__42:
+			case T__39:
+			case T__29:
+			case T__23:
+			case T__22:
+			case T__21:
+			case T__19:
+			case T__16:
+			case Identifier:
+				enterOuterAlt(_localctx, 1);
+				{
+				setState(135); type();
+				setState(136); match(T__14);
+				setState(137); match(T__3);
+				}
+				break;
+			case T__24:
+				enterOuterAlt(_localctx, 2);
+				{
+				setState(139); match(T__24);
+				setState(140); match(T__14);
+				setState(141); match(T__3);
+				}
+				break;
+			default:
+				throw new NoViableAltException(this);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ExpressionListContext extends ParserRuleContext {
+		public ExpressionContext expression(int i) {
+			return getRuleContext(ExpressionContext.class,i);
+		}
+		public List<? extends ExpressionContext> expression() {
+			return getRuleContexts(ExpressionContext.class);
+		}
+		public ExpressionListContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_expressionList; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExpressionList(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExpressionList(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExpressionList(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ExpressionListContext expressionList() throws RecognitionException {
+		ExpressionListContext _localctx = new ExpressionListContext(_ctx, getState());
+		enterRule(_localctx, 10, RULE_expressionList);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(144); expression(0);
+			setState(149);
+			_errHandler.sync(this);
+			_la = _input.LA(1);
+			while (_la==T__36) {
+				{
+				{
+				setState(145); match(T__36);
+				setState(146); expression(0);
+				}
+				}
+				setState(151);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class LiteralContext extends ParserRuleContext {
+		public StringLiteralContext stringLiteral() {
+			return getRuleContext(StringLiteralContext.class,0);
+		}
+		public JavaLiteralContext javaLiteral() {
+			return getRuleContext(JavaLiteralContext.class,0);
+		}
+		public LiteralContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_literal; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterLiteral(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitLiteral(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitLiteral(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final LiteralContext literal() throws RecognitionException {
+		LiteralContext _localctx = new LiteralContext(_ctx, getState());
+		enterRule(_localctx, 12, RULE_literal);
+		try {
+			setState(154);
+			switch (_input.LA(1)) {
+			case IntegerLiteral:
+			case FloatingPointLiteral:
+			case BooleanLiteral:
+			case CharacterLiteral:
+			case NullLiteral:
+				enterOuterAlt(_localctx, 1);
+				{
+				setState(152); javaLiteral();
+				}
+				break;
+			case SingleQuoteString:
+			case DoubleQuoteString:
+				enterOuterAlt(_localctx, 2);
+				{
+				setState(153); stringLiteral();
+				}
+				break;
+			default:
+				throw new NoViableAltException(this);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class IdentifierContext extends ParserRuleContext {
+		public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); }
+		public IdentifierContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_identifier; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterIdentifier(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitIdentifier(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitIdentifier(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final IdentifierContext identifier() throws RecognitionException {
+		IdentifierContext _localctx = new IdentifierContext(_ctx, getState());
+		enterRule(_localctx, 14, RULE_identifier);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(156); match(Identifier);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class JavaLiteralContext extends ParserRuleContext {
+		public TerminalNode NullLiteral() { return getToken(BindingExpressionParser.NullLiteral, 0); }
+		public TerminalNode CharacterLiteral() { return getToken(BindingExpressionParser.CharacterLiteral, 0); }
+		public TerminalNode IntegerLiteral() { return getToken(BindingExpressionParser.IntegerLiteral, 0); }
+		public TerminalNode FloatingPointLiteral() { return getToken(BindingExpressionParser.FloatingPointLiteral, 0); }
+		public TerminalNode BooleanLiteral() { return getToken(BindingExpressionParser.BooleanLiteral, 0); }
+		public JavaLiteralContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_javaLiteral; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterJavaLiteral(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitJavaLiteral(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitJavaLiteral(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final JavaLiteralContext javaLiteral() throws RecognitionException {
+		JavaLiteralContext _localctx = new JavaLiteralContext(_ctx, getState());
+		enterRule(_localctx, 16, RULE_javaLiteral);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(158);
+			_la = _input.LA(1);
+			if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << NullLiteral))) != 0)) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class StringLiteralContext extends ParserRuleContext {
+		public TerminalNode SingleQuoteString() { return getToken(BindingExpressionParser.SingleQuoteString, 0); }
+		public TerminalNode DoubleQuoteString() { return getToken(BindingExpressionParser.DoubleQuoteString, 0); }
+		public StringLiteralContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_stringLiteral; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterStringLiteral(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitStringLiteral(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitStringLiteral(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final StringLiteralContext stringLiteral() throws RecognitionException {
+		StringLiteralContext _localctx = new StringLiteralContext(_ctx, getState());
+		enterRule(_localctx, 18, RULE_stringLiteral);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(160);
+			_la = _input.LA(1);
+			if ( !(_la==SingleQuoteString || _la==DoubleQuoteString) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ExplicitGenericInvocationContext extends ParserRuleContext {
+		public ExplicitGenericInvocationSuffixContext explicitGenericInvocationSuffix() {
+			return getRuleContext(ExplicitGenericInvocationSuffixContext.class,0);
+		}
+		public TypeArgumentsContext typeArguments() {
+			return getRuleContext(TypeArgumentsContext.class,0);
+		}
+		public ExplicitGenericInvocationContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_explicitGenericInvocation; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExplicitGenericInvocation(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExplicitGenericInvocation(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExplicitGenericInvocation(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ExplicitGenericInvocationContext explicitGenericInvocation() throws RecognitionException {
+		ExplicitGenericInvocationContext _localctx = new ExplicitGenericInvocationContext(_ctx, getState());
+		enterRule(_localctx, 20, RULE_explicitGenericInvocation);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(162); typeArguments();
+			setState(163); explicitGenericInvocationSuffix();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class TypeArgumentsContext extends ParserRuleContext {
+		public TypeContext type(int i) {
+			return getRuleContext(TypeContext.class,i);
+		}
+		public List<? extends TypeContext> type() {
+			return getRuleContexts(TypeContext.class);
+		}
+		public TypeArgumentsContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_typeArguments; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterTypeArguments(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitTypeArguments(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitTypeArguments(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final TypeArgumentsContext typeArguments() throws RecognitionException {
+		TypeArgumentsContext _localctx = new TypeArgumentsContext(_ctx, getState());
+		enterRule(_localctx, 22, RULE_typeArguments);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(165); match(T__30);
+			setState(166); type();
+			setState(171);
+			_errHandler.sync(this);
+			_la = _input.LA(1);
+			while (_la==T__36) {
+				{
+				{
+				setState(167); match(T__36);
+				setState(168); type();
+				}
+				}
+				setState(173);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+			}
+			setState(174); match(T__9);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class TypeContext extends ParserRuleContext {
+		public PrimitiveTypeContext primitiveType() {
+			return getRuleContext(PrimitiveTypeContext.class,0);
+		}
+		public ClassOrInterfaceTypeContext classOrInterfaceType() {
+			return getRuleContext(ClassOrInterfaceTypeContext.class,0);
+		}
+		public TypeContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_type; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterType(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitType(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitType(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final TypeContext type() throws RecognitionException {
+		TypeContext _localctx = new TypeContext(_ctx, getState());
+		enterRule(_localctx, 24, RULE_type);
+		try {
+			int _alt;
+			setState(192);
+			switch (_input.LA(1)) {
+			case Identifier:
+				enterOuterAlt(_localctx, 1);
+				{
+				setState(176); classOrInterfaceType();
+				setState(181);
+				_errHandler.sync(this);
+				_alt = getInterpreter().adaptivePredict(_input,10,_ctx);
+				while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
+					if ( _alt==1 ) {
+						{
+						{
+						setState(177); match(T__34);
+						setState(178); match(T__40);
+						}
+						} 
+					}
+					setState(183);
+					_errHandler.sync(this);
+					_alt = getInterpreter().adaptivePredict(_input,10,_ctx);
+				}
+				}
+				break;
+			case T__42:
+			case T__39:
+			case T__29:
+			case T__23:
+			case T__22:
+			case T__21:
+			case T__19:
+			case T__16:
+				enterOuterAlt(_localctx, 2);
+				{
+				setState(184); primitiveType();
+				setState(189);
+				_errHandler.sync(this);
+				_alt = getInterpreter().adaptivePredict(_input,11,_ctx);
+				while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
+					if ( _alt==1 ) {
+						{
+						{
+						setState(185); match(T__34);
+						setState(186); match(T__40);
+						}
+						} 
+					}
+					setState(191);
+					_errHandler.sync(this);
+					_alt = getInterpreter().adaptivePredict(_input,11,_ctx);
+				}
+				}
+				break;
+			default:
+				throw new NoViableAltException(this);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ExplicitGenericInvocationSuffixContext extends ParserRuleContext {
+		public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); }
+		public ArgumentsContext arguments() {
+			return getRuleContext(ArgumentsContext.class,0);
+		}
+		public ExplicitGenericInvocationSuffixContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_explicitGenericInvocationSuffix; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExplicitGenericInvocationSuffix(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExplicitGenericInvocationSuffix(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExplicitGenericInvocationSuffix(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ExplicitGenericInvocationSuffixContext explicitGenericInvocationSuffix() throws RecognitionException {
+		ExplicitGenericInvocationSuffixContext _localctx = new ExplicitGenericInvocationSuffixContext(_ctx, getState());
+		enterRule(_localctx, 26, RULE_explicitGenericInvocationSuffix);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(194); match(Identifier);
+			setState(195); arguments();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ArgumentsContext extends ParserRuleContext {
+		public ExpressionListContext expressionList() {
+			return getRuleContext(ExpressionListContext.class,0);
+		}
+		public ArgumentsContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_arguments; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterArguments(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitArguments(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitArguments(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ArgumentsContext arguments() throws RecognitionException {
+		ArgumentsContext _localctx = new ArgumentsContext(_ctx, getState());
+		enterRule(_localctx, 28, RULE_arguments);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(197); match(T__32);
+			setState(199);
+			_la = _input.LA(1);
+			if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__33) | (1L << T__32) | (1L << T__29) | (1L << T__24) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16) | (1L << T__13) | (1L << T__5) | (1L << T__0) | (1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << SingleQuoteString) | (1L << DoubleQuoteString) | (1L << NullLiteral) | (1L << Identifier) | (1L << ResourceReference))) != 0)) {
+				{
+				setState(198); expressionList();
+				}
+			}
+
+			setState(201); match(T__15);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ClassOrInterfaceTypeContext extends ParserRuleContext {
+		public TypeArgumentsContext typeArguments(int i) {
+			return getRuleContext(TypeArgumentsContext.class,i);
+		}
+		public TerminalNode Identifier(int i) {
+			return getToken(BindingExpressionParser.Identifier, i);
+		}
+		public List<? extends TerminalNode> Identifier() { return getTokens(BindingExpressionParser.Identifier); }
+		public List<? extends TypeArgumentsContext> typeArguments() {
+			return getRuleContexts(TypeArgumentsContext.class);
+		}
+		public IdentifierContext identifier() {
+			return getRuleContext(IdentifierContext.class,0);
+		}
+		public ClassOrInterfaceTypeContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_classOrInterfaceType; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterClassOrInterfaceType(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitClassOrInterfaceType(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitClassOrInterfaceType(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ClassOrInterfaceTypeContext classOrInterfaceType() throws RecognitionException {
+		ClassOrInterfaceTypeContext _localctx = new ClassOrInterfaceTypeContext(_ctx, getState());
+		enterRule(_localctx, 30, RULE_classOrInterfaceType);
+		try {
+			int _alt;
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(203); identifier();
+			setState(205);
+			switch ( getInterpreter().adaptivePredict(_input,14,_ctx) ) {
+			case 1:
+				{
+				setState(204); typeArguments();
+				}
+				break;
+			}
+			setState(214);
+			_errHandler.sync(this);
+			_alt = getInterpreter().adaptivePredict(_input,16,_ctx);
+			while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
+				if ( _alt==1 ) {
+					{
+					{
+					setState(207); match(T__14);
+					setState(208); match(Identifier);
+					setState(210);
+					switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) {
+					case 1:
+						{
+						setState(209); typeArguments();
+						}
+						break;
+					}
+					}
+					} 
+				}
+				setState(216);
+				_errHandler.sync(this);
+				_alt = getInterpreter().adaptivePredict(_input,16,_ctx);
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class PrimitiveTypeContext extends ParserRuleContext {
+		public PrimitiveTypeContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_primitiveType; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterPrimitiveType(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitPrimitiveType(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitPrimitiveType(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final PrimitiveTypeContext primitiveType() throws RecognitionException {
+		PrimitiveTypeContext _localctx = new PrimitiveTypeContext(_ctx, getState());
+		enterRule(_localctx, 32, RULE_primitiveType);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(217);
+			_la = _input.LA(1);
+			if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__29) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16))) != 0)) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ResourcesContext extends ParserRuleContext {
+		public ResourceParametersContext resourceParameters() {
+			return getRuleContext(ResourceParametersContext.class,0);
+		}
+		public TerminalNode ResourceReference() { return getToken(BindingExpressionParser.ResourceReference, 0); }
+		public ResourcesContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_resources; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResources(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResources(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResources(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ResourcesContext resources() throws RecognitionException {
+		ResourcesContext _localctx = new ResourcesContext(_ctx, getState());
+		enterRule(_localctx, 34, RULE_resources);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(219); match(ResourceReference);
+			setState(221);
+			switch ( getInterpreter().adaptivePredict(_input,17,_ctx) ) {
+			case 1:
+				{
+				setState(220); resourceParameters();
+				}
+				break;
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ResourceParametersContext extends ParserRuleContext {
+		public ExpressionListContext expressionList() {
+			return getRuleContext(ExpressionListContext.class,0);
+		}
+		public ResourceParametersContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_resourceParameters; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResourceParameters(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResourceParameters(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResourceParameters(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ResourceParametersContext resourceParameters() throws RecognitionException {
+		ResourceParametersContext _localctx = new ResourceParametersContext(_ctx, getState());
+		enterRule(_localctx, 36, RULE_resourceParameters);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(223); match(T__32);
+			setState(224); expressionList();
+			setState(225); match(T__15);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
+		switch (ruleIndex) {
+		case 3: return expression_sempred((ExpressionContext)_localctx, predIndex);
+		}
+		return true;
+	}
+	private boolean expression_sempred(ExpressionContext _localctx, int predIndex) {
+		switch (predIndex) {
+		case 0: return precpred(_ctx, 13);
+
+		case 1: return precpred(_ctx, 12);
+
+		case 2: return precpred(_ctx, 11);
+
+		case 3: return precpred(_ctx, 10);
+
+		case 4: return precpred(_ctx, 8);
+
+		case 5: return precpred(_ctx, 7);
+
+		case 6: return precpred(_ctx, 6);
+
+		case 7: return precpred(_ctx, 5);
+
+		case 8: return precpred(_ctx, 4);
+
+		case 9: return precpred(_ctx, 3);
+
+		case 10: return precpred(_ctx, 2);
+
+		case 11: return precpred(_ctx, 1);
+
+		case 12: return precpred(_ctx, 19);
+
+		case 13: return precpred(_ctx, 18);
+
+		case 14: return precpred(_ctx, 17);
+
+		case 15: return precpred(_ctx, 9);
+		}
+		return true;
+	}
+
+	public static final String _serializedATN =
+		"\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\3:\u00e6\4\2\t\2\4"+
+		"\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+
+		"\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
+		"\4\23\t\23\4\24\t\24\3\2\3\2\5\2+\n\2\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4"+
+		"\5\4\65\n\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+
+		"\5\3\5\3\5\3\5\5\5I\n\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+
+		"\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5"+
+		"\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+
+		"\5\3\5\3\5\3\5\3\5\3\5\5\5\177\n\5\3\5\3\5\3\5\3\5\7\5\u0085\n\5\f\5\16"+
+		"\5\u0088\13\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\u0091\n\6\3\7\3\7\3\7\7"+
+		"\7\u0096\n\7\f\7\16\7\u0099\13\7\3\b\3\b\5\b\u009d\n\b\3\t\3\t\3\n\3\n"+
+		"\3\13\3\13\3\f\3\f\3\f\3\r\3\r\3\r\3\r\7\r\u00ac\n\r\f\r\16\r\u00af\13"+
+		"\r\3\r\3\r\3\16\3\16\3\16\7\16\u00b6\n\16\f\16\16\16\u00b9\13\16\3\16"+
+		"\3\16\3\16\7\16\u00be\n\16\f\16\16\16\u00c1\13\16\5\16\u00c3\n\16\3\17"+
+		"\3\17\3\17\3\20\3\20\5\20\u00ca\n\20\3\20\3\20\3\21\3\21\5\21\u00d0\n"+
+		"\21\3\21\3\21\3\21\5\21\u00d5\n\21\7\21\u00d7\n\21\f\21\16\21\u00da\13"+
+		"\21\3\22\3\22\3\23\3\23\5\23\u00e0\n\23\3\24\3\24\3\24\3\24\3\24\2\2\3"+
+		"\b\25\2\2\4\2\6\2\b\2\n\2\f\2\16\2\20\2\22\2\24\2\26\2\30\2\32\2\34\2"+
+		"\36\2 \2\"\2$\2&\2\2\13\4\2\f\f  \4\2((--\5\2\n\n\33\33\'\'\5\2\4\4\24"+
+		"\24\31\31\6\2\17\17\22\22$$))\4\2\21\21&&\4\2/\62\65\65\3\2\63\64\b\2"+
+		"\3\3\6\6\20\20\26\30\32\32\35\35\u00f9\2(\3\2\2\2\4,\3\2\2\2\6\64\3\2"+
+		"\2\2\bH\3\2\2\2\n\u0090\3\2\2\2\f\u0092\3\2\2\2\16\u009c\3\2\2\2\20\u009e"+
+		"\3\2\2\2\22\u00a0\3\2\2\2\24\u00a2\3\2\2\2\26\u00a4\3\2\2\2\30\u00a7\3"+
+		"\2\2\2\32\u00c2\3\2\2\2\34\u00c4\3\2\2\2\36\u00c7\3\2\2\2 \u00cd\3\2\2"+
+		"\2\"\u00db\3\2\2\2$\u00dd\3\2\2\2&\u00e1\3\2\2\2(*\5\b\5\2)+\5\4\3\2*"+
+		")\3\2\2\2*+\3\2\2\2+\3\3\2\2\2,-\7\t\2\2-.\7\b\2\2./\7!\2\2/\60\5\6\4"+
+		"\2\60\5\3\2\2\2\61\65\5\16\b\2\62\65\78\2\2\63\65\5\20\t\2\64\61\3\2\2"+
+		"\2\64\62\3\2\2\2\64\63\3\2\2\2\65\7\3\2\2\2\66\67\b\5\1\2\678\7\r\2\2"+
+		"89\5\32\16\29:\7\36\2\2:;\5\b\5\22;I\3\2\2\2<=\t\2\2\2=I\5\b\5\21>?\t"+
+		"\3\2\2?I\5\b\5\20@A\7\r\2\2AB\5\b\5\2BC\7\36\2\2CI\3\2\2\2DI\5\16\b\2"+
+		"EI\5\20\t\2FI\5\n\6\2GI\5$\23\2H\66\3\2\2\2H<\3\2\2\2H>\3\2\2\2H@\3\2"+
+		"\2\2HD\3\2\2\2HE\3\2\2\2HF\3\2\2\2HG\3\2\2\2I\u0086\3\2\2\2JK\f\17\2\2"+
+		"KL\t\4\2\2L\u0085\5\b\5\20MN\f\16\2\2NO\t\2\2\2O\u0085\5\b\5\17PQ\f\r"+
+		"\2\2QR\t\5\2\2R\u0085\5\b\5\16ST\f\f\2\2TU\t\6\2\2U\u0085\5\b\5\rVW\f"+
+		"\n\2\2WX\t\7\2\2X\u0085\5\b\5\13YZ\f\t\2\2Z[\7\7\2\2[\u0085\5\b\5\n\\"+
+		"]\f\b\2\2]^\7\34\2\2^\u0085\5\b\5\t_`\f\7\2\2`a\7+\2\2a\u0085\5\b\5\b"+
+		"bc\f\6\2\2cd\7\"\2\2d\u0085\5\b\5\7ef\f\5\2\2fg\7#\2\2g\u0085\5\b\5\6"+
+		"hi\f\4\2\2ij\7\23\2\2jk\5\b\5\2kl\7\16\2\2lm\5\b\5\5m\u0085\3\2\2\2no"+
+		"\f\3\2\2op\7%\2\2p\u0085\5\b\5\4qr\f\25\2\2rs\7\37\2\2s\u0085\7\66\2\2"+
+		"tu\f\24\2\2uv\7\13\2\2vw\5\b\5\2wx\7\5\2\2x\u0085\3\2\2\2yz\f\23\2\2z"+
+		"{\7\37\2\2{|\7\66\2\2|~\7\r\2\2}\177\5\f\7\2~}\3\2\2\2~\177\3\2\2\2\177"+
+		"\u0080\3\2\2\2\u0080\u0085\7\36\2\2\u0081\u0082\f\13\2\2\u0082\u0083\7"+
+		",\2\2\u0083\u0085\5\32\16\2\u0084J\3\2\2\2\u0084M\3\2\2\2\u0084P\3\2\2"+
+		"\2\u0084S\3\2\2\2\u0084V\3\2\2\2\u0084Y\3\2\2\2\u0084\\\3\2\2\2\u0084"+
+		"_\3\2\2\2\u0084b\3\2\2\2\u0084e\3\2\2\2\u0084h\3\2\2\2\u0084n\3\2\2\2"+
+		"\u0084q\3\2\2\2\u0084t\3\2\2\2\u0084y\3\2\2\2\u0084\u0081\3\2\2\2\u0085"+
+		"\u0088\3\2\2\2\u0086\u0084\3\2\2\2\u0086\u0087\3\2\2\2\u0087\t\3\2\2\2"+
+		"\u0088\u0086\3\2\2\2\u0089\u008a\5\32\16\2\u008a\u008b\7\37\2\2\u008b"+
+		"\u008c\7*\2\2\u008c\u0091\3\2\2\2\u008d\u008e\7\25\2\2\u008e\u008f\7\37"+
+		"\2\2\u008f\u0091\7*\2\2\u0090\u0089\3\2\2\2\u0090\u008d\3\2\2\2\u0091"+
+		"\13\3\2\2\2\u0092\u0097\5\b\5\2\u0093\u0094\7\t\2\2\u0094\u0096\5\b\5"+
+		"\2\u0095\u0093\3\2\2\2\u0096\u0099\3\2\2\2\u0097\u0095\3\2\2\2\u0097\u0098"+
+		"\3\2\2\2\u0098\r\3\2\2\2\u0099\u0097\3\2\2\2\u009a\u009d\5\22\n\2\u009b"+
+		"\u009d\5\24\13\2\u009c\u009a\3\2\2\2\u009c\u009b\3\2\2\2\u009d\17\3\2"+
+		"\2\2\u009e\u009f\7\66\2\2\u009f\21\3\2\2\2\u00a0\u00a1\t\b\2\2\u00a1\23"+
+		"\3\2\2\2\u00a2\u00a3\t\t\2\2\u00a3\25\3\2\2\2\u00a4\u00a5\5\30\r\2\u00a5"+
+		"\u00a6\5\34\17\2\u00a6\27\3\2\2\2\u00a7\u00a8\7\17\2\2\u00a8\u00ad\5\32"+
+		"\16\2\u00a9\u00aa\7\t\2\2\u00aa\u00ac\5\32\16\2\u00ab\u00a9\3\2\2\2\u00ac"+
+		"\u00af\3\2\2\2\u00ad\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b0\3\2"+
+		"\2\2\u00af\u00ad\3\2\2\2\u00b0\u00b1\7$\2\2\u00b1\31\3\2\2\2\u00b2\u00b7"+
+		"\5 \21\2\u00b3\u00b4\7\13\2\2\u00b4\u00b6\7\5\2\2\u00b5\u00b3\3\2\2\2"+
+		"\u00b6\u00b9\3\2\2\2\u00b7\u00b5\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b8\u00c3"+
+		"\3\2\2\2\u00b9\u00b7\3\2\2\2\u00ba\u00bf\5\"\22\2\u00bb\u00bc\7\13\2\2"+
+		"\u00bc\u00be\7\5\2\2\u00bd\u00bb\3\2\2\2\u00be\u00c1\3\2\2\2\u00bf\u00bd"+
+		"\3\2\2\2\u00bf\u00c0\3\2\2\2\u00c0\u00c3\3\2\2\2\u00c1\u00bf\3\2\2\2\u00c2"+
+		"\u00b2\3\2\2\2\u00c2\u00ba\3\2\2\2\u00c3\33\3\2\2\2\u00c4\u00c5\7\66\2"+
+		"\2\u00c5\u00c6\5\36\20\2\u00c6\35\3\2\2\2\u00c7\u00c9\7\r\2\2\u00c8\u00ca"+
+		"\5\f\7\2\u00c9\u00c8\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca\u00cb\3\2\2\2\u00cb"+
+		"\u00cc\7\36\2\2\u00cc\37\3\2\2\2\u00cd\u00cf\5\20\t\2\u00ce\u00d0\5\30"+
+		"\r\2\u00cf\u00ce\3\2\2\2\u00cf\u00d0\3\2\2\2\u00d0\u00d8\3\2\2\2\u00d1"+
+		"\u00d2\7\37\2\2\u00d2\u00d4\7\66\2\2\u00d3\u00d5\5\30\r\2\u00d4\u00d3"+
+		"\3\2\2\2\u00d4\u00d5\3\2\2\2\u00d5\u00d7\3\2\2\2\u00d6\u00d1\3\2\2\2\u00d7"+
+		"\u00da\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9!\3\2\2\2"+
+		"\u00da\u00d8\3\2\2\2\u00db\u00dc\t\n\2\2\u00dc#\3\2\2\2\u00dd\u00df\7"+
+		"8\2\2\u00de\u00e0\5&\24\2\u00df\u00de\3\2\2\2\u00df\u00e0\3\2\2\2\u00e0"+
+		"%\3\2\2\2\u00e1\u00e2\7\r\2\2\u00e2\u00e3\5\f\7\2\u00e3\u00e4\7\36\2\2"+
+		"\u00e4\'\3\2\2\2\24*\64H~\u0084\u0086\u0090\u0097\u009c\u00ad\u00b7\u00bf"+
+		"\u00c2\u00c9\u00cf\u00d4\u00d8\u00df";
+	public static final ATN _ATN =
+		new ATNDeserializer().deserialize(_serializedATN.toCharArray());
+	static {
+	}
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java
new file mode 100644
index 0000000..d589a7d
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java
@@ -0,0 +1,275 @@
+// Generated from BindingExpression.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
+
+/**
+ * This interface defines a complete generic visitor for a parse tree produced
+ * by {@link BindingExpressionParser}.
+ *
+ * @param <Result> The return type of the visit operation. Use {@link Void} for
+ * operations with no return type.
+ */
+public interface BindingExpressionVisitor<Result> extends ParseTreeVisitor<Result> {
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#resources}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code BracketOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code UnaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code CastOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#resourceParameters}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code AndOrOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code MethodInvocation}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#expressionList}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#stringLiteral}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code Primary}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#type}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitType(@NotNull BindingExpressionParser.TypeContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#bindingSyntax}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code ComparisonOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code TernaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#constantValue}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code DotOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#defaults}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code BitShiftOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code InstanceOfOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code BinaryOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code Resource}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitResource(@NotNull BindingExpressionParser.ResourceContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#typeArguments}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code Grouping}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code MathOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#classExtraction}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#arguments}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#primitiveType}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx);
+
+	/**
+	 * Visit a parse tree produced by the {@code QuestionQuestionOp}
+	 * labeled alternative in {@link BindingExpressionParser#expression}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#javaLiteral}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#identifier}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link BindingExpressionParser#literal}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx);
+}
\ No newline at end of file
diff --git a/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java b/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java
new file mode 100644
index 0000000..ba5c395
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.databinder.parser;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+
+public class Main {
+    static String input = "`name` + last_name";
+    static class Field {
+        String fieldName;
+    }
+    public static void main(String[] args) {
+        // ANTLRInputStream inputStream = new ANTLRInputStream(input);
+//         DataBinderLexer lexer = new DataBinderLexer(inputStream);
+//         CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+//         DataBinderParser parser = new DataBinderParser(tokenStream);
+//         ParseTreeWalker walker = new ParseTreeWalker();
+//         System.out.println(parser.expr().toStringTree(parser));
+        float[] aa = new float[2];
+
+    }
+}
diff --git a/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java b/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java
new file mode 100644
index 0000000..9197ccf4
--- /dev/null
+++ b/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java
@@ -0,0 +1,371 @@
+package android.databinding;
+
+import android.databinding.parser.BindingExpressionLexer;
+import android.databinding.parser.BindingExpressionParser;
+import android.databinding.parser.BindingExpressionParser.AndOrOpContext;
+import android.databinding.parser.BindingExpressionParser.BinaryOpContext;
+import android.databinding.parser.BindingExpressionParser.BindingSyntaxContext;
+import android.databinding.parser.BindingExpressionParser.BitShiftOpContext;
+import android.databinding.parser.BindingExpressionParser.ComparisonOpContext;
+import android.databinding.parser.BindingExpressionParser.DefaultsContext;
+import android.databinding.parser.BindingExpressionParser.DotOpContext;
+import android.databinding.parser.BindingExpressionParser.ExpressionContext;
+import android.databinding.parser.BindingExpressionParser.GroupingContext;
+import android.databinding.parser.BindingExpressionParser.LiteralContext;
+import android.databinding.parser.BindingExpressionParser.MathOpContext;
+import android.databinding.parser.BindingExpressionParser.PrimaryContext;
+import android.databinding.parser.BindingExpressionParser.PrimitiveTypeContext;
+import android.databinding.parser.BindingExpressionParser.QuestionQuestionOpContext;
+import android.databinding.parser.BindingExpressionParser.ResourceContext;
+import android.databinding.parser.BindingExpressionParser.StringLiteralContext;
+import android.databinding.parser.BindingExpressionParser.TernaryOpContext;
+import android.databinding.parser.BindingExpressionParser.UnaryOpContext;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.junit.Test;
+
+import java.io.StringReader;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class BindingExpressionParserTest {
+
+    @Test
+    public void testSingleQuoteStringLiteral() throws Exception {
+        String expr = "`test`";
+        LiteralContext literal = parseLiteral(expr);
+        assertNotNull(literal);
+        StringLiteralContext stringLiteral = literal.stringLiteral();
+        assertNotNull(stringLiteral);
+        TerminalNode singleQuote = stringLiteral.SingleQuoteString();
+        Token token = singleQuote.getSymbol();
+        assertEquals("`test`", token.getText());
+    }
+
+    @Test
+    public void testDoubleQuoteStringLiteral() throws Exception {
+        String expr = "\"test\"";
+
+        LiteralContext literal = parseLiteral(expr);
+        StringLiteralContext stringLiteral = literal.stringLiteral();
+        TerminalNode singleQuote = stringLiteral.DoubleQuoteString();
+        Token token = singleQuote.getSymbol();
+        assertEquals("\"test\"", token.getText());
+    }
+
+    @Test
+    public void testSingleQuoteEscapeStringLiteral() throws Exception {
+        String expr = "`\"t\\`est\"`";
+        LiteralContext literal = parseLiteral(expr);
+        StringLiteralContext stringLiteral = literal.stringLiteral();
+        TerminalNode singleQuote = stringLiteral.SingleQuoteString();
+        Token token = singleQuote.getSymbol();
+        assertEquals("`\"t\\`est\"`", token.getText());
+    }
+
+    @Test
+    public void testCharLiteral() throws Exception {
+        LiteralContext literal = parseLiteral("'c'");
+        assertEquals("'c'", literal.getText());
+        literal = parseLiteral("'\\u0054'");
+        assertEquals("'\\u0054'", literal.getText());
+        literal = parseLiteral("'\\''");
+        assertEquals("'\\''", literal.getText());
+    }
+
+    @Test
+    public void testIntLiterals() throws Exception {
+        compareIntLiteral("123");
+        compareIntLiteral("123l");
+        compareIntLiteral("1_2_3l");
+        compareIntLiteral("123L");
+        compareIntLiteral("0xdeadbeef");
+        compareIntLiteral("0xdeadbeefl");
+        compareIntLiteral("0Xdeadbeef");
+        compareIntLiteral("0xdead_beefl");
+        compareIntLiteral("0xdead_beefL");
+        compareIntLiteral("01234567");
+        compareIntLiteral("01234567L");
+        compareIntLiteral("01234567l");
+        compareIntLiteral("0123_45_67l");
+        compareIntLiteral("0b0101");
+        compareIntLiteral("0b0101_0101");
+        compareIntLiteral("0B0101_0101");
+        compareIntLiteral("0B0101_0101L");
+        compareIntLiteral("0B0101_0101l");
+    }
+
+    @Test
+    public void testFloatLiterals() throws Exception {
+        compareFloatLiteral("0.12345");
+        compareFloatLiteral("0.12345f");
+        compareFloatLiteral("0.12345F");
+        compareFloatLiteral("132450.12345F");
+        compareFloatLiteral("132450.12345");
+        compareFloatLiteral("132450e123");
+        compareFloatLiteral("132450.4e123");
+    }
+
+    @Test
+    public void testBoolLiterals() throws Exception {
+        compareBoolLiteral("true");
+        compareBoolLiteral("false");
+    }
+
+    @Test
+    public void testNullLiteral() throws Exception {
+        LiteralContext literal = parseLiteral("null");
+        String token = literal.getText();
+        assertEquals("null", token);
+    }
+
+    @Test
+    public void testVoidExtraction() throws Exception {
+        PrimaryContext primary = parsePrimary("void.class");
+        assertNotNull(primary.classExtraction());
+        assertNull(primary.classExtraction().type());
+        assertEquals("void", primary.classExtraction().getChild(0).getText());
+    }
+
+    @Test
+    public void testPrimitiveClassExtraction() throws Exception {
+        PrimaryContext primary = parsePrimary("int.class");
+        PrimitiveTypeContext type = primary.classExtraction().type().primitiveType();
+        assertEquals("int", type.getText());
+    }
+
+    @Test
+    public void testIdentifier() throws Exception {
+        PrimaryContext primary = parsePrimary("abcdEfg");
+        assertEquals("abcdEfg", primary.identifier().getText());
+    }
+
+    @Test
+    public void testUnaryOperators() throws Exception {
+        compareUnaryOperators("+");
+        compareUnaryOperators("-");
+        compareUnaryOperators("!");
+        compareUnaryOperators("~");
+    }
+
+    @Test
+    public void testMathOperators() throws Exception {
+        compareMathOperators("+");
+        compareMathOperators("-");
+        compareMathOperators("*");
+        compareMathOperators("/");
+        compareMathOperators("%");
+    }
+
+    @Test
+    public void testBitShiftOperators() throws Exception {
+        compareBitShiftOperators(">>>");
+        compareBitShiftOperators("<<");
+        compareBitShiftOperators(">>");
+    }
+
+    @Test
+    public void testComparisonShiftOperators() throws Exception {
+        compareComparisonOperators("<");
+        compareComparisonOperators(">");
+        compareComparisonOperators("<=");
+        compareComparisonOperators(">=");
+        compareComparisonOperators("==");
+        compareComparisonOperators("!=");
+    }
+
+    @Test
+    public void testAndOrOperators() throws Exception {
+        compareAndOrOperators("&&");
+        compareAndOrOperators("||");
+    }
+
+    @Test
+    public void testBinaryOperators() throws Exception {
+        compareBinaryOperators("&");
+        compareBinaryOperators("|");
+        compareBinaryOperators("^");
+    }
+
+    @Test
+    public void testTernaryOperator() throws Exception {
+        TernaryOpContext expression = parseExpression("true ? 1 : 0");
+        assertEquals(5, expression.getChildCount());
+        assertEquals("true",
+                ((PrimaryContext) expression.left).literal().javaLiteral().getText());
+        assertEquals("?", expression.op.getText());
+        assertEquals("1",
+                ((PrimaryContext) expression.iftrue).literal().javaLiteral().getText());
+        assertEquals(":", expression.getChild(3).getText());
+        assertEquals("0", ((PrimaryContext) expression.iffalse).literal().javaLiteral().getText());
+    }
+
+    @Test
+    public void testDot() throws Exception {
+        DotOpContext expression = parseExpression("one.two.three");
+        assertEquals(3, expression.getChildCount());
+        assertEquals("three", expression.Identifier().getText());
+        assertEquals(".", expression.getChild(1).getText());
+        DotOpContext left = (DotOpContext) expression.expression();
+        assertEquals("two", left.Identifier().getText());
+        assertEquals(".", left.getChild(1).getText());
+        assertEquals("one", ((PrimaryContext) left.expression()).identifier().getText());
+    }
+
+    @Test
+    public void testQuestionQuestion() throws Exception {
+        QuestionQuestionOpContext expression = parseExpression("one ?? two");
+        assertEquals(3, expression.getChildCount());
+        assertEquals("one", ((PrimaryContext) expression.left).identifier().getText());
+        assertEquals("two", ((PrimaryContext) expression.right).identifier().getText());
+        assertEquals("??", expression.op.getText());
+    }
+
+    @Test
+    public void testResourceReference() throws Exception {
+        compareResource("@id/foo_bar");
+        compareResource("@transition/foo_bar");
+        compareResource("@anim/foo_bar");
+        compareResource("@animator/foo_bar");
+        compareResource("@android:id/foo_bar");
+        compareResource("@app:id/foo_bar");
+    }
+
+    @Test
+    public void testDefaults() throws Exception {
+        BindingSyntaxContext syntax = parseExpressionString("foo.bar, default = @id/foo_bar");
+        DefaultsContext defaults = syntax.defaults();
+        assertEquals("@id/foo_bar", defaults.constantValue().ResourceReference().getText());
+    }
+
+    @Test
+    public void testParentheses() throws Exception {
+        GroupingContext grouping = parseExpression("(1234)");
+        assertEquals("1234", grouping.expression().getText());
+    }
+
+    // ---------------------- Helpers --------------------
+
+    private void compareResource(String value) throws Exception {
+        ResourceContext resourceContext = parseExpression(value);
+        assertEquals(value, resourceContext.getText());
+    }
+
+    private void compareUnaryOperators(String op) throws Exception {
+        UnaryOpContext expression = parseExpression(op + " 2");
+        assertEquals(2, expression.getChildCount());
+        assertEquals(op, expression.op.getText());
+        assertEquals("2",
+                ((PrimaryContext) expression.expression()).literal().javaLiteral()
+                        .getText());
+    }
+
+    private void compareBinaryOperators(String op) throws Exception {
+        BinaryOpContext expression = parseExpression("1 " + op + " 2");
+        assertEquals(3, expression.getChildCount());
+        assertTrue(expression.left instanceof ExpressionContext);
+        String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText();
+        assertEquals("1", one);
+        assertEquals(op, expression.op.getText());
+        assertTrue(expression.right instanceof ExpressionContext);
+        String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText();
+        assertEquals("2", two);
+    }
+
+    private void compareMathOperators(String op) throws Exception {
+        MathOpContext expression = parseExpression("1 " + op + " 2");
+        assertEquals(3, expression.getChildCount());
+        assertTrue(expression.left instanceof ExpressionContext);
+        String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText();
+        assertEquals("1", one);
+        assertEquals(op, expression.op.getText());
+        assertTrue(expression.right instanceof ExpressionContext);
+        String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText();
+        assertEquals("2", two);
+    }
+
+    private void compareBitShiftOperators(String op) throws Exception {
+        BitShiftOpContext expression = parseExpression("1 " + op + " 2");
+        assertEquals(3, expression.getChildCount());
+        assertTrue(expression.left instanceof ExpressionContext);
+        String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText();
+        assertEquals("1", one);
+        assertEquals(op, expression.op.getText());
+        assertTrue(expression.right instanceof ExpressionContext);
+        String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText();
+        assertEquals("2", two);
+    }
+
+    private void compareComparisonOperators(String op) throws Exception {
+        ComparisonOpContext expression = parseExpression("1 " + op + " 2");
+        assertEquals(3, expression.getChildCount());
+        assertTrue(expression.left instanceof ExpressionContext);
+        String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText();
+        assertEquals("1", one);
+        assertEquals(op, expression.op.getText());
+        assertTrue(expression.right instanceof ExpressionContext);
+        String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText();
+        assertEquals("2", two);
+    }
+
+    private void compareAndOrOperators(String op) throws Exception {
+        AndOrOpContext expression = parseExpression("1 " + op + " 2");
+        assertEquals(3, expression.getChildCount());
+        assertTrue(expression.left instanceof ExpressionContext);
+        String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText();
+        assertEquals("1", one);
+        assertEquals(op, expression.op.getText());
+        assertTrue(expression.right instanceof ExpressionContext);
+        String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText();
+        assertEquals("2", two);
+    }
+
+    private void compareIntLiteral(String constant) throws Exception {
+        LiteralContext literal = parseLiteral(constant);
+        String token = literal.javaLiteral().getText();
+        assertEquals(constant, token);
+    }
+
+    private void compareFloatLiteral(String constant) throws Exception {
+        LiteralContext literal = parseLiteral(constant);
+        String token = literal.javaLiteral().getText();
+        assertEquals(constant, token);
+    }
+
+    private void compareBoolLiteral(String constant) throws Exception {
+        LiteralContext literal = parseLiteral(constant);
+        String token = literal.javaLiteral().getText();
+        assertEquals(constant, token);
+    }
+
+    private BindingSyntaxContext parse(String value) throws Exception {
+        return parseExpressionString(value);
+    }
+
+    private <T extends ExpressionContext> T parseExpression(String value) throws Exception {
+        ExpressionContext expressionContext = parse(value).expression();
+        return (T) expressionContext;
+    }
+
+    private PrimaryContext parsePrimary(String value) throws Exception {
+        return parseExpression(value);
+    }
+
+    private LiteralContext parseLiteral(String value) throws Exception {
+        return parsePrimary(value).literal();
+    }
+
+    BindingExpressionParser.BindingSyntaxContext parseExpressionString(String s) throws Exception {
+        ANTLRInputStream input = new ANTLRInputStream(new StringReader(s));
+        BindingExpressionLexer lexer = new BindingExpressionLexer(input);
+        CommonTokenStream tokens = new CommonTokenStream(lexer);
+        BindingExpressionParser parser = new BindingExpressionParser(tokens);
+        return parser.bindingSyntax();
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore b/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle b/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle
new file mode 100644
index 0000000..552e1a5
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+apply plugin: 'maven'
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://${config.mavenRepoDir}")
+            pom.artifactId = 'independent-library'
+            pom.version = config.snapshotVersion
+            pom.groupId = config.testGroup
+        }
+    }
+}
+
+connectedCheck.dependsOn uploadArchives
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro b/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java
new file mode 100644
index 0000000..a75974f
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding.test.independentlibrary;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java
new file mode 100644
index 0000000..2672186
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.databinding.test.independentlibrary;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+import android.widget.TextView;
+
+public class LibraryActivityTest extends ActivityInstrumentationTestCase2<LibraryActivity> {
+    public LibraryActivityTest() {
+        super(LibraryActivity.class);
+    }
+
+    public void testTextViewContents() throws Throwable {
+        final LibraryActivity activity = getActivity();
+        assertNotNull("test sanity", activity);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextView textView = (TextView) activity.findViewById(R.id.fooTextView);
+                final String expected = LibraryActivity.FIELD_VALUE + " " +
+                        LibraryActivity.FIELD_VALUE;
+                assertEquals(expected, textView.getText().toString());
+            }
+        });
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8b26ea7
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.databinding.test.independentlibrary">
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name">
+        <activity android:name=".LibraryActivity"/>
+    </application>
+
+</manifest>
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java
new file mode 100644
index 0000000..18b7bec
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.databinding.test.independentlibrary;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import android.databinding.test.independentlibrary.vo.MyBindableObject;
+import android.databinding.test.independentlibrary.databinding.LibraryLayoutBinding;
+public class LibraryActivity extends Activity {
+    public static final String FIELD_VALUE = "BAR";
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        LibraryLayoutBinding binding = LibraryLayoutBinding.inflate(this);
+        setContentView(binding.getRoot());
+        MyBindableObject object = new MyBindableObject();
+        object.setField(FIELD_VALUE);
+        binding.setFoo(object);
+        binding.executePendingBindings();
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java
new file mode 100644
index 0000000..e358e62
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding.test.independentlibrary;
+
+import android.databinding.BindingAdapter;
+import android.view.View;
+
+public class LibraryAdapter {
+    @BindingAdapter("myTagAttr")
+    public static void set(View view, String someTag) {
+        view.setTag(someTag);
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java
new file mode 100644
index 0000000..da66cef
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.databinding.test.independentlibrary.vo;
+
+import android.databinding.BaseObservable;
+import android.databinding.test.independentlibrary.BR;
+
+import android.databinding.Bindable;
+
+public class MyBindableObject extends BaseObservable {
+    @Bindable
+    private String mField;
+
+    public String getField() {
+        return mField;
+    }
+
+    public void setField(String field) {
+        mField = field;
+        notifyPropertyChanged(BR.field);
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml
new file mode 100644
index 0000000..4262eb3
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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="foo" type="android.databinding.test.independentlibrary.vo.MyBindableObject"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:id="@+id/fooTextView"
+            android:text='@{foo.field +  " " + foo.field}'/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8e6caf7
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <string name="app_name">IndependentLibrary</string>
+</resources>
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/build.gradle b/tools/data-binding/integration-tests/IndependentLibrary/build.gradle
new file mode 100644
index 0000000..d74b7e6
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    println "loaded config"
+
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties b/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties
new file mode 100644
index 0000000..efd2362
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..de86a57
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradlew b/tools/data-binding/integration-tests/IndependentLibrary/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat b/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle b/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle
new file mode 100644
index 0000000..e2afad2
--- /dev/null
+++ b/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */
+
+include ':app'
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore b/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle
new file mode 100644
index 0000000..b09ed61
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "22"
+
+    defaultConfig {
+        applicationId "com.android.databinding.multimoduletestapp"
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+println "combined ${config.testGroup}.independent-library:${config.snapshotVersion}"
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    compile project(':testlibrary')
+    compile "com.android.support:support-v4:+"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+    compile "${config.testGroup}:independent-library:${config.snapshotVersion}"
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro b/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java
new file mode 100644
index 0000000..b3f219c
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.databinding.multimoduletestapp;
+
+import android.databinding.testlibrary.ObservableInLibrary;
+
+import android.app.Application;
+import android.databinding.Observable;
+import android.databinding.OnPropertyChangedListener;
+import android.test.ApplicationTestCase;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
new file mode 100644
index 0000000..585571b
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.databinding.multimoduletestapp;
+
+import android.databinding.testlibrary.ObservableInLibrary;
+
+import android.databinding.Observable;
+import android.databinding.OnPropertyChangedListener;
+import android.os.Debug;
+import android.test.AndroidTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.databinding.multimoduletestapp.BR;
+
+public class EventIdsTest extends AndroidTestCase {
+    public void testLibraryObservable() {
+        ObservableInLibrary observableInLibrary = new ObservableInLibrary();
+        EventCounter ec = new EventCounter();
+        observableInLibrary.addOnPropertyChangedListener(ec);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setLibField1("a");
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setLibField2("b");
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setSharedField(3);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    public void testAppObservable() {
+        ObservableInMainApp observableInMainApp = new ObservableInMainApp();
+        EventCounter ec = new EventCounter();
+        observableInMainApp.addOnPropertyChangedListener(ec);
+        ec.assertProperty(BR.appField1, 0);
+        ec.assertProperty(BR.appField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setAppField2(3);
+        ec.assertProperty(BR.appField1, 0);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setAppField1("b");
+        ec.assertProperty(BR.appField1, 1);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setSharedField(5);
+        ec.assertProperty(BR.appField1, 1);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    public void testExtendingObservable() {
+        ObservableExtendingLib observable = new ObservableExtendingLib();
+        EventCounter ec = new EventCounter();
+        observable.addOnPropertyChangedListener(ec);
+
+        ec.assertProperty(BR.childClassField, 0);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setChildClassField("a");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setLibField1("b");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setLibField2("c");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setSharedField(2);
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    private static class EventCounter implements OnPropertyChangedListener {
+        Map<Integer, Integer> mCounter = new HashMap<>();
+
+        @Override
+        public void onPropertyChanged(Observable observable, int propertyId) {
+            mCounter.put(propertyId, get(propertyId) + 1);
+        }
+
+        public int get(int propertyId) {
+            Integer val = mCounter.get(propertyId);
+            return val == null ? 0 : val;
+        }
+
+        private void assertProperty(int propertyId, int value) {
+            assertEquals(get(propertyId), value);
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7e1cb9b
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.databinding.multimoduletestapp" >
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java
new file mode 100644
index 0000000..d90606b
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.databinding.multimoduletestapp;
+
+import android.databinding.multimoduletestapp.databinding.ActivityMainBinding;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+public class MainActivity extends Activity {
+    ActivityMainBinding mBinder;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinder = ActivityMainBinding.inflate(this);
+        setContentView(mBinder.getRoot());
+    }
+
+    public ActivityMainBinding getBinder() {
+        return mBinder;
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+
+        //noinspection SimplifiableIfStatement
+        if (id == R.id.action_settings) {
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java
new file mode 100644
index 0000000..b1ef5fe
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.databinding.multimoduletestapp;
+
+import android.databinding.testlibrary.ObservableInLibrary;
+
+import android.databinding.Bindable;
+import android.databinding.multimoduletestapp.BR;
+
+public class ObservableExtendingLib extends ObservableInLibrary {
+    @Bindable
+    private String mChildClassField;
+
+    public String getChildClassField() {
+        return mChildClassField;
+    }
+
+    public void setChildClassField(String childClassField) {
+        mChildClassField = childClassField;
+        notifyPropertyChanged(BR.childClassField);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java
new file mode 100644
index 0000000..22317c76
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java
@@ -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 android.databinding.multimoduletestapp;
+
+import android.databinding.Bindable;
+
+import android.databinding.BaseObservable;
+import android.databinding.multimoduletestapp.BR;
+
+public class ObservableInMainApp extends BaseObservable {
+    @Bindable
+    private String mAppField1;
+    @Bindable
+    private int mAppField2;
+    @Bindable
+    private int mSharedField;
+
+    public String getAppField1() {
+        return mAppField1;
+    }
+
+    public void setAppField1(String appField1) {
+        mAppField1 = appField1;
+        notifyPropertyChanged(BR.appField1);
+    }
+
+    public int getAppField2() {
+        return mAppField2;
+    }
+
+    public void setAppField2(int appField2) {
+        mAppField2 = appField2;
+        notifyPropertyChanged(BR.appField2);
+    }
+
+    public int getSharedField() {
+        return mSharedField;
+    }
+
+    public void setSharedField(int sharedField) {
+        mSharedField = sharedField;
+        notifyPropertyChanged(BR.sharedField);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4549e81
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
+    <variable name="foo" type="String"/>
+    <TextView android:text='@{foo + " " + foo}' android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml
new file mode 100644
index 0000000..5a34049
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    >
+    <TextView android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml
new file mode 100644
index 0000000..891e70f
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml
@@ -0,0 +1,11 @@
+<?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="userName" type="String"/>
+    <variable name="userLastName" type="String"/>
+    <TextView
+        android:text='@{userName + " " + userLastName}'
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml
new file mode 100644
index 0000000..54c26dd
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml
@@ -0,0 +1,12 @@
+<?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="name" type="String"/>
+    <variable name="lastName" type="String"/>
+    <variable name="anotherVariable" type="String"/>
+    <TextView
+        android:text='@{name + " " + lastName}'
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml
new file mode 100644
index 0000000..36872fe
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml
@@ -0,0 +1,11 @@
+<?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="name" type="String"/>
+    <variable name="lastName" type="String"/>
+    <TextView
+        android:text='@{name + " " + lastName}'
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..4674d4d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..9b24d4f
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..4719591
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c06ae3f
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..20d68aa
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<resources>
+
+    <string name="app_name">Multi Module Test App</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..7dc23c8
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle
new file mode 100644
index 0000000..b9340e5
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    println "loaded config"
+
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties
new file mode 100644
index 0000000..5b24ba3
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..992e276
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle
new file mode 100644
index 0000000..c79cb3d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+include ':app', ':testlibrary'
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
new file mode 100644
index 0000000..4b2c87c
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java
new file mode 100644
index 0000000..49d1b7c
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding.testlibrary;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1f1fb2b
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.databinding.testlibrary" >
+
+  <application
+      android:allowBackup="true"
+      android:icon="@drawable/ic_launcher"
+      android:label="@string/app_name" >
+    <activity
+        android:name=".TestLibraryMainActivity"
+        android:label="@string/app_name" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+  </application>
+
+</manifest>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java
new file mode 100644
index 0000000..c3fa89c
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java
@@ -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 android.databinding.testlibrary;
+
+import android.databinding.Bindable;
+
+import android.databinding.testlibrary.BR;
+
+import android.databinding.BaseObservable;
+
+public class ObservableInLibrary extends BaseObservable {
+
+    @Bindable
+    private String mLibField1;
+
+    @Bindable
+    private String mLibField2;
+
+    @Bindable
+    private int mSharedField;
+
+    public String getLibField1() {
+        return mLibField1;
+    }
+
+    public void setLibField1(String libField1) {
+        mLibField1 = libField1;
+        notifyPropertyChanged(BR.libField1);
+    }
+
+    public String getLibField2() {
+        return mLibField2;
+    }
+
+    public void setLibField2(String libField2) {
+        mLibField2 = libField2;
+        notifyPropertyChanged(BR.libField2);
+    }
+
+    public int getSharedField() {
+        return mSharedField;
+    }
+
+    public void setSharedField(int sharedField) {
+        mSharedField = sharedField;
+        notifyPropertyChanged(BR.sharedField);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java
new file mode 100644
index 0000000..8bf253a
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.testlibrary;
+
+import android.databinding.Bindable;
+
+public class TestLibObject {
+    @Bindable
+    private String mField;
+
+    public String getField() {
+        return mField;
+    }
+
+    public void setField(String field) {
+        this.mField = field;
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java
new file mode 100644
index 0000000..783d51f
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java
@@ -0,0 +1,56 @@
+/*
+ * 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 android.databinding.testlibrary;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.databinding.testlibrary.R;
+import android.databinding.testlibrary.databinding.ActivityTestLibraryMainBinding;
+public class TestLibraryMainActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ActivityTestLibraryMainBinding binder = ActivityTestLibraryMainBinding.inflate(this);
+        setContentView(binder.getRoot());
+
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_test_library_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+
+        //noinspection SimplifiableIfStatement
+        if (id == R.id.action_settings) {
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml
new file mode 100644
index 0000000..d60fa98
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".TestLibraryMainActivity">
+    <variable name="obj1" type="android.databinding.testlibrary.TestLibObject"/>
+
+    <TextView android:text="@{obj1.field}" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml
new file mode 100644
index 0000000..62b2c73
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
+    <variable name="foo" type="String"/>
+    <variable name="foo2" type="int"/>
+    <TextView android:text='@{foo + " " + foo}' android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml
new file mode 100644
index 0000000..989517d3
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".TestLibraryMainActivity">
+    <variable name="obj1" type="android.databinding.testlibrary.TestLibObject"/>
+    <variable name="obj2" type="android.databinding.testlibrary.TestLibObject"/>
+
+    <TextView android:text="@{obj1.field}" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml
new file mode 100644
index 0000000..68d936d
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".TestLibraryMainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..4719591
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c06ae3f
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d930ddb6
--- /dev/null
+++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<resources>
+
+    <string name="app_name">Test Library</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/tools/data-binding/integration-tests/TestApp/.gitignore b/tools/data-binding/integration-tests/TestApp/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/tools/data-binding/integration-tests/TestApp/app/.gitignore b/tools/data-binding/integration-tests/TestApp/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/tools/data-binding/integration-tests/TestApp/app/build.gradle b/tools/data-binding/integration-tests/TestApp/app/build.gradle
new file mode 100644
index 0000000..d9da119
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.application'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "22"
+
+    defaultConfig {
+        applicationId "com.android.databinding.testapp"
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    compile "com.android.databinding:adapters:${config.snapshotVersion}"
+    compile "com.android.support:support-v4:+"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro b/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java
new file mode 100644
index 0000000..763f5a4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.databinding;
+
+/**
+ * This helper is used to toggle DataBinder's package private values to change behavior for testing
+ */
+public class DataBinderTrojan {
+    public static void setBuildSdkInt(int level) {
+        ViewDataBinding.SDK_INT = level;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java
new file mode 100644
index 0000000..287fa24
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.AbsListViewAdapterTestBinding;
+import android.databinding.testapp.vo.AbsListViewBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.os.Debug;
+import android.widget.ListView;
+
+public class AbsListViewBindingAdapterTest
+        extends BindingAdapterTestBase<AbsListViewAdapterTestBinding, AbsListViewBindingObject> {
+
+    ListView mView;
+
+    public AbsListViewBindingAdapterTest() {
+        super(AbsListViewAdapterTestBinding.class, AbsListViewBindingObject.class,
+                R.layout.abs_list_view_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testListSelector() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.getListSelector().getColor(),
+                    ((ColorDrawable) mView.getSelector()).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getListSelector().getColor(),
+                    ((ColorDrawable) mView.getSelector()).getColor());
+        }
+    }
+
+    public void testScrollingCache() throws Throwable {
+        assertEquals(mBindingObject.isScrollingCache(), mView.isScrollingCacheEnabled());
+
+        changeValues();
+
+        assertEquals(mBindingObject.isScrollingCache(), mView.isScrollingCacheEnabled());
+    }
+
+    public void testSmoothScrollbar() throws Throwable {
+        assertEquals(mBindingObject.isSmoothScrollbar(), mView.isSmoothScrollbarEnabled());
+
+        changeValues();
+
+        assertEquals(mBindingObject.isSmoothScrollbar(), mView.isSmoothScrollbarEnabled());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java
new file mode 100644
index 0000000..5424309
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.AbsSeekBarAdapterTestBinding;
+import android.databinding.testapp.vo.AbsSeekBarBindingObject;
+
+import android.os.Build;
+import android.widget.SeekBar;
+
+public class AbsSeekBarBindingAdapterTest
+        extends BindingAdapterTestBase<AbsSeekBarAdapterTestBinding, AbsSeekBarBindingObject> {
+
+    SeekBar mView;
+
+    public AbsSeekBarBindingAdapterTest() {
+        super(AbsSeekBarAdapterTestBinding.class, AbsSeekBarBindingObject.class,
+                R.layout.abs_seek_bar_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testThumbTint() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(mBindingObject.getThumbTint(), mView.getThumbTintList().getDefaultColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getThumbTint(), mView.getThumbTintList().getDefaultColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java
new file mode 100644
index 0000000..d1d45f5
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.AbsSpinnerAdapterTestBinding;
+import android.databinding.testapp.vo.AbsSpinnerBindingObject;
+
+import android.os.Build;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+
+public class AbsSpinnerBindingAdapterTest
+        extends BindingAdapterTestBase<AbsSpinnerAdapterTestBinding, AbsSpinnerBindingObject> {
+
+    Spinner mView;
+
+    public AbsSpinnerBindingAdapterTest() {
+        super(AbsSpinnerAdapterTestBinding.class, AbsSpinnerBindingObject.class,
+                R.layout.abs_spinner_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testEntries() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            validateEntries();
+
+            changeValues();
+
+            validateEntries();
+        }
+    }
+
+    private void validateEntries() {
+        assertEquals(mBindingObject.getEntries().length, mView.getAdapter().getCount());
+        CharSequence[] entries = mBindingObject.getEntries();
+        SpinnerAdapter adapter = mView.getAdapter();
+        for (int i = 0; i < entries.length; i++) {
+            assertEquals(adapter.getItem(i), entries[i]);
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java
new file mode 100644
index 0000000..7c7a9b4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java
@@ -0,0 +1,27 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java
new file mode 100644
index 0000000..a14d05d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.AutoCompleteTextViewAdapterTestBinding;
+import android.databinding.testapp.vo.AutoCompleteTextViewBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.AutoCompleteTextView;
+
+public class AutoCompleteTextViewBindingAdapterTest extends
+        BindingAdapterTestBase<AutoCompleteTextViewAdapterTestBinding,
+                AutoCompleteTextViewBindingObject> {
+
+    AutoCompleteTextView mView;
+
+    public AutoCompleteTextViewBindingAdapterTest() {
+        super(AutoCompleteTextViewAdapterTestBinding.class, AutoCompleteTextViewBindingObject.class,
+                R.layout.auto_complete_text_view_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testCompletionThreshold() throws Throwable {
+        assertEquals(mBindingObject.getCompletionThreshold(), mView.getThreshold());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getCompletionThreshold(), mView.getThreshold());
+    }
+
+    public void testPopupBackground() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            assertEquals(mBindingObject.getPopupBackground(),
+                    ((ColorDrawable) mView.getDropDownBackground()).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getPopupBackground(),
+                    ((ColorDrawable) mView.getDropDownBackground()).getColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java
new file mode 100644
index 0000000..ff85523
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ViewDataBinding;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+
+public class BaseDataBinderTest<T extends ViewDataBinding>
+        extends ActivityInstrumentationTestCase2<TestActivity> {
+    protected Class<T> mBinderClass;
+    private int mOrientation;
+    protected T mBinder;
+
+    public BaseDataBinderTest(final Class<T> binderClass) {
+        this(binderClass, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    public BaseDataBinderTest(final Class<T> binderClass, final int orientation) {
+        super(TestActivity.class);
+        mBinderClass = binderClass;
+        mOrientation = orientation;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getActivity().setRequestedOrientation(mOrientation);
+        createBinder();
+    }
+
+    public boolean isMainThread() {
+        return Looper.myLooper() == Looper.getMainLooper();
+    }
+
+    protected void createBinder() {
+        mBinder = null;
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Method method = null;
+                try {
+                    method = mBinderClass.getMethod("inflate", Context.class);
+                    mBinder = (T) method.invoke(null, getActivity());
+                    getActivity().setContentView(mBinder.getRoot());
+                } catch (Exception e) {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new PrintWriter(sw);
+                    e.printStackTrace(pw);
+                    fail("Error creating binder: " + sw.toString());
+                }
+            }
+        });
+        if (!isMainThread()) {
+            getInstrumentation().waitForIdleSync();
+        }
+        assertNotNull(mBinder);
+    }
+
+    protected void assertMethod(Class<?> klass, String methodName) throws NoSuchMethodException {
+        assertEquals(klass, mBinder.getClass().getDeclaredMethod(methodName).getReturnType());
+    }
+
+    protected void assertField(Class<?> klass, String fieldName) throws NoSuchFieldException {
+        assertEquals(klass, mBinder.getClass().getDeclaredField(fieldName).getType());
+    }
+
+    protected void assertPublicField(Class<?> klass, String fieldName) throws NoSuchFieldException {
+        assertEquals(klass, mBinder.getClass().getField(fieldName).getType());
+    }
+
+    protected void assertNoField(String fieldName) {
+        Exception[] ex = new Exception[1];
+        try {
+            mBinder.getClass().getField(fieldName);
+        } catch (NoSuchFieldException e) {
+            ex[0] = e;
+        }
+        assertNotNull(ex[0]);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java
new file mode 100644
index 0000000..830e79b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ViewDataBinding;
+
+import android.content.pm.ActivityInfo;
+
+public class BaseLandDataBinderTest<T extends ViewDataBinding> extends BaseDataBinderTest<T> {
+
+    public BaseLandDataBinderTest(Class<T> binderClass, int layoutId) {
+        super(binderClass, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java
new file mode 100644
index 0000000..c168ecb
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.BaseObservable;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.Observable;
+import android.databinding.OnPropertyChangedListener;
+
+import java.util.ArrayList;
+
+public class BaseObservableTest extends BaseDataBinderTest<BasicBindingBinding> {
+    private BaseObservable mObservable;
+    private ArrayList<Integer> mNotifications = new ArrayList<>();
+    private OnPropertyChangedListener mListener = new OnPropertyChangedListener() {
+        @Override
+        public void onPropertyChanged(Observable observable, int i) {
+            assertEquals(mObservable, observable);
+            mNotifications.add(i);
+        }
+    };
+
+    public BaseObservableTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mNotifications.clear();
+        mObservable = new BaseObservable();
+    }
+
+    public void testAddListener() {
+        mObservable.notifyChange();
+        assertTrue(mNotifications.isEmpty());
+        mObservable.addOnPropertyChangedListener(mListener);
+        mObservable.notifyChange();
+        assertFalse(mNotifications.isEmpty());
+    }
+
+    public void testRemoveListener() {
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnPropertyChangedListener(mListener);
+
+        mObservable.addOnPropertyChangedListener(mListener);
+        mObservable.notifyChange();
+        mNotifications.clear();
+        mObservable.removeOnPropertyChangedListener(mListener);
+        mObservable.notifyChange();
+        assertTrue(mNotifications.isEmpty());
+
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnPropertyChangedListener(mListener);
+    }
+
+    public void testNotifyChange() {
+        mObservable.addOnPropertyChangedListener(mListener);
+        mObservable.notifyChange();
+        assertEquals(1, mNotifications.size());
+        assertEquals(0, (int) mNotifications.get(0));
+    }
+
+    public void testNotifyPropertyChanged() {
+        final int expectedId = 100;
+        mObservable.addOnPropertyChangedListener(mListener);
+        mObservable.notifyPropertyChanged(expectedId);
+        assertEquals(1, mNotifications.size());
+        assertEquals(expectedId, (int) mNotifications.get(0));
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java
new file mode 100644
index 0000000..024d7f2
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.test.UiThreadTest;
+
+public class BasicBindingTest extends BaseDataBinderTest<BasicBindingBinding> {
+    public BasicBindingTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    @UiThreadTest
+    public void testTextViewContentInInitialization() {
+        assertAB("X", "Y");
+    }
+
+    @UiThreadTest
+    public void testNullValuesInInitialization() {
+        assertAB(null, null);
+    }
+
+    @UiThreadTest
+    public void testSecondIsNullInInitialization() {
+        assertAB(null, "y");
+    }
+
+    @UiThreadTest
+    public void testFirstIsNullInInitialization() {
+        assertAB("x", null);
+    }
+
+    @UiThreadTest
+    public void testTextViewContent() {
+        assertAB("X", "Y");
+    }
+
+    @UiThreadTest
+    public void testNullValues() {
+        assertAB(null, null);
+    }
+
+    @UiThreadTest
+    public void testSecondIsNull() {
+        assertAB(null, "y");
+    }
+
+    @UiThreadTest
+    public void testFirstIsNull() {
+        assertAB("x", null);
+    }
+
+    private void assertAB(String a, String b) {
+        mBinder.setA(a);
+        mBinder.setB(b);
+        rebindAndAssert(a + b);
+    }
+
+    private void rebindAndAssert(String text) {
+        mBinder.executePendingBindings();
+        assertEquals(text, mBinder.textView.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java
new file mode 100644
index 0000000..536a00a
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BasicDependantBindingBinding;
+import android.databinding.testapp.vo.NotBindableVo;
+
+import android.test.UiThreadTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BasicDependantBindingTest extends BaseDataBinderTest<BasicDependantBindingBinding> {
+
+    public BasicDependantBindingTest() {
+        super(BasicDependantBindingBinding.class);
+    }
+
+    public List<NotBindableVo> permutations(String value) {
+        List<NotBindableVo> result = new ArrayList<>();
+        result.add(null);
+        result.add(new NotBindableVo(null));
+        result.add(new NotBindableVo(value));
+        return result;
+    }
+
+    @UiThreadTest
+    public void testAllPermutations() {
+        List<NotBindableVo> obj1s = permutations("a");
+        List<NotBindableVo> obj2s = permutations("b");
+        for (NotBindableVo obj1 : obj1s) {
+            for (NotBindableVo obj2 : obj2s) {
+                createBinder(); //get a new one
+                testWith(obj1, obj2);
+                createBinder();
+                mBinder.executePendingBindings();
+                testWith(obj1, obj2);
+            }
+        }
+    }
+
+    private void testWith(NotBindableVo obj1, NotBindableVo obj2) {
+        mBinder.setObj1(obj1);
+        mBinder.setObj2(obj2);
+        mBinder.executePendingBindings();
+        assertValues(safeGet(obj1), safeGet(obj2),
+                obj1 == null ? "" : obj1.mergeStringFields(obj2),
+                obj2 == null ? "" : obj2.mergeStringFields(obj1),
+                (obj1 == null ? null : obj1.getStringValue())
+                        + (obj2 == null ? null : obj2.getStringValue())
+        );
+    }
+
+    private String safeGet(NotBindableVo vo) {
+        if (vo == null || vo.getStringValue() == null) {
+            return "";
+        }
+        return vo.getStringValue();
+    }
+
+    private void assertValues(String textView1, String textView2,
+            String mergedView1, String mergedView2, String rawMerge) {
+        assertEquals(textView1, mBinder.textView1.getText().toString());
+        assertEquals(textView2, mBinder.textView2.getText().toString());
+        assertEquals(mergedView1, mBinder.mergedTextView1.getText().toString());
+        assertEquals(mergedView2, mBinder.mergedTextView2.getText().toString());
+        assertEquals(rawMerge, mBinder.rawStringMerge.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java
new file mode 100644
index 0000000..73f3811
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BindToFinalBinding;
+import android.databinding.testapp.vo.PublicFinalTestVo;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class BindToFinalFieldTest extends BaseDataBinderTest<BindToFinalBinding>{
+
+    public BindToFinalFieldTest() {
+        super(BindToFinalBinding.class);
+    }
+
+    @UiThreadTest
+    public void testSimple() {
+        final PublicFinalTestVo vo = new PublicFinalTestVo(R.string.app_name);
+        mBinder.setObj(vo);
+        mBinder.executePendingBindings();
+        final TextView textView = (TextView) mBinder.getRoot().findViewById(R.id.text_view);
+        assertEquals(getActivity().getResources().getString(R.string.app_name), textView.getText().toString());
+    }
+
+
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java
new file mode 100644
index 0000000..72c2efb
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BindToFinalObservableBinding;
+import android.databinding.testapp.vo.PublicFinalWithObservableTestVo;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class BindToFinalObservableFieldTest extends BaseDataBinderTest<BindToFinalObservableBinding>{
+
+    public BindToFinalObservableFieldTest() {
+        super(BindToFinalObservableBinding.class);
+    }
+
+    @UiThreadTest
+    public void testSimple() {
+        final PublicFinalWithObservableTestVo vo = new PublicFinalWithObservableTestVo(R.string.app_name);
+        mBinder.setObj(vo);
+        mBinder.executePendingBindings();
+        final TextView textView = (TextView) mBinder.getRoot().findViewById(R.id.text_view);
+        assertEquals(getActivity().getResources().getString(R.string.app_name), textView.getText().toString());
+        vo.myFinalVo.setVal(R.string.rain);
+        mBinder.executePendingBindings();
+        assertEquals("The field should be observed and its notify event should've invalidated"
+                        + " binder flags.", getActivity().getResources().getString(R.string.rain),
+                textView.getText().toString());
+    }
+
+
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java
new file mode 100644
index 0000000..730ebb2
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ViewDataBinding;
+import android.databinding.testapp.vo.BindingAdapterBindingObject;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class BindingAdapterTestBase<T extends ViewDataBinding, V extends BindingAdapterBindingObject>
+        extends BaseDataBinderTest<T> {
+    private Class<V> mBindingObjectClass;
+
+    protected V mBindingObject;
+
+    private Method mSetMethod;
+
+    public BindingAdapterTestBase(Class<T> binderClass, Class<V> observableClass, int layoutId) {
+        super(binderClass);
+        mBindingObjectClass = observableClass;
+        try {
+            mSetMethod = binderClass.getDeclaredMethod("setObj", observableClass);
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mBindingObject = mBindingObjectClass.newInstance();
+                        mSetMethod.invoke(mBinder, mBindingObject);
+                        mBinder.executePendingBindings();
+                    } catch (IllegalAccessException e) {
+                        throw new RuntimeException(e);
+                    } catch (InvocationTargetException e) {
+                        throw new RuntimeException(e);
+                    } catch (InstantiationException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    protected void changeValues() throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mBindingObject.changeValues();
+                mBinder.executePendingBindings();
+            }
+        });
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java
new file mode 100644
index 0000000..20d90ed
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BracketTestBinding;
+
+import android.test.UiThreadTest;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
+
+public class BracketTest extends BaseDataBinderTest<BracketTestBinding> {
+    private String[] mArray = {
+            "Hello World"
+    };
+
+    private SparseArray<String> mSparseArray = new SparseArray<>();
+    private SparseIntArray mSparseIntArray = new SparseIntArray();
+    private SparseBooleanArray mSparseBooleanArray = new SparseBooleanArray();
+    private SparseLongArray mSparseLongArray = new SparseLongArray();
+    private LongSparseArray<String> mLongSparseArray = new LongSparseArray<>();
+
+    public BracketTest() {
+        super(BracketTestBinding.class);
+        mSparseArray.put(0, "Hello");
+        mLongSparseArray.put(0, "World");
+        mSparseIntArray.put(0, 100);
+        mSparseBooleanArray.put(0, true);
+        mSparseLongArray.put(0, 5);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mBinder.setArray(mArray);
+                    mBinder.setSparseArray(mSparseArray);
+                    mBinder.setSparseIntArray(mSparseIntArray);
+                    mBinder.setSparseBooleanArray(mSparseBooleanArray);
+                    mBinder.setSparseLongArray(mSparseLongArray);
+                    mBinder.setLongSparseArray(mLongSparseArray);
+
+                    mBinder.executePendingBindings();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testBrackets() {
+        assertEquals("Hello World", mBinder.arrayText.getText().toString());
+        assertEquals("Hello", mBinder.sparseArrayText.getText().toString());
+        assertEquals("World", mBinder.longSparseArrayText.getText().toString());
+        assertEquals("100", mBinder.sparseIntArrayText.getText().toString());
+        assertEquals("true", mBinder.sparseBooleanArrayText.getText().toString());
+        assertEquals("5", mBinder.sparseLongArrayText.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java
new file mode 100644
index 0000000..2fce2d4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java
@@ -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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.CastTestBinding;
+
+import android.support.v4.util.ArrayMap;
+import android.test.UiThreadTest;
+
+import java.util.ArrayList;
+
+public class CastTest extends BaseDataBinderTest<CastTestBinding> {
+    ArrayList<String> mValues = new ArrayList<>();
+    ArrayMap<String, String> mMap = new ArrayMap<>();
+
+    public CastTest() {
+        super(CastTestBinding.class);
+    }
+
+    @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.executePendingBindings();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testCast() throws Throwable {
+        assertEquals("hello", mBinder.textView0.getText().toString());
+        assertEquals("world", mBinder.textView1.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java
new file mode 100644
index 0000000..68464e6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.CheckedTextViewAdapterTestBinding;
+import android.databinding.testapp.vo.CheckedTextViewBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.CheckedTextView;
+
+public class CheckedTextViewBindingAdapterTest extends
+        BindingAdapterTestBase<CheckedTextViewAdapterTestBinding, CheckedTextViewBindingObject> {
+
+    CheckedTextView mView;
+
+    public CheckedTextViewBindingAdapterTest() {
+        super(CheckedTextViewAdapterTestBinding.class, CheckedTextViewBindingObject.class,
+                R.layout.checked_text_view_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testView() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(mBindingObject.getCheckMark().getColor(),
+                    ((ColorDrawable) mView.getCheckMarkDrawable()).getColor());
+            assertEquals(mBindingObject.getCheckMarkTint(),
+                    mView.getCheckMarkTintList().getDefaultColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getCheckMark().getColor(),
+                    ((ColorDrawable) mView.getCheckMarkDrawable()).getColor());
+            assertEquals(mBindingObject.getCheckMarkTint(),
+                    mView.getCheckMarkTintList().getDefaultColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java
new file mode 100644
index 0000000..7845923
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.CompoundButtonAdapterTestBinding;
+import android.databinding.testapp.vo.CompoundButtonBindingObject;
+
+import android.widget.CompoundButton;
+
+public class CompoundButtonBindingAdapterTest extends
+        BindingAdapterTestBase<CompoundButtonAdapterTestBinding, CompoundButtonBindingObject> {
+
+    CompoundButton mView;
+
+    public CompoundButtonBindingAdapterTest() {
+        super(CompoundButtonAdapterTestBinding.class, CompoundButtonBindingObject.class,
+                R.layout.compound_button_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testCompoundButton() throws Throwable {
+        assertEquals(mBindingObject.getButtonTint(), mView.getButtonTintList().getDefaultColor());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getButtonTint(), mView.getButtonTintList().getDefaultColor());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java
new file mode 100644
index 0000000..61839db
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java
@@ -0,0 +1,34 @@
+package android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ConditionalBindingBinding;
+import android.databinding.testapp.vo.NotBindableVo;
+
+import android.test.UiThreadTest;
+
+public class ConditionalBindingTest extends BaseDataBinderTest<ConditionalBindingBinding>{
+
+    public ConditionalBindingTest() {
+        super(ConditionalBindingBinding.class);
+    }
+
+    @UiThreadTest
+    public void test1() {
+        testCorrectness(true, true);
+    }
+
+    private void testCorrectness(boolean cond1, boolean cond2) {
+        NotBindableVo o1 = new NotBindableVo("a");
+        NotBindableVo o2 = new NotBindableVo("b");
+        NotBindableVo o3 = new NotBindableVo("c");
+        mBinder.setObj1(o1);
+        mBinder.setObj2(o2);
+        mBinder.setObj3(o3);
+        mBinder.setCond1(cond1);
+        mBinder.setCond2(cond2);
+        mBinder.executePendingBindings();
+        final String text = mBinder.textView.getText().toString();
+        assertEquals(cond1 && cond2, "a".equals(text));
+        assertEquals(cond1 && !cond2, "b".equals(text));
+        assertEquals(!cond1, "c".equals(text));
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java
new file mode 100644
index 0000000..ca826fa
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.FindMethodTestBinding;
+import android.databinding.testapp.vo.FindMethodBindingObject;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class FindMethodTest
+        extends BindingAdapterTestBase<FindMethodTestBinding, FindMethodBindingObject> {
+
+    public FindMethodTest() {
+        super(FindMethodTestBinding.class, FindMethodBindingObject.class, R.layout.find_method_test);
+    }
+
+    public void testNoArg() throws Throwable {
+        TextView textView = mBinder.textView6;
+        assertEquals("no arg", textView.getText().toString());
+    }
+
+    public void testIntArg() throws Throwable {
+        TextView textView = mBinder.textView0;
+        assertEquals("1", textView.getText().toString());
+    }
+
+    public void testFloatArg() throws Throwable {
+        TextView textView = mBinder.textView1;
+        assertEquals("1.25", textView.getText().toString());
+    }
+
+    public void testStringArg() throws Throwable {
+        TextView textView = mBinder.textView2;
+        assertEquals("hello", textView.getText().toString());
+    }
+
+    public void testBoxedArg() throws Throwable {
+        TextView textView = mBinder.textView3;
+        assertEquals("1", textView.getText().toString());
+    }
+
+    public void testInheritedMethod() throws Throwable {
+        TextView textView = mBinder.textView4;
+        assertEquals("base", textView.getText().toString());
+    }
+
+    public void testInheritedMethodInt() throws Throwable {
+        TextView textView = mBinder.textView5;
+        assertEquals("base 2", textView.getText().toString());
+    }
+
+    public void testStaticMethod() throws Throwable {
+        TextView textView = mBinder.textView7;
+        assertEquals("world", textView.getText().toString());
+    }
+
+    public void testStaticField() throws Throwable {
+        TextView textView = mBinder.textView8;
+        assertEquals("hello world", textView.getText().toString());
+    }
+
+    public void testImportStaticMethod() throws Throwable {
+        TextView textView = mBinder.textView9;
+        assertEquals("world", textView.getText().toString());
+    }
+
+    public void testImportStaticField() throws Throwable {
+        TextView textView = mBinder.textView10;
+        assertEquals("hello world", textView.getText().toString());
+    }
+
+    public void testAliasStaticMethod() throws Throwable {
+        TextView textView = mBinder.textView11;
+        assertEquals("world", textView.getText().toString());
+    }
+
+    public void testAliasStaticField() throws Throwable {
+        TextView textView = mBinder.textView12;
+        assertEquals("hello world", textView.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testImports() throws Throwable {
+        mBinder.setObj2(new FindMethodBindingObject.Bar<String>());
+        mBinder.executePendingBindings();
+        TextView textView = mBinder.textView15;
+        assertEquals("hello", textView.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testConfusingMethods() throws Throwable {
+        assertEquals("1", mBinder.textView16.getText().toString());
+        assertEquals("1", mBinder.textView17.getText().toString());
+        assertEquals("hello", mBinder.textView18.getText().toString());
+        assertEquals("yay", mBinder.textView19.getText().toString());
+        assertEquals("hello", mBinder.textView20.getText().toString());
+        assertEquals("hello", mBinder.textView21.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java
new file mode 100644
index 0000000..1f2f835
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.FrameLayoutAdapterTestBinding;
+import android.databinding.testapp.vo.FrameLayoutBindingObject;
+
+import android.os.Build;
+import android.widget.FrameLayout;
+
+public class FrameLayoutBindingAdapterTest
+        extends BindingAdapterTestBase<FrameLayoutAdapterTestBinding, FrameLayoutBindingObject> {
+
+    FrameLayout mView;
+
+    public FrameLayoutBindingAdapterTest() {
+        super(FrameLayoutAdapterTestBinding.class, FrameLayoutBindingObject.class,
+                R.layout.frame_layout_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testTint() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(mBindingObject.getForegroundTint(),
+                    mView.getForegroundTintList().getDefaultColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getForegroundTint(),
+                    mView.getForegroundTintList().getDefaultColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java
new file mode 100644
index 0000000..e4dcdfe
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ImageViewAdapterTestBinding;
+import android.databinding.testapp.vo.ImageViewBindingObject;
+
+import android.widget.ImageView;
+
+public class ImageViewBindingAdapterTest
+        extends BindingAdapterTestBase<ImageViewAdapterTestBinding, ImageViewBindingObject> {
+
+    ImageView mView;
+
+    public ImageViewBindingAdapterTest() {
+        super(ImageViewAdapterTestBinding.class, ImageViewBindingObject.class,
+                R.layout.image_view_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testImageView() throws Throwable {
+        assertEquals(mBindingObject.getSrc(), mView.getDrawable());
+        assertEquals(mBindingObject.getTint(), mView.getImageTintList().getDefaultColor());
+        assertEquals(mBindingObject.getTintMode(), mView.getImageTintMode());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getSrc(), mView.getDrawable());
+        assertEquals(mBindingObject.getTint(), mView.getImageTintList().getDefaultColor());
+        assertEquals(mBindingObject.getTintMode(), mView.getImageTintMode());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java
new file mode 100644
index 0000000..48d6689
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.LayoutWithIncludeBinding;
+import android.databinding.testapp.vo.NotBindableVo;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class IncludeTagTest extends BaseDataBinderTest<LayoutWithIncludeBinding> {
+
+    public IncludeTagTest() {
+        super(LayoutWithIncludeBinding.class);
+    }
+
+    @UiThreadTest
+    public void testIncludeTag() {
+        NotBindableVo vo = new NotBindableVo(3, "a");
+        mBinder.setOuterObject(vo);
+        mBinder.executePendingBindings();
+        final TextView outerText = (TextView) mBinder.getRoot().findViewById(R.id.outerTextView);
+        assertEquals("a", outerText.getText());
+        final TextView innerText = (TextView) mBinder.getRoot().findViewById(R.id.innerTextView);
+        assertEquals("modified 3a", innerText.getText().toString());
+
+        vo.setIntValue(5);
+        vo.setStringValue("b");
+        mBinder.invalidateAll();
+        mBinder.executePendingBindings();
+        assertEquals("b", outerText.getText());
+        assertEquals("modified 5b", innerText.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java
new file mode 100644
index 0000000..68cf06b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.InnerCannotReadDependencyBinding;
+import android.databinding.testapp.vo.BasicObject;
+import android.os.Debug;
+import android.test.UiThreadTest;
+
+import org.junit.Test;
+
+public class InnerCannotReadDependencyTest extends
+        BaseDataBinderTest<InnerCannotReadDependencyBinding> {
+
+    public InnerCannotReadDependencyTest() {
+        super(InnerCannotReadDependencyBinding.class);
+    }
+
+    @UiThreadTest
+    public void testBinding() {
+        BasicObject object = new BasicObject();
+        object.setField1("a");
+        mBinder.setObj(object);
+        mBinder.executePendingBindings();
+        assertEquals("a ", mBinder.textView.getText().toString());
+        object.setField1(null);
+        mBinder.executePendingBindings();
+        assertEquals("null ", mBinder.textView.getText().toString());
+        object.setField2("b");
+        mBinder.executePendingBindings();
+        assertEquals("null b", mBinder.textView.getText().toString());
+        object.setField1("c");
+        mBinder.executePendingBindings();
+        assertEquals("c b", mBinder.textView.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java
new file mode 100644
index 0000000..0455a46
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.LeakTestBinding;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+import java.lang.ref.WeakReference;
+
+public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> {
+    WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null);
+
+    public LeakTest() {
+        super(TestActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        try {
+            getActivity().runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        LeakTestBinding binding = LeakTestBinding.inflate(getActivity());
+                        getActivity().setContentView(binding.getRoot());
+                        mWeakReference = new WeakReference<LeakTestBinding>(binding);
+                        binding.setName("hello world");
+                        binding.executePendingBindings();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        throw e;
+                    }
+                }
+            });
+            getInstrumentation().waitForIdleSync();
+        } catch (Throwable t) {
+            throw new Exception(t);
+        }
+    }
+
+    public void testBindingLeak() throws Throwable {
+        assertNotNull(mWeakReference.get());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().setContentView(new FrameLayout(getActivity()));
+            }
+        });
+        WeakReference<Object> canary = new WeakReference<Object>(new Object());
+        while (canary.get() != null) {
+            byte[] b = new byte[1024 * 1024];
+            System.gc();
+        }
+        assertNull(mWeakReference.get());
+    }
+
+    // Test to ensure that when the View is detached that it doesn't rebind
+    // the dirty Views. The rebind should happen only after the root view is
+    // reattached.
+    public void testNoChangeWhenDetached() throws Throwable {
+        final LeakTestBinding binding = mWeakReference.get();
+        final AnimationWatcher watcher = new AnimationWatcher();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().setContentView(new FrameLayout(getActivity()));
+                binding.setName("goodbye world");
+                binding.getRoot().postOnAnimation(watcher);
+            }
+        });
+
+        watcher.waitForAnimationThread();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals("hello world", binding.textView.getText().toString());
+                getActivity().setContentView(binding.getRoot());
+                binding.getRoot().postOnAnimation(watcher);
+            }
+        });
+
+        watcher.waitForAnimationThread();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals("goodbye world", binding.textView.getText().toString());
+            }
+        });
+    }
+
+    private static class AnimationWatcher implements Runnable {
+        private boolean mWaiting = true;
+
+        public void waitForAnimationThread() throws InterruptedException {
+            synchronized (this) {
+                while (mWaiting) {
+                    this.wait();
+                }
+                mWaiting = true;
+            }
+        }
+
+
+        @Override
+        public void run() {
+            synchronized (this) {
+                mWaiting = false;
+                this.notifyAll();
+            }
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java
new file mode 100644
index 0000000..c601564
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.LinearLayoutAdapterTestBinding;
+import android.databinding.testapp.vo.LinearLayoutBindingObject;
+
+import android.os.Build;
+import android.widget.LinearLayout;
+
+public class LinearLayoutBindingAdapterTest
+        extends BindingAdapterTestBase<LinearLayoutAdapterTestBinding, LinearLayoutBindingObject> {
+
+    LinearLayout mView;
+
+    public LinearLayoutBindingAdapterTest() {
+        super(LinearLayoutAdapterTestBinding.class, LinearLayoutBindingObject.class,
+                R.layout.linear_layout_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testMeasureWithLargestChild() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.isMeasureWithLargestChild(),
+                    mView.isMeasureWithLargestChildEnabled());
+
+            changeValues();
+
+            assertEquals(mBindingObject.isMeasureWithLargestChild(),
+                    mView.isMeasureWithLargestChildEnabled());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java
new file mode 100644
index 0000000..365eab9
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ListChangeRegistry;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.OnListChangedListener;
+
+public class ListChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> {
+
+    private ListChangeRegistry mListChangeRegistry;
+
+    private int mCallCount;
+
+    public ListChangeRegistryTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mListChangeRegistry = new ListChangeRegistry();
+        mCallCount = 0;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mListChangeRegistry = null;
+    }
+
+    public void testNotifyChangedAll() {
+        OnListChangedListener listChangedListener = new OnListChangedListener() {
+            @Override
+            public void onChanged() {
+                mCallCount++;
+            }
+
+            @Override
+            public void onItemRangeChanged(int start, int count) {
+                fail("onItemRangeChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeInserted(int start, int count) {
+                fail("onItemRangeInserted should not be called");
+            }
+
+            @Override
+            public void onItemRangeMoved(int from, int to, int count) {
+                fail("onItemRangeMoved should not be called");
+            }
+
+            @Override
+            public void onItemRangeRemoved(int start, int count) {
+                fail("onItemRangeRemoved should not be called");
+            }
+        };
+
+        mListChangeRegistry.add(listChangedListener);
+        assertEquals(0, mCallCount);
+        mListChangeRegistry.notifyChanged(null);
+        assertEquals(1, mCallCount);
+    }
+
+    public void testNotifyChanged() {
+        final int expectedStart = 10;
+        final int expectedCount = 3;
+
+        OnListChangedListener listChangedListener = new OnListChangedListener() {
+            @Override
+            public void onChanged() {
+                fail("onChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeChanged(int start, int count) {
+                assertEquals(expectedStart, start);
+                assertEquals(expectedCount, count);
+                mCallCount++;
+            }
+
+            @Override
+            public void onItemRangeInserted(int start, int count) {
+                fail("onItemRangeInserted should not be called");
+            }
+
+            @Override
+            public void onItemRangeMoved(int from, int to, int count) {
+                fail("onItemRangeMoved should not be called");
+            }
+
+            @Override
+            public void onItemRangeRemoved(int start, int count) {
+                fail("onItemRangeRemoved should not be called");
+            }
+        };
+
+        mListChangeRegistry.add(listChangedListener);
+        assertEquals(0, mCallCount);
+        mListChangeRegistry.notifyChanged(null, expectedStart, expectedCount);
+        assertEquals(1, mCallCount);
+    }
+
+    public void testNotifyInserted() {
+        final int expectedStart = 10;
+        final int expectedCount = 3;
+
+        OnListChangedListener listChangedListener = new OnListChangedListener() {
+            @Override
+            public void onChanged() {
+                fail("onChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeChanged(int start, int count) {
+                fail("onItemRangeChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeInserted(int start, int count) {
+                assertEquals(expectedStart, start);
+                assertEquals(expectedCount, count);
+                mCallCount++;
+            }
+
+            @Override
+            public void onItemRangeMoved(int from, int to, int count) {
+                fail("onItemRangeMoved should not be called");
+            }
+
+            @Override
+            public void onItemRangeRemoved(int start, int count) {
+                fail("onItemRangeRemoved should not be called");
+            }
+        };
+
+        mListChangeRegistry.add(listChangedListener);
+        assertEquals(0, mCallCount);
+        mListChangeRegistry.notifyInserted(null, expectedStart, expectedCount);
+        assertEquals(1, mCallCount);
+    }
+
+    public void testNotifyMoved() {
+        final int expectedFrom = 10;
+        final int expectedTo = 100;
+        final int expectedCount = 3;
+
+        OnListChangedListener listChangedListener = new OnListChangedListener() {
+            @Override
+            public void onChanged() {
+                fail("onChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeChanged(int start, int count) {
+                fail("onItemRangeChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeInserted(int start, int count) {
+                fail("onItemRangeInserted should not be called");
+            }
+
+            @Override
+            public void onItemRangeMoved(int from, int to, int count) {
+                assertEquals(expectedFrom, from);
+                assertEquals(expectedTo, to);
+                assertEquals(expectedCount, count);
+                mCallCount++;
+            }
+
+            @Override
+            public void onItemRangeRemoved(int start, int count) {
+                fail("onItemRangeRemoved should not be called");
+            }
+        };
+
+        mListChangeRegistry.add(listChangedListener);
+        assertEquals(0, mCallCount);
+        mListChangeRegistry.notifyMoved(null, expectedFrom, expectedTo, expectedCount);
+        assertEquals(1, mCallCount);
+    }
+
+    public void testNotifyRemoved() {
+        final int expectedStart = 10;
+        final int expectedCount = 3;
+
+        OnListChangedListener listChangedListener = new OnListChangedListener() {
+            @Override
+            public void onChanged() {
+                fail("onChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeChanged(int start, int count) {
+                fail("onItemRangeChanged should not be called");
+            }
+
+            @Override
+            public void onItemRangeInserted(int start, int count) {
+                fail("onItemRangeInserted should not be called");
+            }
+
+            @Override
+            public void onItemRangeMoved(int from, int to, int count) {
+                fail("onItemRangeMoved should not be called");
+            }
+
+            @Override
+            public void onItemRangeRemoved(int start, int count) {
+                assertEquals(expectedStart, start);
+                assertEquals(expectedCount, count);
+                mCallCount++;
+            }
+        };
+
+        mListChangeRegistry.add(listChangedListener);
+        assertEquals(0, mCallCount);
+        mListChangeRegistry.notifyRemoved(null, expectedStart, expectedCount);
+        assertEquals(1, mCallCount);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java
new file mode 100644
index 0000000..fa5b536
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.MapChangeRegistry;
+import android.databinding.ObservableArrayMap;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.ObservableMap;
+import android.databinding.OnMapChangedListener;
+
+public class MapChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> {
+
+    private int notificationCount = 0;
+
+    public MapChangeRegistryTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    public void testNotifyAllChanged() {
+        MapChangeRegistry mapChangeRegistry = new MapChangeRegistry();
+
+        final ObservableMap<String, Integer> observableObj = new ObservableArrayMap<>();
+
+        final String expectedKey = "key";
+        OnMapChangedListener listener = new OnMapChangedListener<ObservableMap<String, Integer>, String>() {
+            @Override
+            public void onMapChanged(ObservableMap sender, String key) {
+                notificationCount++;
+                assertEquals(observableObj, sender);
+                assertEquals(key, expectedKey);
+            }
+        };
+        mapChangeRegistry.add(listener);
+
+        assertEquals(0, notificationCount);
+        mapChangeRegistry.notifyChange(observableObj, expectedKey);
+        assertEquals(1, notificationCount);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java
new file mode 100644
index 0000000..2c6fdf6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.DataBinderTrojan;
+import android.databinding.testapp.databinding.NewApiLayoutBinding;
+
+import android.os.Build;
+import android.test.UiThreadTest;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class NewApiTest extends BaseDataBinderTest<NewApiLayoutBinding> {
+    public NewApiTest() {
+        super(NewApiLayoutBinding.class);
+    }
+
+    @UiThreadTest
+    public void testSetElevation() {
+        mBinder.setElevation(3);
+        mBinder.setName("foo");
+        mBinder.setChildren(new ArrayList<View>());
+        mBinder.executePendingBindings();
+        assertEquals("foo", mBinder.textView.getText().toString());
+        assertEquals(3f, mBinder.textView.getElevation());
+    }
+
+    @UiThreadTest
+    public void testSetElevationOlderAPI() {
+        DataBinderTrojan.setBuildSdkInt(1);
+        try {
+            TextView textView = mBinder.textView;
+            float originalElevation = textView.getElevation();
+            mBinder.setElevation(3);
+            mBinder.setName("foo2");
+            mBinder.executePendingBindings();
+            assertEquals("foo2", textView.getText().toString());
+            assertEquals(originalElevation, textView.getElevation());
+        } finally {
+            DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT);
+        }
+    }
+
+    @UiThreadTest
+    public void testGeneric() {
+        ArrayList<View> views = new ArrayList<>();
+        mBinder.setChildren(views);
+        mBinder.executePendingBindings();
+        assertEquals(1, views.size());
+        assertSame(mBinder.textView, views.get(0));
+    }
+
+    @UiThreadTest
+    public void testGenericOlderApi() {
+        DataBinderTrojan.setBuildSdkInt(1);
+        try {
+            ArrayList<View> views = new ArrayList<>();
+            mBinder.setChildren(views);
+            mBinder.executePendingBindings();
+            // we should not call the api on older platforms.
+            assertEquals(0, views.size());
+        } finally {
+            DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT);
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java
new file mode 100644
index 0000000..bf58709
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.NoIdTestBinding;
+
+import android.test.UiThreadTest;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class NoIdTest extends BaseDataBinderTest<NoIdTestBinding> {
+    public NoIdTest() {
+        super(NoIdTestBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mBinder.setName("hello");
+                    mBinder.setOrientation(LinearLayout.VERTICAL);
+                    mBinder.executePendingBindings();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testOnRoot() {
+        LinearLayout linearLayout = (LinearLayout) mBinder.getRoot();
+        assertEquals(LinearLayout.VERTICAL, linearLayout.getOrientation());
+        mBinder.setOrientation(LinearLayout.HORIZONTAL);
+        mBinder.executePendingBindings();
+        assertEquals(LinearLayout.HORIZONTAL, linearLayout.getOrientation());
+    }
+
+    @UiThreadTest
+    public void testNormal() {
+        LinearLayout linearLayout = (LinearLayout) mBinder.getRoot();
+        TextView view = (TextView) linearLayout.getChildAt(0);
+        assertEquals("hello world", view.getTag());
+        assertEquals("hello", view.getText().toString());
+        mBinder.setName("world");
+        mBinder.executePendingBindings();
+        assertEquals("world", view.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testNoTag() {
+        LinearLayout linearLayout = (LinearLayout) mBinder.getRoot();
+        TextView view = (TextView) linearLayout.getChildAt(1);
+        assertNull(view.getTag());
+    }
+
+    @UiThreadTest
+    public void testResourceTag() {
+        LinearLayout linearLayout = (LinearLayout) mBinder.getRoot();
+        TextView view = (TextView) linearLayout.getChildAt(2);
+        String expectedValue = view.getResources().getString(R.string.app_name);
+        assertEquals(expectedValue, view.getTag());
+    }
+
+    @UiThreadTest
+    public void testAndroidResourceTag() {
+        LinearLayout linearLayout = (LinearLayout) mBinder.getRoot();
+        TextView view = (TextView) linearLayout.getChildAt(3);
+        String expectedValue = view.getResources().getString(android.R.string.ok);
+        assertEquals(expectedValue, view.getTag());
+    }
+
+    @UiThreadTest
+    public void testIdOnly() {
+        assertEquals("hello", mBinder.textView.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java
new file mode 100644
index 0000000..739ea7a
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ObservableArrayList;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.ObservableList;
+import android.databinding.OnListChangedListener;
+
+import java.util.ArrayList;
+
+public class ObservableArrayListTest extends BaseDataBinderTest<BasicBindingBinding> {
+
+    private static final int ALL = 0;
+
+    private static final int CHANGE = 1;
+
+    private static final int INSERT = 2;
+
+    private static final int MOVE = 3;
+
+    private static final int REMOVE = 4;
+
+    private ObservableList<String> mObservable;
+
+    private ArrayList<ListChange> mNotifications = new ArrayList<>();
+
+    private OnListChangedListener mListener = new OnListChangedListener() {
+        @Override
+        public void onChanged() {
+            mNotifications.add(new ListChange(ALL, 0, 0));
+        }
+
+        @Override
+        public void onItemRangeChanged(int start, int count) {
+            mNotifications.add(new ListChange(CHANGE, start, count));
+        }
+
+        @Override
+        public void onItemRangeInserted(int start, int count) {
+            mNotifications.add(new ListChange(INSERT, start, count));
+        }
+
+        @Override
+        public void onItemRangeMoved(int from, int to, int count) {
+            mNotifications.add(new ListChange(MOVE, from, to, count));
+        }
+
+        @Override
+        public void onItemRangeRemoved(int start, int count) {
+            mNotifications.add(new ListChange(REMOVE, start, count));
+        }
+    };
+
+    private static class ListChange {
+
+        public ListChange(int change, int start, int count) {
+            this.start = start;
+            this.count = count;
+            this.from = 0;
+            this.to = 0;
+            this.change = change;
+        }
+
+        public ListChange(int change, int from, int to, int count) {
+            this.from = from;
+            this.to = to;
+            this.count = count;
+            this.start = 0;
+            this.change = change;
+        }
+
+        public final int start;
+
+        public final int count;
+
+        public final int from;
+
+        public final int to;
+
+        public final int change;
+    }
+
+    public ObservableArrayListTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mNotifications.clear();
+        mObservable = new ObservableArrayList<>();
+    }
+
+    public void testAddListener() {
+        mObservable.add("Hello");
+        assertTrue(mNotifications.isEmpty());
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.add("World");
+        assertFalse(mNotifications.isEmpty());
+    }
+
+    public void testRemoveListener() {
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnListChangedListener(mListener);
+
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.add("Hello");
+        mNotifications.clear();
+        mObservable.removeOnListChangedListener(mListener);
+        mObservable.add("World");
+        assertTrue(mNotifications.isEmpty());
+
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnListChangedListener(mListener);
+    }
+
+    public void testAdd() {
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.add("Hello");
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(INSERT, change.change);
+        assertEquals(0, change.start);
+        assertEquals(1, change.count);
+        assertEquals("Hello", mObservable.get(0));
+    }
+
+    public void testInsert() {
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.add("Hello");
+        mObservable.add(0, "World");
+        mObservable.add(1, "Dang");
+        mObservable.add(3, "End");
+        assertEquals(4, mObservable.size());
+        assertEquals("World", mObservable.get(0));
+        assertEquals("Dang", mObservable.get(1));
+        assertEquals("Hello", mObservable.get(2));
+        assertEquals("End", mObservable.get(3));
+        assertEquals(4, mNotifications.size());
+        ListChange change = mNotifications.get(1);
+        assertEquals(INSERT, change.change);
+        assertEquals(0, change.start);
+        assertEquals(1, change.count);
+    }
+
+    public void testAddAll() {
+        ArrayList<String> toAdd = new ArrayList<>();
+        toAdd.add("Hello");
+        toAdd.add("World");
+        mObservable.add("First");
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.addAll(toAdd);
+        assertEquals(3, mObservable.size());
+        assertEquals("Hello", mObservable.get(1));
+        assertEquals("World", mObservable.get(2));
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(INSERT, change.change);
+        assertEquals(1, change.start);
+        assertEquals(2, change.count);
+    }
+
+    public void testInsertAll() {
+        ArrayList<String> toAdd = new ArrayList<>();
+        toAdd.add("Hello");
+        toAdd.add("World");
+        mObservable.add("First");
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.addAll(0, toAdd);
+        assertEquals(3, mObservable.size());
+        assertEquals("Hello", mObservable.get(0));
+        assertEquals("World", mObservable.get(1));
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(INSERT, change.change);
+        assertEquals(0, change.start);
+        assertEquals(2, change.count);
+    }
+
+    public void testClear() {
+        mObservable.add("Hello");
+        mObservable.add("World");
+        mObservable.addOnListChangedListener(mListener);
+        mObservable.clear();
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(REMOVE, change.change);
+        assertEquals(0, change.start);
+        assertEquals(2, change.count);
+
+        mObservable.clear();
+        // No notification when nothing is cleared.
+        assertEquals(1, mNotifications.size());
+    }
+
+    public void testRemoveIndex() {
+        mObservable.add("Hello");
+        mObservable.add("World");
+        mObservable.addOnListChangedListener(mListener);
+        assertEquals("Hello", mObservable.remove(0));
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(REMOVE, change.change);
+        assertEquals(0, change.start);
+        assertEquals(1, change.count);
+    }
+
+    public void testRemoveObject() {
+        mObservable.add("Hello");
+        mObservable.add("World");
+        mObservable.addOnListChangedListener(mListener);
+        assertTrue(mObservable.remove("Hello"));
+        assertEquals(1, mNotifications.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(REMOVE, change.change);
+        assertEquals(0, change.start);
+        assertEquals(1, change.count);
+
+        assertFalse(mObservable.remove("Hello"));
+        // nothing removed, don't notify
+        assertEquals(1, mNotifications.size());
+    }
+
+    public void testSet() {
+        mObservable.add("Hello");
+        mObservable.add("World");
+        mObservable.addOnListChangedListener(mListener);
+        assertEquals("Hello", mObservable.set(0, "Goodbye"));
+        assertEquals("Goodbye", mObservable.get(0));
+        assertEquals(2, mObservable.size());
+        ListChange change = mNotifications.get(0);
+        assertEquals(CHANGE, change.change);
+        assertEquals(0, change.start);
+        assertEquals(1, change.count);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java
new file mode 100644
index 0000000..fc5f689
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.ObservableArrayMap;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.ObservableMap;
+import android.databinding.OnMapChangedListener;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.util.SimpleArrayMap;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+public class ObservableArrayMapTest extends BaseDataBinderTest<BasicBindingBinding> {
+
+    private ObservableArrayMap<String, String> mObservable;
+
+    private ArrayList<String> mNotifications = new ArrayList<>();
+
+    private OnMapChangedListener mListener = new OnMapChangedListener() {
+        @Override
+        public void onMapChanged(ObservableMap observableMap, Object o) {
+            assertEquals(mObservable, observableMap);
+            mNotifications.add((String) o);
+        }
+    };
+
+    public ObservableArrayMapTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mNotifications.clear();
+        mObservable = new ObservableArrayMap<>();
+    }
+
+    public void testAddListener() {
+        mObservable.put("Hello", "World");
+        assertTrue(mNotifications.isEmpty());
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.put("Hello", "Goodbye");
+        assertFalse(mNotifications.isEmpty());
+    }
+
+    public void testRemoveListener() {
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnMapChangedListener(mListener);
+
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.put("Hello", "World");
+        mNotifications.clear();
+        mObservable.removeOnMapChangedListener(mListener);
+        mObservable.put("World", "Hello");
+        assertTrue(mNotifications.isEmpty());
+
+        // test there is no exception when the listener isn't there
+        mObservable.removeOnMapChangedListener(mListener);
+    }
+
+    public void testClear() {
+        mObservable.put("Hello", "World");
+        mObservable.put("World", "Hello");
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.clear();
+        assertEquals(1, mNotifications.size());
+        assertNull(mNotifications.get(0));
+        assertEquals(0, mObservable.size());
+        assertTrue(mObservable.isEmpty());
+
+        mObservable.clear();
+        // No notification when nothing is cleared.
+        assertEquals(1, mNotifications.size());
+    }
+
+    public void testPut() {
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.put("Hello", "World");
+        assertEquals(1, mNotifications.size());
+        assertEquals("Hello", mNotifications.get(0));
+        assertEquals("World", mObservable.get("Hello"));
+
+        mObservable.put("Hello", "World2");
+        assertEquals(2, mNotifications.size());
+        assertEquals("Hello", mNotifications.get(1));
+        assertEquals("World2", mObservable.get("Hello"));
+
+        mObservable.put("World", "Hello");
+        assertEquals(3, mNotifications.size());
+        assertEquals("World", mNotifications.get(2));
+        assertEquals("Hello", mObservable.get("World"));
+    }
+
+    public void testPutAll() {
+        Map<String, String> toAdd = new ArrayMap<>();
+        toAdd.put("Hello", "World");
+        toAdd.put("Goodbye", "Cruel World");
+        mObservable.put("Cruel", "World");
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.putAll(toAdd);
+        assertEquals(3, mObservable.size());
+        assertEquals("World", mObservable.get("Hello"));
+        assertEquals("Cruel World", mObservable.get("Goodbye"));
+        assertEquals(2, mNotifications.size());
+        // order is not guaranteed
+        assertTrue(mNotifications.contains("Hello"));
+        assertTrue(mNotifications.contains("Goodbye"));
+    }
+
+    public void testPutAllSimpleArrayMap() {
+        SimpleArrayMap<String, String> toAdd = new ArrayMap<>();
+        toAdd.put("Hello", "World");
+        toAdd.put("Goodbye", "Cruel World");
+        mObservable.put("Cruel", "World");
+        mObservable.addOnMapChangedListener(mListener);
+        mObservable.putAll(toAdd);
+        assertEquals(3, mObservable.size());
+        assertEquals("World", mObservable.get("Hello"));
+        assertEquals("Cruel World", mObservable.get("Goodbye"));
+        assertEquals(2, mNotifications.size());
+        // order is not guaranteed
+        assertTrue(mNotifications.contains("Hello"));
+        assertTrue(mNotifications.contains("Goodbye"));
+    }
+
+    public void testRemove() {
+        mObservable.put("Hello", "World");
+        mObservable.put("Goodbye", "Cruel World");
+        mObservable.addOnMapChangedListener(mListener);
+        assertEquals("World", mObservable.remove("Hello"));
+        assertEquals(1, mNotifications.size());
+        assertEquals("Hello", mNotifications.get(0));
+
+        assertNull(mObservable.remove("Hello"));
+        // nothing removed, don't notify
+        assertEquals(1, mNotifications.size());
+    }
+
+    public void testRemoveAll() {
+        ArrayList<String> toRemove = new ArrayList<>();
+        toRemove.add("Hello");
+        toRemove.add("Goodbye");
+        mObservable.put("Hello", "World");
+        mObservable.put("Goodbye", "Cruel World");
+        mObservable.put("Cruel", "World");
+        mObservable.addOnMapChangedListener(mListener);
+        assertTrue(mObservable.removeAll(toRemove));
+        assertEquals(2, mNotifications.size());
+        // order is not guaranteed
+        assertTrue(mNotifications.contains("Hello"));
+        assertTrue(mNotifications.contains("Goodbye"));
+
+        assertTrue(mObservable.containsKey("Cruel"));
+
+        // Test nothing removed
+        assertFalse(mObservable.removeAll(toRemove));
+        assertEquals(2, mNotifications.size());
+    }
+
+    public void testRetainAll() {
+        ArrayList<String> toRetain = new ArrayList<>();
+        toRetain.add("Hello");
+        toRetain.add("Goodbye");
+        mObservable.put("Hello", "World");
+        mObservable.put("Goodbye", "Cruel World");
+        mObservable.put("Cruel", "World");
+        mObservable.addOnMapChangedListener(mListener);
+        assertTrue(mObservable.retainAll(toRetain));
+        assertEquals(1, mNotifications.size());
+        assertEquals("Cruel", mNotifications.get(0));
+        assertTrue(mObservable.containsKey("Hello"));
+        assertTrue(mObservable.containsKey("Goodbye"));
+
+        // Test nothing removed
+        assertFalse(mObservable.retainAll(toRetain));
+        assertEquals(1, mNotifications.size());
+    }
+
+    public void testRemoveAt() {
+        mObservable.put("Hello", "World");
+        mObservable.put("Goodbye", "Cruel World");
+        mObservable.addOnMapChangedListener(mListener);
+        String key = mObservable.keyAt(0);
+        String value = mObservable.valueAt(0);
+        assertTrue("Hello".equals(key) || "Goodbye".equals(key));
+        assertEquals(value, mObservable.removeAt(0));
+        assertEquals(1, mNotifications.size());
+        assertEquals(key, mNotifications.get(0));
+    }
+
+    public void testSetValueAt() {
+        mObservable.put("Hello", "World");
+        mObservable.addOnMapChangedListener(mListener);
+        assertEquals("World", mObservable.setValueAt(0, "Cruel World"));
+        assertEquals(1, mNotifications.size());
+        assertEquals("Hello", mNotifications.get(0));
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java
new file mode 100644
index 0000000..7b29122
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ObservableFieldTestBinding;
+import android.databinding.testapp.vo.ObservableFieldBindingObject;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class ObservableFieldTest extends BaseDataBinderTest<ObservableFieldTestBinding> {
+    private ObservableFieldBindingObject mObj;
+
+    public ObservableFieldTest() {
+        super(ObservableFieldTestBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mObj = new ObservableFieldBindingObject();
+                    mBinder.setObj(mObj);
+                    mBinder.executePendingBindings();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testBoolean() {
+        TextView view = mBinder.bField;
+        assertEquals("false", view.getText());
+
+        mObj.bField.set(true);
+        mBinder.executePendingBindings();
+
+        assertEquals("true", view.getText());
+    }
+
+    @UiThreadTest
+    public void testByte() {
+        TextView view = mBinder.tField;
+        assertEquals("0", view.getText());
+
+        mObj.tField.set((byte) 1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1", view.getText());
+    }
+
+    @UiThreadTest
+    public void testShort() {
+        TextView view = mBinder.sField;
+        assertEquals("0", view.getText());
+
+        mObj.sField.set((short) 1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1", view.getText());
+    }
+
+    @UiThreadTest
+    public void testChar() {
+        TextView view = mBinder.cField;
+        assertEquals("\u0000", view.getText());
+
+        mObj.cField.set('A');
+        mBinder.executePendingBindings();
+
+        assertEquals("A", view.getText());
+    }
+
+    @UiThreadTest
+    public void testInt() {
+        TextView view = mBinder.iField;
+        assertEquals("0", view.getText());
+
+        mObj.iField.set(1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1", view.getText());
+    }
+
+    @UiThreadTest
+    public void testLong() {
+        TextView view = mBinder.lField;
+        assertEquals("0", view.getText());
+
+        mObj.lField.set(1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1", view.getText());
+    }
+
+    @UiThreadTest
+    public void testFloat() {
+        TextView view = mBinder.fField;
+        assertEquals("0.0", view.getText());
+
+        mObj.fField.set(1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1.0", view.getText());
+    }
+
+    @UiThreadTest
+    public void testDouble() {
+        TextView view = mBinder.dField;
+        assertEquals("0.0", view.getText());
+
+        mObj.dField.set(1);
+        mBinder.executePendingBindings();
+
+        assertEquals("1.0", view.getText());
+    }
+
+    @UiThreadTest
+    public void testObject() {
+        TextView view = mBinder.oField;
+        assertEquals("Hello", view.getText());
+
+        mObj.oField.set("World");
+        mBinder.executePendingBindings();
+
+        assertEquals("World", view.getText());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java
new file mode 100644
index 0000000..b14f413
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.BaseDataBinderTest;
+import android.databinding.testapp.R;
+import android.databinding.testapp.databinding.ObservableWithNotBindableFieldBinding;
+import android.databinding.testapp.vo.ObservableWithNotBindableFieldObject;
+
+import android.test.UiThreadTest;
+
+public class ObservableWithNotBindableFieldObjectTest extends BaseDataBinderTest<ObservableWithNotBindableFieldBinding> {
+
+
+    public ObservableWithNotBindableFieldObjectTest() {
+        super(ObservableWithNotBindableFieldBinding.class);
+    }
+
+    @UiThreadTest
+    public void testSimple() {
+        ObservableWithNotBindableFieldObject obj = new ObservableWithNotBindableFieldObject();
+        mBinder.setObj(obj);
+        mBinder.executePendingBindings();
+        assertEquals("", mBinder.textView.getText().toString());
+        obj.update("100");
+        mBinder.executePendingBindings();
+        assertEquals("100", mBinder.textView.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java
new file mode 100644
index 0000000..4325213
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.util.ArrayMap;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import android.databinding.testapp.BR;
+public class ProcessBindableTest extends BaseDataBinderTest<BasicBindingBinding> {
+    private static String[] EXPECTED_BINDING_NAMES = {
+            "bindableField1",
+            "bindableField2",
+            "bindableField3",
+            "bindableField4",
+            "mbindableField5",
+            "bindableField6",
+            "bindableField7",
+            "bindableField8",
+    };
+
+    public ProcessBindableTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    public void testFieldsGenerated() throws IllegalAccessException {
+        Field[] fields = BR.class.getFields();
+
+        ArrayMap<String, Integer> fieldValues = new ArrayMap<>();
+        int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
+        for (Field field: fields) {
+            assertTrue(field.getModifiers() == modifiers);
+            String name = field.getName();
+            fieldValues.put(name, field.getInt(null));
+        }
+
+        assertTrue(fieldValues.containsKey("_all"));
+        assertEquals(0, (int) fieldValues.get("_all"));
+        HashSet<Integer> values = new HashSet<>();
+        values.add(0);
+
+        for (String fieldName : EXPECTED_BINDING_NAMES) {
+            assertTrue("missing field: " + fieldName, fieldValues.containsKey(fieldName));
+            assertFalse(values.contains(fieldValues.get(fieldName)));
+            values.add(fieldValues.get(fieldName));
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java
new file mode 100644
index 0000000..52d558f
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ProgressBarAdapterTestBinding;
+import android.databinding.testapp.vo.ProgressBarBindingObject;
+
+import android.os.Build;
+import android.widget.ProgressBar;
+
+public class ProgressBarBindingAdapterTest
+        extends BindingAdapterTestBase<ProgressBarAdapterTestBinding, ProgressBarBindingObject> {
+
+    ProgressBar mView;
+
+    public ProgressBarBindingAdapterTest() {
+        super(ProgressBarAdapterTestBinding.class, ProgressBarBindingObject.class,
+                R.layout.progress_bar_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testTint() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            assertEquals(mBindingObject.getIndeterminateTint(),
+                    mView.getIndeterminateTintList().getDefaultColor());
+            assertEquals(mBindingObject.getProgressTint(),
+                    mView.getProgressTintList().getDefaultColor());
+            assertEquals(mBindingObject.getSecondaryProgressTint(),
+                    mView.getSecondaryProgressTintList().getDefaultColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getIndeterminateTint(),
+                    mView.getIndeterminateTintList().getDefaultColor());
+            assertEquals(mBindingObject.getProgressTint(),
+                    mView.getProgressTintList().getDefaultColor());
+            assertEquals(mBindingObject.getSecondaryProgressTint(),
+                    mView.getSecondaryProgressTintList().getDefaultColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java
new file mode 100644
index 0000000..44b7dcf
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java
@@ -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 android.databinding.testapp;
+
+import android.databinding.PropertyChangeRegistry;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+
+import android.databinding.Observable;
+import android.databinding.OnPropertyChangedListener;
+
+public class PropertyChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> {
+
+    private int notificationCount = 0;
+
+    public PropertyChangeRegistryTest() {
+        super(BasicBindingBinding.class);
+    }
+
+    public void testNotifyChanged() {
+        PropertyChangeRegistry propertyChangeRegistry = new PropertyChangeRegistry();
+
+        final Observable observableObj = new Observable() {
+            @Override
+            public void addOnPropertyChangedListener(
+                    OnPropertyChangedListener onPropertyChangedListener) {
+            }
+
+            @Override
+            public void removeOnPropertyChangedListener(
+                    OnPropertyChangedListener onPropertyChangedListener) {
+            }
+        };
+
+        final int expectedId = 100;
+        OnPropertyChangedListener listener = new OnPropertyChangedListener() {
+            @Override
+            public void onPropertyChanged(Observable observable, int id) {
+                notificationCount++;
+                assertEquals(expectedId, id);
+                assertEquals(observableObj, observable);
+            }
+        };
+        propertyChangeRegistry.add(listener);
+
+        assertEquals(0, notificationCount);
+        propertyChangeRegistry.notifyChange(observableObj, expectedId);
+        assertEquals(1, notificationCount);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java
new file mode 100644
index 0000000..7ab4a0d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.RadioGroupAdapterTestBinding;
+import android.databinding.testapp.vo.RadioGroupBindingObject;
+
+import android.widget.RadioGroup;
+
+public class RadioGroupBindingAdapterTest
+        extends BindingAdapterTestBase<RadioGroupAdapterTestBinding, RadioGroupBindingObject> {
+
+    RadioGroup mView;
+
+    public RadioGroupBindingAdapterTest() {
+        super(RadioGroupAdapterTestBinding.class, RadioGroupBindingObject.class,
+                R.layout.radio_group_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testRadioGroup() throws Throwable {
+        assertEquals(mBindingObject.getCheckedButton(), mView.getCheckedRadioButtonId());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getCheckedButton(), mView.getCheckedRadioButtonId());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java
new file mode 100644
index 0000000..6970075
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java
@@ -0,0 +1,22 @@
+package android.databinding.testapp;
+
+import android.test.UiThreadTest;
+import android.databinding.testapp.databinding.ReadComplexTernaryBinding;
+
+import android.databinding.testapp.vo.User;
+
+public class ReadComplexTernaryTest extends BaseDataBinderTest<ReadComplexTernaryBinding> {
+    public ReadComplexTernaryTest() {
+        super(ReadComplexTernaryBinding.class);
+    }
+
+    @UiThreadTest
+    public void testWhenNull() {
+        User user = new User();
+        user.setName("a");
+        user.setFullName("a b");
+        mBinder.setUser(user);
+        mBinder.executePendingBindings();
+        assertEquals("?", mBinder.textView.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java
new file mode 100644
index 0000000..f48bb2b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ResourceTestBinding;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class ResourceTest extends BaseDataBinderTest<ResourceTestBinding> {
+
+    public ResourceTest() {
+        super(ResourceTestBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mBinder.setCount(0);
+        mBinder.setTitle("Mrs.");
+        mBinder.setLastName("Doubtfire");
+        mBinder.setBase(2);
+        mBinder.setPbase(3);
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mBinder.executePendingBindings();
+                }
+            });
+        } catch (Throwable throwable) {
+            throw new Exception(throwable);
+        }
+    }
+
+    @UiThreadTest
+    public void testStringFormat() throws Throwable {
+        TextView view = mBinder.textView0;
+        assertEquals("Mrs. Doubtfire", view.getText().toString());
+
+        mBinder.setTitle("Mr.");
+        mBinder.executePendingBindings();
+        assertEquals("Mr. Doubtfire", view.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testQuantityString() throws Throwable {
+        TextView view = mBinder.textView1;
+        assertEquals("oranges", view.getText().toString());
+
+        mBinder.setCount(1);
+        mBinder.executePendingBindings();
+        assertEquals("orange", view.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testFractionNoParameters() throws Throwable {
+        TextView view = mBinder.fractionNoParameters;
+        assertEquals("1.5", view.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testFractionOneParameter() throws Throwable {
+        TextView view = mBinder.fractionOneParameter;
+        assertEquals("3.0", view.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testFractionTwoParameters() throws Throwable {
+        TextView view = mBinder.fractionTwoParameters;
+        assertEquals("9.0", view.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java
new file mode 100644
index 0000000..384ff21
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.SpinnerAdapterTestBinding;
+import android.databinding.testapp.vo.SpinnerBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.Spinner;
+
+public class SpinnerBindingAdapterTest
+        extends BindingAdapterTestBase<SpinnerAdapterTestBinding, SpinnerBindingObject> {
+
+    Spinner mView;
+
+    public SpinnerBindingAdapterTest() {
+        super(SpinnerAdapterTestBinding.class, SpinnerBindingObject.class,
+                R.layout.spinner_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testSpinner() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getPopupBackground(),
+                    ((ColorDrawable) mView.getPopupBackground()).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getPopupBackground(),
+                    ((ColorDrawable) mView.getPopupBackground()).getColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java
new file mode 100644
index 0000000..65f38ba
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.SwitchAdapterTestBinding;
+import android.databinding.testapp.vo.SwitchBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.Switch;
+
+public class SwitchBindingAdapterTest
+        extends BindingAdapterTestBase<SwitchAdapterTestBinding, SwitchBindingObject> {
+
+    Switch mView;
+
+    public SwitchBindingAdapterTest() {
+        super(SwitchAdapterTestBinding.class, SwitchBindingObject.class,
+                R.layout.switch_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testSwitch() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getThumb(),
+                    ((ColorDrawable) mView.getThumbDrawable()).getColor());
+            assertEquals(mBindingObject.getTrack(),
+                    ((ColorDrawable) mView.getTrackDrawable()).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getThumb(),
+                    ((ColorDrawable) mView.getThumbDrawable()).getColor());
+            assertEquals(mBindingObject.getTrack(),
+                    ((ColorDrawable) mView.getTrackDrawable()).getColor());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java
new file mode 100644
index 0000000..c72010b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.TabWidgetAdapterTestBinding;
+import android.databinding.testapp.vo.TabWidgetBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.TabWidget;
+
+public class TabWidgetBindingAdapterTest
+        extends BindingAdapterTestBase<TabWidgetAdapterTestBinding, TabWidgetBindingObject> {
+
+    TabWidget mView;
+
+    public TabWidgetBindingAdapterTest() {
+        super(TabWidgetAdapterTestBinding.class, TabWidgetBindingObject.class,
+                R.layout.tab_widget_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testStrip() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getDivider().getColor(),
+                    ((ColorDrawable) mView.getDividerDrawable()).getColor());
+            assertEquals(mBindingObject.isTabStripEnabled(), mView.isStripEnabled());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getDivider().getColor(),
+                    ((ColorDrawable) mView.getDividerDrawable()).getColor());
+            assertEquals(mBindingObject.isTabStripEnabled(), mView.isStripEnabled());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java
new file mode 100644
index 0000000..4f00623
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.TableLayoutAdapterTestBinding;
+import android.databinding.testapp.vo.TableLayoutBindingObject;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.widget.TableLayout;
+
+public class TableLayoutBindingAdapterTest
+        extends BindingAdapterTestBase<TableLayoutAdapterTestBinding, TableLayoutBindingObject> {
+
+    TableLayout mView;
+
+    public TableLayoutBindingAdapterTest() {
+        super(TableLayoutAdapterTestBinding.class, TableLayoutBindingObject.class,
+                R.layout.table_layout_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testDivider() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getDivider(),
+                    ((ColorDrawable) mView.getDividerDrawable()).getColor());
+            changeValues();
+            assertEquals(mBindingObject.getDivider(),
+                    ((ColorDrawable) mView.getDividerDrawable()).getColor());
+        }
+    }
+
+    public void testColumns() throws Throwable {
+        assertFalse(mView.isColumnCollapsed(0));
+        assertTrue(mView.isColumnCollapsed(1));
+        assertFalse(mView.isColumnCollapsed(2));
+
+        assertFalse(mView.isColumnShrinkable(0));
+        assertTrue(mView.isColumnShrinkable(1));
+        assertFalse(mView.isColumnShrinkable(2));
+
+        assertFalse(mView.isColumnStretchable(0));
+        assertTrue(mView.isColumnStretchable(1));
+        assertFalse(mView.isColumnStretchable(2));
+
+        changeValues();
+
+        assertFalse(mView.isColumnCollapsed(0));
+        assertFalse(mView.isColumnCollapsed(1));
+        assertFalse(mView.isColumnCollapsed(2));
+
+        assertTrue(mView.isColumnShrinkable(0));
+        assertTrue(mView.isColumnShrinkable(1));
+        assertFalse(mView.isColumnShrinkable(2));
+
+        assertTrue(mView.isColumnStretchable(0));
+        assertTrue(mView.isColumnStretchable(1));
+        assertTrue(mView.isColumnStretchable(2));
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java
new file mode 100644
index 0000000..98272dd
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.TextViewAdapterTestBinding;
+import android.databinding.testapp.vo.TextViewBindingObject;
+
+import android.annotation.TargetApi;
+import android.databinding.adapters.TextViewBindingAdapter;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.Spannable;
+import android.text.method.DialerKeyListener;
+import android.text.method.DigitsKeyListener;
+import android.text.method.KeyListener;
+import android.text.method.TextKeyListener;
+import android.widget.TextView;
+
+public class TextViewBindingAdapterTest
+        extends BindingAdapterTestBase<TextViewAdapterTestBinding, TextViewBindingObject> {
+
+    public TextViewBindingAdapterTest() {
+        super(TextViewAdapterTestBinding.class, TextViewBindingObject.class,
+                R.layout.text_view_adapter_test);
+    }
+
+    public void testNumeric() throws Throwable {
+        TextView view = mBinder.numericText;
+        assertTrue(view.getKeyListener() instanceof DigitsKeyListener);
+        DigitsKeyListener listener = (DigitsKeyListener) view.getKeyListener();
+        assertEquals(getExpectedNumericType(), listener.getInputType());
+
+        changeValues();
+
+        assertTrue(view.getKeyListener() instanceof DigitsKeyListener);
+        listener = (DigitsKeyListener) view.getKeyListener();
+        assertEquals(getExpectedNumericType(), listener.getInputType());
+    }
+
+    private int getExpectedNumericType() {
+        int expectedType = InputType.TYPE_CLASS_NUMBER;
+        if ((mBindingObject.getNumeric() & TextViewBindingAdapter.SIGNED) != 0) {
+            expectedType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
+        }
+        if ((mBindingObject.getNumeric() & TextViewBindingAdapter.DECIMAL) != 0) {
+            expectedType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        }
+        return expectedType;
+    }
+
+    public void testDrawables() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            TextView view = mBinder.textDrawableNormal;
+            assertEquals(mBindingObject.getDrawableLeft(),
+                    ((ColorDrawable) view.getCompoundDrawables()[0]).getColor());
+            assertEquals(mBindingObject.getDrawableTop(),
+                    ((ColorDrawable) view.getCompoundDrawables()[1]).getColor());
+            assertEquals(mBindingObject.getDrawableRight(),
+                    ((ColorDrawable) view.getCompoundDrawables()[2]).getColor());
+            assertEquals(mBindingObject.getDrawableBottom(),
+                    ((ColorDrawable) view.getCompoundDrawables()[3]).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getDrawableLeft(),
+                    ((ColorDrawable) view.getCompoundDrawables()[0]).getColor());
+            assertEquals(mBindingObject.getDrawableTop(),
+                    ((ColorDrawable) view.getCompoundDrawables()[1]).getColor());
+            assertEquals(mBindingObject.getDrawableRight(),
+                    ((ColorDrawable) view.getCompoundDrawables()[2]).getColor());
+            assertEquals(mBindingObject.getDrawableBottom(),
+                    ((ColorDrawable) view.getCompoundDrawables()[3]).getColor());
+        }
+    }
+
+    public void testDrawableStartEnd() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            TextView view = mBinder.textDrawableStartEnd;
+            assertEquals(mBindingObject.getDrawableStart(),
+                    ((ColorDrawable) view.getCompoundDrawablesRelative()[0]).getColor());
+            assertEquals(mBindingObject.getDrawableEnd(),
+                    ((ColorDrawable) view.getCompoundDrawablesRelative()[2]).getColor());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getDrawableStart(),
+                    ((ColorDrawable) view.getCompoundDrawablesRelative()[0]).getColor());
+            assertEquals(mBindingObject.getDrawableEnd(),
+                    ((ColorDrawable) view.getCompoundDrawablesRelative()[2]).getColor());
+        }
+    }
+
+    public void testSimpleProperties() throws Throwable {
+        TextView view = mBinder.textView;
+
+        assertEquals(mBindingObject.getAutoLink(), view.getAutoLinkMask());
+        assertEquals(mBindingObject.getDrawablePadding(), view.getCompoundDrawablePadding());
+        assertEquals(mBindingObject.getTextSize(), view.getTextSize());
+        assertEquals(mBindingObject.getTextColorHint(), view.getHintTextColors().getDefaultColor());
+        assertEquals(mBindingObject.getTextColorLink(), view.getLinkTextColors().getDefaultColor());
+        assertEquals(mBindingObject.isAutoText(), isAutoTextEnabled(view));
+        assertEquals(mBindingObject.getCapitalize(), getCapitalization(view));
+        assertEquals(mBindingObject.getImeActionLabel(), view.getImeActionLabel());
+        assertEquals(mBindingObject.getImeActionId(), view.getImeActionId());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getTextColorHighlight(), view.getHighlightColor());
+            assertEquals(mBindingObject.getLineSpacingExtra(), view.getLineSpacingExtra());
+            assertEquals(mBindingObject.getLineSpacingMultiplier(),
+                    view.getLineSpacingMultiplier());
+            assertEquals(mBindingObject.getShadowColor(), view.getShadowColor());
+            assertEquals(mBindingObject.getShadowDx(), view.getShadowDx());
+            assertEquals(mBindingObject.getShadowDy(), view.getShadowDy());
+            assertEquals(mBindingObject.getShadowRadius(), view.getShadowRadius());
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                assertEquals(mBindingObject.getMaxLength(), getMaxLength(view));
+            }
+        }
+
+        changeValues();
+
+        assertEquals(mBindingObject.getAutoLink(), view.getAutoLinkMask());
+        assertEquals(mBindingObject.getDrawablePadding(), view.getCompoundDrawablePadding());
+        assertEquals(mBindingObject.getTextSize(), view.getTextSize());
+        assertEquals(mBindingObject.getTextColorHint(), view.getHintTextColors().getDefaultColor());
+        assertEquals(mBindingObject.getTextColorLink(), view.getLinkTextColors().getDefaultColor());
+        assertEquals(mBindingObject.isAutoText(), isAutoTextEnabled(view));
+        assertEquals(mBindingObject.getCapitalize(), getCapitalization(view));
+        assertEquals(mBindingObject.getImeActionLabel(), view.getImeActionLabel());
+        assertEquals(mBindingObject.getImeActionId(), view.getImeActionId());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getTextColorHighlight(), view.getHighlightColor());
+            assertEquals(mBindingObject.getLineSpacingExtra(), view.getLineSpacingExtra());
+            assertEquals(mBindingObject.getLineSpacingMultiplier(),
+                    view.getLineSpacingMultiplier());
+            assertEquals(mBindingObject.getShadowColor(), view.getShadowColor());
+            assertEquals(mBindingObject.getShadowDx(), view.getShadowDx());
+            assertEquals(mBindingObject.getShadowDy(), view.getShadowDy());
+            assertEquals(mBindingObject.getShadowRadius(), view.getShadowRadius());
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                assertEquals(mBindingObject.getMaxLength(), getMaxLength(view));
+            }
+        }
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mBindingObject.setCapitalize(TextKeyListener.Capitalize.CHARACTERS);
+                mBinder.executePendingBindings();
+            }
+        });
+
+        assertEquals(mBindingObject.getCapitalize(), getCapitalization(view));
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mBindingObject.setCapitalize(TextKeyListener.Capitalize.WORDS);
+                mBinder.executePendingBindings();
+            }
+        });
+
+        assertEquals(mBindingObject.getCapitalize(), getCapitalization(view));
+    }
+
+    private static boolean isAutoTextEnabled(TextView view) {
+        KeyListener keyListener = view.getKeyListener();
+        if (keyListener == null) {
+            return false;
+        }
+        if (!(keyListener instanceof TextKeyListener)) {
+            return false;
+        }
+        TextKeyListener textKeyListener = (TextKeyListener) keyListener;
+        return ((textKeyListener.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0);
+    }
+
+    private static TextKeyListener.Capitalize getCapitalization(TextView view) {
+        KeyListener keyListener = view.getKeyListener();
+        if (keyListener == null) {
+            return TextKeyListener.Capitalize.NONE;
+        }
+        int inputType = keyListener.getInputType();
+        if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
+            return TextKeyListener.Capitalize.CHARACTERS;
+        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) {
+            return TextKeyListener.Capitalize.WORDS;
+        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) {
+            return TextKeyListener.Capitalize.SENTENCES;
+        } else {
+            return TextKeyListener.Capitalize.NONE;
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private static int getMaxLength(TextView view) {
+        InputFilter[] filters = view.getFilters();
+        for (InputFilter filter : filters) {
+            if (filter instanceof InputFilter.LengthFilter) {
+                InputFilter.LengthFilter lengthFilter = (InputFilter.LengthFilter) filter;
+                return lengthFilter.getMax();
+            }
+        }
+        return -1;
+    }
+
+    public void testAllCaps() throws Throwable {
+        TextView view = mBinder.textAllCaps;
+
+        assertEquals(mBindingObject.isTextAllCaps(), view.getTransformationMethod() != null);
+        if (view.getTransformationMethod() != null) {
+            assertEquals("ALL CAPS",
+                    view.getTransformationMethod().getTransformation("all caps", view));
+        }
+
+        changeValues();
+
+        assertEquals(mBindingObject.isTextAllCaps(), view.getTransformationMethod() != null);
+        if (view.getTransformationMethod() != null) {
+            assertEquals("ALL CAPS",
+                    view.getTransformationMethod().getTransformation("all caps", view));
+        }
+    }
+
+    public void testBufferType() throws Throwable {
+        TextView view = mBinder.textBufferType;
+
+        assertEquals(mBindingObject.getBufferType(), getBufferType(view));
+        changeValues();
+        assertEquals(mBindingObject.getBufferType(), getBufferType(view));
+    }
+
+    private static TextView.BufferType getBufferType(TextView view) {
+        CharSequence text = view.getText();
+        if (text instanceof Editable) {
+            return TextView.BufferType.EDITABLE;
+        }
+        if (text instanceof Spannable) {
+            return TextView.BufferType.SPANNABLE;
+        }
+        return TextView.BufferType.NORMAL;
+    }
+
+    public void testInputType() throws Throwable {
+        TextView view = mBinder.textInputType;
+        assertEquals(mBindingObject.getInputType(), view.getInputType());
+        changeValues();
+        assertEquals(mBindingObject.getInputType(), view.getInputType());
+    }
+
+    public void testDigits() throws Throwable {
+        TextView view = mBinder.textDigits;
+        assertEquals(mBindingObject.getDigits(), getDigits(view));
+        changeValues();
+        assertEquals(mBindingObject.getDigits(), getDigits(view));
+    }
+
+    private static String getDigits(TextView textView) {
+        KeyListener keyListener = textView.getKeyListener();
+        if (!(keyListener instanceof DigitsKeyListener)) {
+            return null;
+        }
+        DigitsKeyListener digitsKeyListener = (DigitsKeyListener) keyListener;
+        String input = "abcdefghijklmnopqrstuvwxyz";
+        Spannable spannable = Spannable.Factory.getInstance().newSpannable(input);
+        return digitsKeyListener.filter(input, 0, input.length(), spannable, 0, input.length())
+                .toString();
+    }
+
+    public void testPhoneNumber() throws Throwable {
+        TextView textView = mBinder.textPhoneNumber;
+        assertEquals(mBindingObject.isPhoneNumber(), isPhoneNumber(textView));
+        changeValues();
+        assertEquals(mBindingObject.isPhoneNumber(), isPhoneNumber(textView));
+    }
+
+    private static boolean isPhoneNumber(TextView view) {
+        KeyListener keyListener = view.getKeyListener();
+        return (keyListener instanceof DialerKeyListener);
+    }
+
+    public void testInputMethod() throws Throwable {
+        TextView textView = mBinder.textInputMethod;
+        assertTrue(TextViewBindingObject.KeyListener1.class.isInstance(textView.getKeyListener()));
+        changeValues();
+        assertTrue(TextViewBindingObject.KeyListener2.class.isInstance(textView.getKeyListener()));
+    }
+
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java
new file mode 100644
index 0000000..c9bbb35
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ViewAdapterTestBinding;
+import android.databinding.testapp.vo.ViewBindingObject;
+
+import android.content.res.ColorStateList;
+import android.os.Build;
+import android.test.UiThreadTest;
+import android.view.View;
+
+public class ViewBindingAdapterTest extends BindingAdapterTestBase<ViewAdapterTestBinding, ViewBindingObject> {
+
+    public ViewBindingAdapterTest() {
+        super(ViewAdapterTestBinding.class, ViewBindingObject.class, R.layout.view_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testPadding() throws Throwable {
+        View view = mBinder.padding;
+        assertEquals(mBindingObject.getPadding(), view.getPaddingBottom());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingTop());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingRight());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingLeft());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getPadding(), view.getPaddingBottom());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingTop());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingRight());
+        assertEquals(mBindingObject.getPadding(), view.getPaddingLeft());
+    }
+
+    public void testPaddingLeftRight() throws Throwable {
+        View view = mBinder.paddingLeftRight;
+        assertEquals(mBindingObject.getPaddingLeft(), view.getPaddingLeft());
+        assertEquals(mBindingObject.getPaddingRight(), view.getPaddingRight());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getPaddingLeft(), view.getPaddingLeft());
+        assertEquals(mBindingObject.getPaddingRight(), view.getPaddingRight());
+    }
+
+    public void testPaddingStartEnd() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            View view = mBinder.paddingStartEnd;
+            assertEquals(mBindingObject.getPaddingStart(), view.getPaddingStart());
+            assertEquals(mBindingObject.getPaddingEnd(), view.getPaddingEnd());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getPaddingStart(), view.getPaddingStart());
+            assertEquals(mBindingObject.getPaddingEnd(), view.getPaddingEnd());
+        }
+    }
+
+    public void testPaddingTopBottom() throws Throwable {
+        View view = mBinder.paddingTopBottom;
+        assertEquals(mBindingObject.getPaddingTop(), view.getPaddingTop());
+        assertEquals(mBindingObject.getPaddingBottom(), view.getPaddingBottom());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getPaddingTop(), view.getPaddingTop());
+        assertEquals(mBindingObject.getPaddingBottom(), view.getPaddingBottom());
+    }
+
+    public void testBackgroundTint() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            View view = mBinder.backgroundTint;
+            assertNotNull(view.getBackgroundTintList());
+            ColorStateList colorStateList = view.getBackgroundTintList();
+            assertEquals(mBindingObject.getBackgroundTint(), colorStateList.getDefaultColor());
+
+            changeValues();
+
+            assertNotNull(view.getBackgroundTintList());
+            colorStateList = view.getBackgroundTintList();
+            assertEquals(mBindingObject.getBackgroundTint(), colorStateList.getDefaultColor());
+        }
+    }
+
+    public void testFadeScrollbars() throws Throwable {
+        View view = mBinder.fadeScrollbars;
+        assertEquals(mBindingObject.getFadeScrollbars(), view.isScrollbarFadingEnabled());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getFadeScrollbars(), view.isScrollbarFadingEnabled());
+    }
+
+    public void testNextFocus() throws Throwable {
+        View view = mBinder.nextFocus;
+
+        assertEquals(mBindingObject.getNextFocusDown(), view.getNextFocusDownId());
+        assertEquals(mBindingObject.getNextFocusUp(), view.getNextFocusUpId());
+        assertEquals(mBindingObject.getNextFocusLeft(), view.getNextFocusLeftId());
+        assertEquals(mBindingObject.getNextFocusRight(), view.getNextFocusRightId());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.getNextFocusForward(), view.getNextFocusForwardId());
+        }
+
+        changeValues();
+
+        assertEquals(mBindingObject.getNextFocusDown(), view.getNextFocusDownId());
+        assertEquals(mBindingObject.getNextFocusUp(), view.getNextFocusUpId());
+        assertEquals(mBindingObject.getNextFocusLeft(), view.getNextFocusLeftId());
+        assertEquals(mBindingObject.getNextFocusRight(), view.getNextFocusRightId());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.getNextFocusForward(), view.getNextFocusForwardId());
+        }
+    }
+
+    public void testRequiresFadingEdge() throws Throwable {
+        View view = mBinder.requiresFadingEdge;
+
+        assertTrue(view.isVerticalFadingEdgeEnabled());
+        assertFalse(view.isHorizontalFadingEdgeEnabled());
+
+        changeValues();
+
+        assertFalse(view.isVerticalFadingEdgeEnabled());
+        assertTrue(view.isHorizontalFadingEdgeEnabled());
+    }
+
+    public void testScrollbar() throws Throwable {
+        View view = mBinder.scrollbar;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getScrollbarDefaultDelayBeforeFade(),
+                    view.getScrollBarDefaultDelayBeforeFade());
+            assertEquals(mBindingObject.getScrollbarFadeDuration(), view.getScrollBarFadeDuration());
+            assertEquals(mBindingObject.getScrollbarSize(), view.getScrollBarSize());
+        }
+        assertEquals(mBindingObject.getScrollbarStyle(), view.getScrollBarStyle());
+
+        changeValues();
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(mBindingObject.getScrollbarDefaultDelayBeforeFade(),
+                    view.getScrollBarDefaultDelayBeforeFade());
+            assertEquals(mBindingObject.getScrollbarFadeDuration(), view.getScrollBarFadeDuration());
+            assertEquals(mBindingObject.getScrollbarSize(), view.getScrollBarSize());
+        }
+        assertEquals(mBindingObject.getScrollbarStyle(), view.getScrollBarStyle());
+    }
+
+    public void testTransformPivot() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            View view = mBinder.transformPivot;
+
+            assertEquals(mBindingObject.getTransformPivotX(), view.getPivotX());
+            assertEquals(mBindingObject.getTransformPivotY(), view.getPivotY());
+
+            changeValues();
+
+            assertEquals(mBindingObject.getTransformPivotX(), view.getPivotX());
+            assertEquals(mBindingObject.getTransformPivotY(), view.getPivotY());
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java
new file mode 100644
index 0000000..981495d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ViewGroupAdapterTestBinding;
+import android.databinding.testapp.vo.ViewGroupBindingObject;
+
+import android.os.Build;
+import android.view.ViewGroup;
+
+public class ViewGroupBindingAdapterTest
+        extends BindingAdapterTestBase<ViewGroupAdapterTestBinding, ViewGroupBindingObject> {
+
+    ViewGroup mView;
+
+    public ViewGroupBindingAdapterTest() {
+        super(ViewGroupAdapterTestBinding.class, ViewGroupBindingObject.class,
+                R.layout.view_group_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view;
+    }
+
+    public void testDrawnWithCache() throws Throwable {
+        assertEquals(mBindingObject.isAlwaysDrawnWithCache(),
+                mView.isAlwaysDrawnWithCacheEnabled());
+
+        changeValues();
+
+        assertEquals(mBindingObject.isAlwaysDrawnWithCache(),
+                mView.isAlwaysDrawnWithCacheEnabled());
+    }
+
+    public void testAnimationCache() throws Throwable {
+        assertEquals(mBindingObject.isAnimationCache(), mView.isAnimationCacheEnabled());
+
+        changeValues();
+
+        assertEquals(mBindingObject.isAnimationCache(), mView.isAnimationCacheEnabled());
+    }
+
+    public void testSplitMotionEvents() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.isSplitMotionEvents(),
+                    mView.isMotionEventSplittingEnabled());
+
+            changeValues();
+
+            assertEquals(mBindingObject.isSplitMotionEvents(),
+                    mView.isMotionEventSplittingEnabled());
+        }
+    }
+
+    public void testAnimateLayoutChanges() throws Throwable {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            assertEquals(mBindingObject.isAnimateLayoutChanges(),
+                    mView.getLayoutTransition() != null);
+
+            changeValues();
+
+            assertEquals(mBindingObject.isAnimateLayoutChanges(),
+                    mView.getLayoutTransition() != null);
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java
new file mode 100644
index 0000000..87cb5b2
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ViewStubAdapterTestBinding;
+import android.databinding.testapp.vo.ViewStubBindingObject;
+
+import android.view.ViewStub;
+
+public class ViewStubBindingAdapterTest
+        extends BindingAdapterTestBase<ViewStubAdapterTestBinding, ViewStubBindingObject> {
+
+    ViewStub mView;
+
+    public ViewStubBindingAdapterTest() {
+        super(ViewStubAdapterTestBinding.class, ViewStubBindingObject.class,
+                R.layout.view_stub_adapter_test);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mView = mBinder.view.getViewStub();
+    }
+
+    public void testLayout() throws Throwable {
+        assertEquals(mBindingObject.getLayout(), mView.getLayoutResource());
+
+        changeValues();
+
+        assertEquals(mBindingObject.getLayout(), mView.getLayoutResource());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java
new file mode 100644
index 0000000..a78bda3
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.databinding.testapp.databinding.ViewStubBinding;
+import android.databinding.testapp.databinding.ViewStubContentsBinding;
+import android.databinding.ViewStubProxy;
+import android.support.v4.util.ArrayMap;
+import android.test.UiThreadTest;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class ViewStubTest extends BaseDataBinderTest<ViewStubBinding> {
+
+    public ViewStubTest() {
+        super(ViewStubBinding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mBinder.setViewStubVisibility(View.GONE);
+        mBinder.setFirstName("Hello");
+        mBinder.setLastName("World");
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mBinder.executePendingBindings();
+                }
+            });
+        } catch (Exception e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new Exception(t);
+        }
+    }
+
+    @UiThreadTest
+    public void testInflation() throws Throwable {
+        ViewStubProxy viewStubProxy = mBinder.viewStub;
+        assertFalse(viewStubProxy.isInflated());
+        assertNull(viewStubProxy.getBinding());
+        assertNotNull(viewStubProxy.getViewStub());
+        assertNull(mBinder.getRoot().findViewById(R.id.firstNameContents));
+        assertNull(mBinder.getRoot().findViewById(R.id.lastNameContents));
+        mBinder.setViewStubVisibility(View.VISIBLE);
+        mBinder.executePendingBindings();
+        assertTrue(viewStubProxy.isInflated());
+        assertNotNull(viewStubProxy.getBinding());
+        assertNull(viewStubProxy.getViewStub());
+        ViewStubContentsBinding contentsBinding = (ViewStubContentsBinding)
+                viewStubProxy.getBinding();
+        assertNotNull(contentsBinding.firstNameContents);
+        assertNotNull(contentsBinding.lastNameContents);
+        assertEquals("Hello", contentsBinding.firstNameContents.getText().toString());
+        assertEquals("World", contentsBinding.lastNameContents.getText().toString());
+    }
+
+    @UiThreadTest
+    public void testChangeValues() throws Throwable {
+        ViewStubProxy viewStubProxy = mBinder.viewStub;
+        mBinder.setViewStubVisibility(View.VISIBLE);
+        mBinder.executePendingBindings();
+        ViewStubContentsBinding contentsBinding = (ViewStubContentsBinding)
+                viewStubProxy.getBinding();
+        assertEquals("Hello", contentsBinding.firstNameContents.getText().toString());
+        mBinder.setFirstName("Goodbye");
+        mBinder.executePendingBindings();
+        assertEquals("Goodbye", contentsBinding.firstNameContents.getText().toString());
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java
new file mode 100644
index 0000000..7f38f3b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.databinding.testapp.multiconfig;
+
+import android.databinding.ViewDataBinding;
+import android.databinding.testapp.BaseLandDataBinderTest;
+import android.databinding.testapp.R;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+import android.databinding.testapp.databinding.ConditionalBindingBinding;
+import android.databinding.testapp.databinding.IncludedLayoutBinding;
+import android.databinding.testapp.databinding.MultiResLayoutBinding;
+import android.databinding.testapp.vo.NotBindableVo;
+
+import android.content.pm.ActivityInfo;
+import android.view.View;
+import android.widget.TextView;
+
+public class LandscapeConfigTest extends BaseLandDataBinderTest<MultiResLayoutBinding> {
+
+    public LandscapeConfigTest() {
+        super(MultiResLayoutBinding.class, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+    }
+
+    public void testSharedViewIdAndVariableInheritance()
+            throws InterruptedException, NoSuchMethodException, NoSuchFieldException {
+        assertEquals("MultiResLayoutBindingLandImpl", mBinder.getClass().getSimpleName());
+        assertPublicField(TextView.class, "objectInLandTextView");
+        assertPublicField(TextView.class, "objectInDefaultTextView");
+        assertPublicField(View.class, "objectInDefaultTextView2");
+
+        assertField(NotBindableVo.class, "mObjectInLand");
+        assertField(NotBindableVo.class, "mObjectInDefault");
+
+        // includes
+        assertPublicField(ViewDataBinding.class, "includedLayoutConflict");
+        assertPublicField(BasicBindingBinding.class, "includedLayoutShared");
+        assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort");
+        assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand");
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java
new file mode 100644
index 0000000..a4e4f2f
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.databinding.testapp.multiconfig;
+
+import android.databinding.ViewDataBinding;
+import android.databinding.testapp.BaseDataBinderTest;
+import android.databinding.testapp.databinding.BasicBindingBinding;
+import android.databinding.testapp.databinding.ConditionalBindingBinding;
+import android.databinding.testapp.databinding.IncludedLayoutBinding;
+import android.databinding.testapp.databinding.MultiResLayoutBinding;
+import android.databinding.testapp.vo.NotBindableVo;
+
+import android.content.pm.ActivityInfo;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+public class PortraitConfigTest extends BaseDataBinderTest<MultiResLayoutBinding> {
+    public PortraitConfigTest() {
+        super(MultiResLayoutBinding.class, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    public void testSharedViewIdAndVariableInheritance()
+            throws InterruptedException, NoSuchMethodException, NoSuchFieldException {
+        assertEquals("MultiResLayoutBindingImpl", mBinder.getClass().getSimpleName());
+        assertPublicField(TextView.class, "objectInLandTextView");
+        assertPublicField(TextView.class, "objectInDefaultTextView");
+        assertPublicField(View.class, "objectInDefaultTextView2");
+
+        assertField(NotBindableVo.class, "mObjectInDefault");
+
+        // includes
+        assertPublicField(ViewDataBinding.class, "includedLayoutConflict");
+        assertPublicField(BasicBindingBinding.class, "includedLayoutShared");
+        assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort");
+        assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand");
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c6f719e
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.databinding.testapp">
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name"
+                 android:icon="@drawable/ic_launcher"
+            >
+        <activity android:name=".TestActivity"
+                android:screenOrientation="portrait"/>
+    </application>
+
+</manifest>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java
new file mode 100644
index 0000000..d5f95e6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.databinding.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class TestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java
new file mode 100644
index 0000000..24bc067
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java
@@ -0,0 +1,47 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.graphics.drawable.ColorDrawable;
+
+public class AbsListViewBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private ColorDrawable mListSelector = new ColorDrawable(0xFFFF0000);
+    @Bindable
+    private boolean mScrollingCache;
+    @Bindable
+    private boolean mSmoothScrollbar;
+
+    public ColorDrawable getListSelector() {
+        return mListSelector;
+    }
+
+    public boolean isScrollingCache() {
+        return mScrollingCache;
+    }
+
+    public boolean isSmoothScrollbar() {
+        return mSmoothScrollbar;
+    }
+
+    public void changeValues() {
+        mListSelector = new ColorDrawable(0xFFFFFFFF);
+        mScrollingCache = true;
+        mSmoothScrollbar = true;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java
new file mode 100644
index 0000000..bc8c3cb
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class AbsSeekBarBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mThumbTint = 0xFFFF0000;
+
+    public int getThumbTint() {
+        return mThumbTint;
+    }
+
+    public void changeValues() {
+        mThumbTint = 0xFF00FF00;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java
new file mode 100644
index 0000000..05d7ed7
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class AbsSpinnerBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private CharSequence[] mEntries = {
+            "hello",
+            "world",
+    };
+
+    private static final CharSequence[] CHANGED_VALUES = {
+            "goodbye",
+            "cruel",
+            "world"
+    };
+
+    public CharSequence[] getEntries() {
+        return mEntries;
+    }
+
+    public void changeValues() {
+        mEntries = CHANGED_VALUES;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java
new file mode 100644
index 0000000..eef46d6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class AutoCompleteTextViewBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mPopupBackground;
+
+    @Bindable
+    private int mCompletionThreshold = 1;
+
+    public int getCompletionThreshold() {
+        return mCompletionThreshold;
+    }
+
+    public int getPopupBackground() {
+        return mPopupBackground;
+    }
+
+    public void changeValues() {
+        mPopupBackground = 0xFF23456;
+        mCompletionThreshold = 5;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java
new file mode 100644
index 0000000..450f7fb
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.databinding.testapp.vo;
+import android.databinding.BaseObservable;
+import android.databinding.Bindable;
+import android.databinding.testapp.BR;
+
+public class BasicObject extends BaseObservable {
+    @Bindable
+    private String mField1;
+    @Bindable
+    private String mField2;
+
+    public String getField1() {
+        return mField1;
+    }
+
+    public void setField1(String field1) {
+        this.mField1 = field1;
+        notifyPropertyChanged(BR.field1);
+    }
+
+    public String getField2() {
+        return mField2;
+    }
+
+    public void setField2(String field2) {
+        this.mField2 = field2;
+        notifyPropertyChanged(BR.field1);
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java
new file mode 100644
index 0000000..d297f8a
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class BindableTestObject {
+    @Bindable
+    public int bindableField1;
+
+    @Bindable
+    private int bindableField2;
+
+    private int bindableField3;
+
+    @Bindable
+    public int m_bindableField4;
+
+    @Bindable
+    public int mbindableField5;
+
+    @Bindable
+    public int _bindableField6;
+
+    @Bindable
+    public int _BindableField7;
+
+    @Bindable
+    public int mBindableField8;
+
+    public int getBindableField2() {
+        return bindableField2;
+    }
+
+    @Bindable
+    public int getBindableField3() {
+        return bindableField3;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java
new file mode 100644
index 0000000..404e104
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java
@@ -0,0 +1,23 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.BaseObservable;
+
+public abstract class BindingAdapterBindingObject extends BaseObservable {
+
+    public abstract void changeValues();
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java
new file mode 100644
index 0000000..75eba50
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.graphics.drawable.ColorDrawable;
+
+public class CheckedTextViewBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private ColorDrawable mCheckMark = new ColorDrawable(0xFF123456);
+
+    @Bindable
+    private int mCheckMarkTint = 0xDead_Beef;
+
+    public ColorDrawable getCheckMark() {
+        return mCheckMark;
+    }
+
+    public int getCheckMarkTint() {
+        return mCheckMarkTint;
+    }
+
+    public void changeValues() {
+        mCheckMark = new ColorDrawable(0xFF111111);
+        mCheckMarkTint = 0xFF222222;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java
new file mode 100644
index 0000000..bb13201
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class CompoundButtonBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mButtonTint;
+
+    public int getButtonTint() {
+        return mButtonTint;
+    }
+
+    public void changeValues() {
+        mButtonTint = 0xFF111111;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java
new file mode 100644
index 0000000..2281e223
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class FindMethodBindingObject extends FindMethodBindingObjectBase {
+    public String method() { return "no arg"; }
+
+    public String method(int i) { return String.valueOf(i); }
+
+    public String method(float f) { return String.valueOf(f); }
+
+    public String method(String value) { return value; }
+
+    public static String staticMethod() { return "world"; }
+
+    public static Foo foo = new Foo();
+
+    public static Bar<String> bar = new Bar<>();
+
+    public float confusingParam(int i) { return i; }
+    public String confusingParam(Object o) { return o.toString(); }
+
+    public int confusingPrimitive(int i) { return i; }
+    public String confusingPrimitive(Integer i) { return i.toString(); }
+
+    public float confusingInheritance(Object o) { return 0; }
+    public String confusingInheritance(String s) { return s; }
+    public int confusingInheritance(Integer i) { return i; }
+
+    public int confusingTypeArgs(List<String> s) { return 0; }
+    public String confusingTypeArgs(Map<String, String> s) { return "yay"; }
+
+    public ArrayMap<String, String> getMap() { return null; }
+
+    public List getList() {
+        ArrayList<String> vals = new ArrayList<>();
+        vals.add("hello");
+        return vals;
+    }
+
+    public static class Foo {
+        public final String bar = "hello world";
+    }
+
+    public static class Bar<T> {
+        public T method(T value) { return value; }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java
new file mode 100644
index 0000000..8678e1e
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+public class FindMethodBindingObjectBase extends BindingAdapterBindingObject {
+    public String inheritedMethod() {
+        return "base";
+    }
+
+    public String inheritedMethod(int i) {
+        return "base " + i;
+    }
+
+    @Override
+    public void changeValues() {
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java
new file mode 100644
index 0000000..a0fa6b7
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class FrameLayoutBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int foregroundTint;
+
+    public int getForegroundTint() {
+        return foregroundTint;
+    }
+
+    public void changeValues() {
+        foregroundTint = 0xFF111111;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java
new file mode 100644
index 0000000..926617e
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+
+public class ImageViewBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mTint;
+
+    @Bindable
+    private Drawable mSrc;
+
+    @Bindable
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.DARKEN;
+
+    public int getTint() {
+        return mTint;
+    }
+
+    public Drawable getSrc() {
+        return mSrc;
+    }
+
+    public PorterDuff.Mode getTintMode() {
+        return mTintMode;
+    }
+
+    public void changeValues() {
+        mTint = 0xFF111111;
+        mSrc = new ColorDrawable(0xFF00FF00);
+        mTintMode = PorterDuff.Mode.LIGHTEN;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java
new file mode 100644
index 0000000..f9e07c3
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class LinearLayoutBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mDivider;
+
+    @Bindable
+    private boolean mMeasureWithLargestChild;
+
+    public int getDivider() {
+        return mDivider;
+    }
+
+    public boolean isMeasureWithLargestChild() {
+        return mMeasureWithLargestChild;
+    }
+
+    public void changeValues() {
+        mDivider = 0xFF111111;
+        mMeasureWithLargestChild = true;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java
new file mode 100644
index 0000000..64d1a48
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+public class NotBindableVo {
+    private int mIntValue;
+    private int mIntValueGetCount;
+    private boolean mBoolValue;
+    private int mBoolValueGetCount;
+    private String mStringValue;
+    private int mStringValueGetCount;
+    private final String mFinalString = "this has final content";
+    public final int publicField = 3;
+
+    public NotBindableVo() {
+    }
+
+    public NotBindableVo(int intValue) {
+        this.mIntValue = intValue;
+    }
+
+    public NotBindableVo(String stringValue) {
+        this.mStringValue = stringValue;
+    }
+
+    public NotBindableVo(int intValue, String stringValue) {
+        this.mIntValue = intValue;
+        this.mStringValue = stringValue;
+    }
+
+    public int getIntValue() {
+        mIntValueGetCount ++;
+        return mIntValue;
+    }
+
+    public String getFinalString() {
+        return mFinalString;
+    }
+
+    public void setIntValue(int intValue) {
+        this.mIntValue = intValue;
+    }
+
+    public String getStringValue() {
+        mStringValueGetCount ++;
+        return mStringValue;
+    }
+
+    public void setStringValue(String stringValue) {
+        this.mStringValue = stringValue;
+    }
+
+    public String mergeStringFields(NotBindableVo other) {
+        return mStringValue + (other == null ? "" : other.mStringValue);
+    }
+
+    public boolean getBoolValue() {
+        mBoolValueGetCount ++;
+        return mBoolValue;
+    }
+
+    public void setBoolValue(boolean boolValue) {
+        mBoolValue = boolValue;
+    }
+
+    public int getIntValueGetCount() {
+        return mIntValueGetCount;
+    }
+
+    public int getBoolValueGetCount() {
+        return mBoolValueGetCount;
+    }
+
+    public int getStringValueGetCount() {
+        return mStringValueGetCount;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java
new file mode 100644
index 0000000..87127fe
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.ObservableBoolean;
+import android.databinding.ObservableByte;
+import android.databinding.ObservableChar;
+import android.databinding.ObservableDouble;
+import android.databinding.ObservableField;
+import android.databinding.ObservableFloat;
+import android.databinding.ObservableInt;
+import android.databinding.ObservableLong;
+import android.databinding.ObservableShort;
+
+public class ObservableFieldBindingObject {
+    public final ObservableBoolean bField = new ObservableBoolean();
+    public final ObservableByte tField = new ObservableByte();
+    public final ObservableShort sField = new ObservableShort();
+    public final ObservableChar cField = new ObservableChar();
+    public final ObservableInt iField = new ObservableInt();
+    public final ObservableLong lField = new ObservableLong();
+    public final ObservableFloat fField = new ObservableFloat();
+    public final ObservableDouble dField = new ObservableDouble();
+    public final ObservableField<String> oField = new ObservableField<>();
+
+    public ObservableFieldBindingObject() {
+        oField.set("Hello");
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java
new file mode 100644
index 0000000..ba33539
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.BaseObservable;
+
+public class ObservableWithNotBindableFieldObject extends BaseObservable {
+    private String data;
+    public void update(String data) {
+        this.data = data;
+        notifyChange();
+    }
+
+    public String getData() {
+        return data;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java
new file mode 100644
index 0000000..8a18aba
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class ProgressBarBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mIndeterminateTint;
+
+    @Bindable
+    private int mProgressTint;
+
+    @Bindable
+    private int mSecondaryProgressTint;
+
+    public int getIndeterminateTint() {
+        return mIndeterminateTint;
+    }
+
+    public int getProgressTint() {
+        return mProgressTint;
+    }
+
+    public int getSecondaryProgressTint() {
+        return mSecondaryProgressTint;
+    }
+
+    public void changeValues() {
+        mIndeterminateTint = 0xFF111111;
+        mProgressTint = 0xFF222222;
+        mSecondaryProgressTint = 0xFF333333;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java
new file mode 100644
index 0000000..03135e3
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java
@@ -0,0 +1,21 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+public class PublicFinalTestVo {
+    public final int myField;
+    public PublicFinalTestVo(int field) {
+        myField = field;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java
new file mode 100644
index 0000000..8d6f4317
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.BaseObservable;
+import android.databinding.Bindable;
+import android.databinding.testapp.BR;
+import android.databinding.testapp.R;
+
+public class PublicFinalWithObservableTestVo {
+    public final int myField;
+    public final MyVo myFinalVo = new MyVo();
+
+    public PublicFinalWithObservableTestVo(int field) {
+        myField = field;
+    }
+
+    public static class MyVo extends BaseObservable {
+        @Bindable
+        private int val = R.string.app_name;
+
+        public int getVal() {
+            return val;
+        }
+
+        public void setVal(int val) {
+            this.val = val;
+            notifyPropertyChanged(BR.val);
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java
new file mode 100644
index 0000000..ceacd20
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.databinding.testapp.R;
+
+public class RadioGroupBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mCheckedButton = R.id.choiceOne;
+
+    public int getCheckedButton() {
+        return mCheckedButton;
+    }
+
+    public void changeValues() {
+        mCheckedButton = R.id.choiceTwo;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java
new file mode 100644
index 0000000..c2ac72f
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class SpinnerBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mPopupBackground = 0xFF123456;
+
+    public int getPopupBackground() {
+        return mPopupBackground;
+    }
+
+    public void changeValues() {
+        mPopupBackground = 0xFF111111;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java
new file mode 100644
index 0000000..86ea227
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class SwitchBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mThumb;
+    @Bindable
+    private int mTrack;
+
+    public int getThumb() {
+        return mThumb;
+    }
+
+    public int getTrack() {
+        return mTrack;
+    }
+
+    public void changeValues() {
+        mThumb = 0xFF111111;
+        mTrack = 0xFF333333;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java
new file mode 100644
index 0000000..2ce8681
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.graphics.drawable.ColorDrawable;
+
+public class TabWidgetBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private ColorDrawable mDivider = new ColorDrawable(0xFF0000FF);
+    @Bindable
+    private boolean mTabStripEnabled;
+    @Bindable
+    private ColorDrawable mTabStripLeft = new ColorDrawable(0xFF00FF00);
+    @Bindable
+    private ColorDrawable mTabStripRight = new ColorDrawable(0xFFFF0000);
+
+    public ColorDrawable getDivider() {
+        return mDivider;
+    }
+
+    public ColorDrawable getTabStripLeft() {
+        return mTabStripLeft;
+    }
+
+    public ColorDrawable getTabStripRight() {
+        return mTabStripRight;
+    }
+
+    public boolean isTabStripEnabled() {
+        return mTabStripEnabled;
+    }
+
+    public void changeValues() {
+        mDivider = new ColorDrawable(0xFF111111);
+        mTabStripEnabled = true;
+        mTabStripLeft = new ColorDrawable(0xFF222222);
+        mTabStripRight = new ColorDrawable(0xFF333333);
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java
new file mode 100644
index 0000000..34f7e8d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class TableLayoutBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private String mCollapseColumns = "1";
+    @Bindable
+    private String mShrinkColumns = "1";
+    @Bindable
+    private String mStretchColumns = "1";
+    @Bindable
+    private int mDivider = 0xFF112233;
+
+    public String getCollapseColumns() {
+        return mCollapseColumns;
+    }
+
+    public String getShrinkColumns() {
+        return mShrinkColumns;
+    }
+
+    public String getStretchColumns() {
+        return mStretchColumns;
+    }
+
+    public int getDivider() {
+        return mDivider;
+    }
+
+    public void changeValues() {
+        mCollapseColumns = "";
+        mShrinkColumns = "1,0";
+        mStretchColumns = "*";
+        mDivider = 0xFF445566;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java
new file mode 100644
index 0000000..b1d04b6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java
@@ -0,0 +1,320 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.databinding.adapters.TextViewBindingAdapter;
+import android.databinding.testapp.BR;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.method.KeyListener;
+import android.text.method.TextKeyListener;
+import android.text.util.Linkify;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.TextView;
+
+public class TextViewBindingObject extends BindingAdapterBindingObject {
+
+    @Bindable
+    private int mAutoLink = Linkify.WEB_URLS;
+
+    @Bindable
+    private int mDrawablePadding;
+
+    @Bindable
+    private int mInputType = InputType.TYPE_CLASS_PHONE;
+
+    @Bindable
+    private boolean mScrollHorizontally;
+
+    @Bindable
+    private boolean mTextAllCaps;
+
+    @Bindable
+    private int mTextColorHighlight;
+
+    @Bindable
+    private int mTextColorHint;
+
+    @Bindable
+    private int mTextColorLink;
+
+    @Bindable
+    private boolean mAutoText;
+
+    @Bindable
+    private TextKeyListener.Capitalize mCapitalize = TextKeyListener.Capitalize.NONE;
+
+    @Bindable
+    private TextView.BufferType mBufferType = TextView.BufferType.NORMAL;
+
+    @Bindable
+    private String mDigits = "abcdefg";
+
+    @Bindable
+    private int mNumeric = TextViewBindingAdapter.DECIMAL;
+
+    @Bindable
+    private boolean mPhoneNumber;
+
+    @Bindable
+    private int mDrawableBottom;
+
+    @Bindable
+    private int mDrawableTop;
+
+    @Bindable
+    private int mDrawableLeft;
+
+    @Bindable
+    private int mDrawableRight;
+
+    @Bindable
+    private int mDrawableStart;
+
+    @Bindable
+    private int mDrawableEnd;
+
+    @Bindable
+    private String mImeActionLabel;
+
+    @Bindable
+    private int mImeActionId;
+
+    @Bindable
+    private String mInputMethod
+            = "android.databinding.testapp.vo.TextViewBindingObject$KeyListener1";
+
+    @Bindable
+    private float mLineSpacingExtra;
+
+    @Bindable
+    private float mLineSpacingMultiplier;
+
+    @Bindable
+    private int mMaxLength;
+
+    @Bindable
+    private int mShadowColor;
+
+    @Bindable
+    private float mShadowDx;
+
+    @Bindable
+    private float mShadowDy;
+
+    @Bindable
+    private float mShadowRadius;
+
+    @Bindable
+    private float mTextSize = 10f;
+
+    public TextView.BufferType getBufferType() {
+        return mBufferType;
+    }
+
+    public float getLineSpacingExtra() {
+        return mLineSpacingExtra;
+    }
+
+    public float getLineSpacingMultiplier() {
+        return mLineSpacingMultiplier;
+    }
+
+    public float getShadowDx() {
+        return mShadowDx;
+    }
+
+    public float getShadowDy() {
+        return mShadowDy;
+    }
+
+    public float getShadowRadius() {
+        return mShadowRadius;
+    }
+
+    public float getTextSize() {
+        return mTextSize;
+    }
+
+    public int getAutoLink() {
+        return mAutoLink;
+    }
+
+    public int getDrawableBottom() {
+        return mDrawableBottom;
+    }
+
+    public int getDrawableEnd() {
+        return mDrawableEnd;
+    }
+
+    public int getDrawableLeft() {
+        return mDrawableLeft;
+    }
+
+    public int getDrawablePadding() {
+        return mDrawablePadding;
+    }
+
+    public int getDrawableRight() {
+        return mDrawableRight;
+    }
+
+    public int getDrawableStart() {
+        return mDrawableStart;
+    }
+
+    public int getDrawableTop() {
+        return mDrawableTop;
+    }
+
+    public int getImeActionId() {
+        return mImeActionId;
+    }
+
+    public int getInputType() {
+        return mInputType;
+    }
+
+    public int getMaxLength() {
+        return mMaxLength;
+    }
+
+    public int getNumeric() {
+        return mNumeric;
+    }
+
+    public int getShadowColor() {
+        return mShadowColor;
+    }
+
+    public int getTextColorHighlight() {
+        return mTextColorHighlight;
+    }
+
+    public int getTextColorHint() {
+        return mTextColorHint;
+    }
+
+    public int getTextColorLink() {
+        return mTextColorLink;
+    }
+
+    public String getDigits() {
+        return mDigits;
+    }
+
+    public String getImeActionLabel() {
+        return mImeActionLabel;
+    }
+
+    public String getInputMethod() {
+        return mInputMethod;
+    }
+
+    public boolean isAutoText() {
+        return mAutoText;
+    }
+
+    public TextKeyListener.Capitalize getCapitalize() {
+        return mCapitalize;
+    }
+
+    public void setCapitalize(TextKeyListener.Capitalize capitalize) {
+        mCapitalize = capitalize;
+        notifyPropertyChanged(BR.capitalize);
+    }
+
+    public boolean isPhoneNumber() {
+        return mPhoneNumber;
+    }
+
+    public boolean isScrollHorizontally() {
+        return mScrollHorizontally;
+    }
+
+    public boolean isTextAllCaps() {
+        return mTextAllCaps;
+    }
+
+    public void changeValues() {
+        mAutoLink = Linkify.EMAIL_ADDRESSES;
+        mDrawablePadding = 10;
+        mInputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS;
+        mScrollHorizontally = true;
+        mTextAllCaps = true;
+        mTextColorHighlight = 0xFF00FF00;
+        mTextColorHint = 0xFFFF0000;
+        mTextColorLink = 0xFF0000FF;
+        mAutoText = true;
+        mCapitalize = TextKeyListener.Capitalize.SENTENCES;
+        mBufferType = TextView.BufferType.SPANNABLE;
+        mDigits = "hijklmno";
+        mNumeric = TextViewBindingAdapter.SIGNED;
+        mPhoneNumber = true;
+        mDrawableBottom = 0xFF880088;
+        mDrawableTop = 0xFF111111;
+        mDrawableLeft = 0xFF222222;
+        mDrawableRight = 0xFF333333;
+        mDrawableStart = 0xFF444444;
+        mDrawableEnd = 0xFF555555;
+        mImeActionLabel = "Hello World";
+        mImeActionId = 3;
+        mInputMethod = "android.databinding.testapp.vo.TextViewBindingObject$KeyListener2";
+        mLineSpacingExtra = 2;
+        mLineSpacingMultiplier = 3;
+        mMaxLength = 100;
+        mShadowColor = 0xFF666666;
+        mShadowDx = 2;
+        mShadowDy = 3;
+        mShadowRadius = 4;
+        mTextSize = 20f;
+        notifyChange();
+    }
+
+    public static class KeyListener1 implements KeyListener {
+
+        @Override
+        public int getInputType() {
+            return InputType.TYPE_CLASS_TEXT;
+        }
+
+        @Override
+        public boolean onKeyDown(View view, Editable text, int keyCode, KeyEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean onKeyUp(View view, Editable text, int keyCode, KeyEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean onKeyOther(View view, Editable text, KeyEvent event) {
+            return false;
+        }
+
+        @Override
+        public void clearMetaKeyState(View view, Editable content, int states) {
+        }
+    }
+
+    public static class KeyListener2 extends KeyListener1 {
+
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java
new file mode 100644
index 0000000..1107265
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java
@@ -0,0 +1,36 @@
+package android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class User {
+    @Bindable
+    private User friend;
+    @Bindable
+    private String name;
+    @Bindable
+    private String fullName;
+
+    public User getFriend() {
+        return friend;
+    }
+
+    public void setFriend(User friend) {
+        this.friend = friend;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java
new file mode 100644
index 0000000..a955e81
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java
@@ -0,0 +1,175 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.databinding.adapters.ViewBindingAdapter;
+import android.databinding.testapp.R;
+import android.view.View;
+
+public class ViewBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mBackgroundTint = 0xFF00FF00;
+    @Bindable
+    private boolean mFadeScrollbars = false;
+    @Bindable
+    private int mNextFocusForward = R.id.padding;
+    @Bindable
+    private int mNextFocusLeft = R.id.paddingStartEnd;
+    @Bindable
+    private int mNextFocusRight = R.id.paddingTopBottom;
+    @Bindable
+    private int mNextFocusUp = R.id.backgroundTint;
+    @Bindable
+    private int mNextFocusDown = R.id.fadeScrollbars;
+    @Bindable
+    private int mRequiresFadingEdge = ViewBindingAdapter.FADING_EDGE_VERTICAL;
+    @Bindable
+    private int mScrollbarDefaultDelayBeforeFade = 300;
+    @Bindable
+    private int mScrollbarFadeDuration = 400;
+    @Bindable
+    private int mScrollbarSize = 10;
+    @Bindable
+    private int mScrollbarStyle = View.SCROLLBARS_INSIDE_OVERLAY;
+    @Bindable
+    private float mTransformPivotX = 9;
+    @Bindable
+    private float mTransformPivotY = 8;
+    @Bindable
+    private int mPadding = 11;
+    @Bindable
+    private int mPaddingBottom = 12;
+    @Bindable
+    private int mPaddingTop = 13;
+    @Bindable
+    private int mPaddingLeft = 14;
+    @Bindable
+    private int mPaddingRight = 15;
+    @Bindable
+    private int mPaddingStart = 16;
+    @Bindable
+    private int mPaddingEnd = 17;
+
+    public int getBackgroundTint() {
+        return mBackgroundTint;
+    }
+
+    public int getScrollbarFadeDuration() {
+        return mScrollbarFadeDuration;
+    }
+
+    public boolean getFadeScrollbars() {
+        return mFadeScrollbars;
+    }
+
+    public int getNextFocusDown() {
+        return mNextFocusDown;
+    }
+
+    public int getNextFocusForward() {
+        return mNextFocusForward;
+    }
+
+    public int getNextFocusLeft() {
+        return mNextFocusLeft;
+    }
+
+    public int getNextFocusRight() {
+        return mNextFocusRight;
+    }
+
+    public int getNextFocusUp() {
+        return mNextFocusUp;
+    }
+
+    public int getRequiresFadingEdge() {
+        return mRequiresFadingEdge;
+    }
+
+    public int getScrollbarDefaultDelayBeforeFade() {
+        return mScrollbarDefaultDelayBeforeFade;
+    }
+
+    public int getScrollbarSize() {
+        return mScrollbarSize;
+    }
+
+    public int getScrollbarStyle() {
+        return mScrollbarStyle;
+    }
+
+    public float getTransformPivotX() {
+        return mTransformPivotX;
+    }
+
+    public float getTransformPivotY() {
+        return mTransformPivotY;
+    }
+
+    public int getPadding() {
+        return mPadding;
+    }
+
+    public int getPaddingBottom() {
+        return mPaddingBottom;
+    }
+
+    public int getPaddingEnd() {
+        return mPaddingEnd;
+    }
+
+    public int getPaddingLeft() {
+        return mPaddingLeft;
+    }
+
+    public int getPaddingRight() {
+        return mPaddingRight;
+    }
+
+    public int getPaddingStart() {
+        return mPaddingStart;
+    }
+
+    public int getPaddingTop() {
+        return mPaddingTop;
+    }
+
+    public void changeValues() {
+        mBackgroundTint = 0xFFFF0000;
+        mFadeScrollbars = true;
+        mNextFocusForward = R.id.paddingStartEnd;
+        mNextFocusLeft = R.id.paddingTopBottom;
+        mNextFocusRight = R.id.backgroundTint;
+        mNextFocusUp = R.id.fadeScrollbars;
+        mNextFocusDown = R.id.padding;
+        mRequiresFadingEdge = ViewBindingAdapter.FADING_EDGE_HORIZONTAL;
+        mScrollbarDefaultDelayBeforeFade = 400;
+        mScrollbarFadeDuration = 500;
+        mScrollbarSize = 11;
+        mScrollbarStyle = View.SCROLLBARS_INSIDE_INSET;
+        mTransformPivotX = 7;
+        mTransformPivotY = 6;
+        mPadding = 110;
+        mPaddingBottom = 120;
+        mPaddingTop = 130;
+        mPaddingLeft = 140;
+        mPaddingRight = 150;
+        mPaddingStart = 160;
+        mPaddingEnd = 170;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java
new file mode 100644
index 0000000..107fec6
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+
+public class ViewGroupBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private boolean mAlwaysDrawnWithCache;
+    @Bindable
+    private boolean mAnimationCache;
+    @Bindable
+    private boolean mSplitMotionEvents;
+    @Bindable
+    private boolean mAnimateLayoutChanges;
+
+    public boolean isAlwaysDrawnWithCache() {
+        return mAlwaysDrawnWithCache;
+    }
+
+    public boolean isAnimationCache() {
+        return mAnimationCache;
+    }
+
+    public boolean isSplitMotionEvents() {
+        return mSplitMotionEvents;
+    }
+
+    public boolean isAnimateLayoutChanges() {
+        return mAnimateLayoutChanges;
+    }
+
+    public void changeValues() {
+        mAlwaysDrawnWithCache = true;
+        mAnimationCache = true;
+        mAnimateLayoutChanges = true;
+        mSplitMotionEvents = true;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java
new file mode 100644
index 0000000..3e88e91
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.databinding.testapp.vo;
+
+import android.databinding.Bindable;
+import android.databinding.testapp.R;
+
+public class ViewStubBindingObject extends BindingAdapterBindingObject {
+    @Bindable
+    private int mLayout = R.layout.table_layout_adapter_test;
+
+    public int getLayout() {
+        return mLayout;
+    }
+
+    public void changeValues() {
+        mLayout = R.layout.auto_complete_text_view_adapter_test;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
new file mode 100644
index 0000000..594c3fe
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="objectInLand" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/objectInLandTextView"
+              android:text="@{objectInLand.stringValue}"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/objectInDefaultTextView"
+              android:text="@{objectInDefault.stringValue}"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/objectInDefaultTextView2"
+              android:text="@{objectInDefault.stringValue}"/>
+
+    <include layout="@layout/included_layout" android:id="@+id/includedLayoutConflict"
+             bind:innerObject="@{objectInLand}"
+             bind:innerValue='@{"modified " + objectInLand.intValue}' />
+    <include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared"
+             bind:a="@{objectInDefault.stringValue}"
+            />
+    <include layout="@layout/conditional_binding" android:id="@+id/includedLayoutLand"
+             bind:obj2="@{objectInDefault}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml
new file mode 100644
index 0000000..4ff0ba8
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/view"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:listSelector="@{obj.listSelector}"
+          android:scrollingCache="@{obj.scrollingCache}"
+          android:smoothScrollbar="@{obj.smoothScrollbar}"
+        >
+    <variable name="obj" type="android.databinding.testapp.vo.AbsListViewBindingObject"/>
+</ListView>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml
new file mode 100644
index 0000000..0fafb2d
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml
@@ -0,0 +1,10 @@
+<?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="obj" type="android.databinding.testapp.vo.AbsSeekBarBindingObject"/>
+    <SeekBar android:layout_width="match_parent" android:layout_height="match_parent"
+             android:id="@+id/view"
+             android:thumbTint="@{obj.thumbTint}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml
new file mode 100644
index 0000000..047b107
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml
@@ -0,0 +1,10 @@
+<?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="obj" type="android.databinding.testapp.vo.AbsSpinnerBindingObject"/>
+    <Spinner android:layout_width="match_parent" android:layout_height="match_parent"
+             android:id="@+id/view"
+            android:entries="@{obj.entries}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml
new file mode 100644
index 0000000..b999841
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml
@@ -0,0 +1,11 @@
+<?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="obj" type="android.databinding.testapp.vo.AutoCompleteTextViewBindingObject"/>
+    <AutoCompleteTextView android:layout_width="match_parent" android:layout_height="match_parent"
+                          android:id="@+id/view"
+                          android:completionThreshold="@{obj.completionThreshold}"
+                          android:popupBackground="@{obj.popupBackground}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml
new file mode 100644
index 0000000..6e75fb2
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="a" type="String"/>
+    <variable name="b" type="String"/>
+    <TextView
+            android:id="@+id/textView"
+            android:text="@{a + b}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml
new file mode 100644
index 0000000..453348a
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="obj1" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <variable name="obj2" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <TextView
+            android:id="@+id/textView1"
+            android:text="@{obj1.stringValue}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    <TextView
+            android:id="@+id/textView2"
+            android:text="@{obj2.stringValue}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    <TextView
+            android:id="@+id/mergedTextView1"
+            android:text="@{obj1.mergeStringFields(obj2)}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    <TextView
+            android:id="@+id/mergedTextView2"
+            android:text="@{obj2.mergeStringFields(obj1)}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+    <TextView
+            android:id="@+id/rawStringMerge"
+            android:text="@{obj1.stringValue + obj2.stringValue}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml
new file mode 100644
index 0000000..f578585
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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="obj" type="android.databinding.testapp.vo.PublicFinalTestVo"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:id="@+id/text_view"
+            android:text="@{obj.myField}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml
new file mode 100644
index 0000000..15d62fe
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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="obj" type="android.databinding.testapp.vo.PublicFinalWithObservableTestVo"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/text_view"
+              android:text="@{obj.myFinalVo.val}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml
new file mode 100644
index 0000000..49c2872
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:orientation="vertical"
+        >
+    <variable name="array" type="String[]"/>
+    <variable name="sparseArray" type="android.util.SparseArray&lt;String>"/>
+    <variable name="sparseBooleanArray" type="android.util.SparseBooleanArray"/>
+    <variable name="sparseIntArray" type="android.util.SparseIntArray"/>
+    <variable name="sparseLongArray" type="android.util.SparseLongArray"/>
+    <variable name="longSparseArray" type="android.util.LongSparseArray&lt;String>"/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/arrayText"
+              android:text="@{array[0]}"/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/sparseArrayText"
+              android:text='@{sparseArray[0]}'/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/sparseBooleanArrayText"
+              android:text='@{"" + sparseBooleanArray[0]}'/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/sparseIntArrayText"
+              android:text='@{"" + sparseIntArray[0]}'/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/sparseLongArrayText"
+              android:text='@{"" + sparseLongArray[0]}'/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/longSparseArrayText"
+              android:text='@{longSparseArray[0]}'/>
+
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml
new file mode 100644
index 0000000..803b5ee
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml
@@ -0,0 +1,20 @@
+<?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">
+    <import type="java.util.Collection"/>
+    <import type="java.util.ArrayList"/>
+    <import type="java.util.Map"/>
+    <variable name="list" type="Collection&lt;String&gt;"/>
+    <variable name="map" type="Object"/>
+
+    <TextView
+            android:id="@+id/textView0"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{((ArrayList&lt;String&gt;)list)[0]}"/>
+    <TextView
+            android:id="@+id/textView1"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{((Map&lt;String, String&gt;)map)[`hello`]}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml
new file mode 100644
index 0000000..02c50c8
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml
@@ -0,0 +1,13 @@
+<?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="obj" type="android.databinding.testapp.vo.CheckedTextViewBindingObject"/>
+    <CheckedTextView
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:checkMark="@{obj.checkMark}"
+            android:checkMarkTint="@{obj.checkMarkTint}"/>
+
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml
new file mode 100644
index 0000000..6f61825
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml
@@ -0,0 +1,11 @@
+<?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="obj" type="android.databinding.testapp.vo.CompoundButtonBindingObject"/>
+    <CheckBox
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:buttonTint="@{obj.buttonTint}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml
new file mode 100644
index 0000000..70376a0
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="obj1" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <variable name="obj2" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <variable name="obj3" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <variable name="cond1" type="boolean"/>
+    <variable name="cond2" type="boolean"/>
+    <TextView
+            android:id="@+id/textView"
+            android:text="@{cond1 ? cond2 ? obj1.stringValue : obj2.stringValue : obj3.stringValue}"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml
new file mode 100644
index 0000000..33a3746
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml
@@ -0,0 +1,102 @@
+<?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="obj" type="android.databinding.testapp.vo.FindMethodBindingObject"/>
+    <import type="android.databinding.testapp.vo.FindMethodBindingObject.Bar"/>
+    <variable name="obj2" type="Bar&lt;String&gt;"/>
+    <import type="android.databinding.testapp.vo.FindMethodBindingObject"/>
+    <import type="android.databinding.testapp.vo.FindMethodBindingObject" alias="FMBO"/>
+    <TextView
+            android:id="@+id/textView0"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.method(1)}"/>
+    <TextView
+            android:id="@+id/textView1"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.method(1.25f)}"/>
+    <TextView
+            android:id="@+id/textView2"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.method(`hello`)}"/>
+    <TextView
+            android:id="@+id/textView3"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.method((java.lang.Integer) 1)}"/>
+    <TextView
+            android:id="@+id/textView4"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.inheritedMethod()}"/>
+    <TextView
+            android:id="@+id/textView5"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.inheritedMethod(2)}"/>
+    <TextView
+            android:id="@+id/textView6"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.method()}"/>
+    <TextView
+            android:id="@+id/textView7"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{android.databinding.testapp.vo.FindMethodBindingObject.staticMethod()}"/>
+    <TextView
+            android:id="@+id/textView8"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{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}"/>
+    <!-- Imported classes -->
+    <TextView
+            android:id="@+id/textView15"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj2.method(`hello`)}"/>
+    <!-- confusing parameters may interfere with compile step -->
+    <TextView
+            android:id="@+id/textView16"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.confusingPrimitive((Integer)1)}"/>
+    <TextView
+            android:id="@+id/textView17"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{`` + (obj.confusingPrimitive(2) / 2)}"/>
+    <TextView
+            android:id="@+id/textView18"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.confusingInheritance(`hello`)}"/>
+    <TextView
+            android:id="@+id/textView19"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.confusingTypeArgs(obj.getMap())}"/>
+    <TextView
+            android:id="@+id/textView20"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.confusingParam(`hello`)}"/>
+    <TextView
+            android:id="@+id/textView21"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{obj.getList()[0]}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml
new file mode 100644
index 0000000..7c9b860
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml
@@ -0,0 +1,11 @@
+<?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="obj" type="android.databinding.testapp.vo.FrameLayoutBindingObject"/>
+    <FrameLayout
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:foregroundTint="@{obj.foregroundTint}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml
new file mode 100644
index 0000000..a3ce7f4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml
@@ -0,0 +1,14 @@
+<?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="obj" type="android.databinding.testapp.vo.ImageViewBindingObject"/>
+    <ImageView
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:src="@{obj.src}"
+            android:tint="@{obj.tint}"
+            android:tintMode="@{obj.tintMode}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml
new file mode 100644
index 0000000..5b0bf4c
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="innerObject" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <variable name="innerValue" type="java.lang.String"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/innerTextView"
+              android:text="@{innerValue + innerObject.stringValue}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml
new file mode 100644
index 0000000..2ad1980
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml
@@ -0,0 +1,11 @@
+<?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="obj" type="android.databinding.testapp.vo.BasicObject"/>
+    <TextView
+        android:id="@+id/text_view"
+        android:text='@{obj.field1 + " " + (obj.field2 ?? "")}'
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml
new file mode 100644
index 0000000..6504e4c
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="outerObject" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/outerTextView"
+              android:text="@{outerObject.stringValue}"/>
+    <!-- TODO test id collision-->
+    <include layout="@layout/included_layout" android:id="@+id/includedLayout"
+             bind:innerObject="@{outerObject}"
+             bind:innerValue="@{`modified ` + outerObject.intValue}"
+            />
+    <include layout="@layout/plain_layout" android:id="@+id/plainLayout"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml
new file mode 100644
index 0000000..3dbf2f5
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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="name" type="String"/>
+    <TextView
+            android:id="@+id/textView"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{name}"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml
new file mode 100644
index 0000000..0b08f17
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml
@@ -0,0 +1,14 @@
+<?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="obj" type="android.databinding.testapp.vo.LinearLayoutBindingObject"/>
+    <LinearLayout
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:orientation="horizontal"
+            android:divider="@{obj.divider}"
+            android:measureWithLargestChild="@{obj.measureWithLargestChild}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml
new file mode 100644
index 0000000..99c7d817
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:bind="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="objectInDefault" type="android.databinding.testapp.vo.NotBindableVo"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/objectInDefaultTextView"
+              android:text="@{objectInDefault.stringValue}"/>
+    <EditText android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:id="@+id/objectInDefaultTextView2"
+              android:text="@{objectInDefault.stringValue}"/>
+
+    <include layout="@layout/basic_binding" android:id="@+id/includedLayoutConflict"
+             bind:a="@{objectInDefault.stringValue}"
+            />
+    <include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared"
+             bind:a="@{objectInDefault.stringValue}"
+            />
+    <include layout="@layout/conditional_binding" android:id="@+id/includedLayoutPort"
+             bind:cond1="@{objectInDefault == null}"
+            />
+
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml
new file mode 100644
index 0000000..26c4e95
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/myContainer"
+              android:addChildrenForAccessibility="@{children}"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="elevation" type="float"/>
+    <variable name="name" type="java.lang.String"/>
+    <variable name="children" type="java.util.ArrayList&lt;android.view.View>"/>
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/textView"
+              android:text="@{name}" android:elevation="@{elevation}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml
new file mode 100644
index 0000000..32f06f4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:orientation="@{orientation}"
+        >
+    <variable name="name" type="String"/>
+    <variable name="orientation" type="int"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="@{name}" android:tag="hello world"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{name}"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{name}" android:tag="@string/app_name"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{name}" android:tag="@android:string/ok"/>
+    <TextView android:id="@+id/textView"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="hello"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml
new file mode 100644
index 0000000..1db044f
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml
@@ -0,0 +1,52 @@
+<?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="obj" type="android.databinding.testapp.vo.ObservableFieldBindingObject"/>
+    <TextView
+            android:id="@+id/bField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.bField}"/>
+    <TextView
+            android:id="@+id/tField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.tField}"/>
+    <TextView
+            android:id="@+id/sField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.sField}"/>
+    <TextView
+            android:id="@+id/cField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.cField}"/>
+    <TextView
+            android:id="@+id/iField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.iField}"/>
+    <TextView
+            android:id="@+id/lField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.lField}"/>
+    <TextView
+            android:id="@+id/fField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.fField}"/>
+    <TextView
+            android:id="@+id/dField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + obj.dField}"/>
+    <TextView
+            android:id="@+id/oField"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{obj.oField}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml
new file mode 100644
index 0000000..bde2dd4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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="obj" type="android.databinding.testapp.vo.ObservableWithNotBindableFieldObject"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:id="@+id/text_view"
+            android:text="@{obj.data}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml
new file mode 100644
index 0000000..2132370
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"/>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml
new file mode 100644
index 0000000..4fb8235
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml
@@ -0,0 +1,14 @@
+<?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="obj" type="android.databinding.testapp.vo.ProgressBarBindingObject"/>
+    <ProgressBar
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:indeterminateTint="@{obj.indeterminateTint}"
+            android:progressTint="@{obj.progressTint}"
+            android:secondaryProgressTint="@{obj.secondaryProgressTint}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml
new file mode 100644
index 0000000..efb6038
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml
@@ -0,0 +1,18 @@
+<?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="obj" type="android.databinding.testapp.vo.RadioGroupBindingObject"/>
+    <RadioGroup
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:checkedButton="@{obj.checkedButton}"
+            >
+        <RadioButton android:layout_width="match_parent" android:layout_height="wrap_content"
+                     android:text="One" android:id="@+id/choiceOne"/>
+        <RadioButton android:layout_width="match_parent" android:layout_height="wrap_content"
+                     android:text="Two" android:id="@+id/choiceTwo"/>
+    </RadioGroup>
+
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml
new file mode 100644
index 0000000..a50cda2
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml
@@ -0,0 +1,12 @@
+<?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="user" type="android.databinding.testapp.vo.User"/>
+    <TextView
+        android:id="@+id/text_view"
+        android:text='@{user.friend == null ? "?" : (user.friend.name + "is friend of " + user.name)}'
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml
new file mode 100644
index 0000000..59ecc35
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/view"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:orientation="vertical"
+        >
+    <variable name="count" type="int"/>
+    <variable name="title" type="String"/>
+    <variable name="lastName" type="String"/>
+    <variable name="base" type="int"/>
+    <variable name="pbase" type="int"/>
+
+    <TextView
+            android:id="@+id/textView0"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{@string/nameWithTitle(title, lastName)}"/>
+
+    <TextView
+            android:id="@+id/textView1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{@plurals/orange(count)}"/>
+    <TextView
+            android:id="@+id/fractionNoParameters"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + @fraction/myFraction}"/>
+    <TextView
+            android:id="@+id/fractionOneParameter"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + @fraction/myFraction(base)}"/>
+    <TextView
+            android:id="@+id/fractionTwoParameters"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{`` + @fraction/myParentFraction(base, pbase)}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml
new file mode 100644
index 0000000..a82e7cea
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml
@@ -0,0 +1,12 @@
+<?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="obj" type="android.databinding.testapp.vo.SpinnerBindingObject"/>
+    <Spinner
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:popupBackground="@{obj.popupBackground}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml
new file mode 100644
index 0000000..4bb1b84
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml
@@ -0,0 +1,13 @@
+<?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="obj" type="android.databinding.testapp.vo.SwitchBindingObject"/>
+    <Switch
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:thumb="@{obj.thumb}"
+            android:track="@{obj.track}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml
new file mode 100644
index 0000000..4167ae4
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml
@@ -0,0 +1,14 @@
+<?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="obj" type="android.databinding.testapp.vo.TabWidgetBindingObject"/>
+    <TabWidget android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:divider="@{obj.divider}"
+            android:tabStripEnabled="@{obj.tabStripEnabled}"
+            android:tabStripLeft="@{obj.tabStripLeft}"
+            android:tabStripRight="@{obj.tabStripRight}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml
new file mode 100644
index 0000000..8d53bce
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml
@@ -0,0 +1,32 @@
+<?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="obj" type="android.databinding.testapp.vo.TableLayoutBindingObject"/>
+    <TableLayout
+            android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:divider="@{obj.divider}"
+            android:collapseColumns="@{obj.collapseColumns}"
+            android:shrinkColumns="@{obj.shrinkColumns}"
+            android:stretchColumns="@{obj.stretchColumns}"
+            >
+        <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content">
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                    android:text="Hello"/>
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                      android:text="Happy"/>
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                      android:text="World"/>
+        </TableRow>
+        <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content">
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                      android:text="Goodbye"/>
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                      android:text="Cruel"/>
+            <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                      android:text="World"/>
+        </TableRow>
+    </TableLayout>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml
new file mode 100644
index 0000000..dc6fd29
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml
@@ -0,0 +1,69 @@
+<?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="obj" type="android.databinding.testapp.vo.TextViewBindingObject"/>
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/numericText"
+               android:numeric="@{obj.numeric}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textDrawableNormal"
+               android:drawableBottom="@{obj.drawableBottom}"
+               android:drawableLeft="@{obj.drawableLeft}"
+               android:drawableRight="@{obj.drawableRight}"
+               android:drawableTop="@{obj.drawableTop}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textDrawableStartEnd"
+               android:drawableStart="@{obj.drawableStart}"
+               android:drawableEnd="@{obj.drawableEnd}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textView"
+               android:autoLink="@{obj.autoLink}"
+               android:drawablePadding="@{obj.drawablePadding}"
+               android:scrollHorizontally="@{obj.scrollHorizontally}"
+               android:textColorHighlight="@{obj.textColorHighlight}"
+               android:textColorHint="@{obj.textColorHint}"
+               android:textColorLink="@{obj.textColorLink}"
+               android:autoText="@{obj.autoText}"
+               android:capitalize="@{obj.capitalize}"
+               android:imeActionLabel="@{obj.imeActionLabel}"
+               android:imeActionId="@{obj.imeActionId}"
+               android:lineSpacingExtra="@{obj.lineSpacingExtra}"
+               android:lineSpacingMultiplier="@{obj.lineSpacingMultiplier}"
+               android:maxLength="@{obj.maxLength}"
+               android:shadowColor="@{obj.shadowColor}"
+               android:shadowDx="@{obj.shadowDx}"
+               android:shadowDy="@{obj.shadowDy}"
+               android:shadowRadius="@{obj.shadowRadius}"
+               android:textSize="@{obj.textSize}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textAllCaps"
+               android:textAllCaps="@{obj.textAllCaps}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textBufferType"
+               android:bufferType="@{obj.bufferType}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textInputType"
+               android:inputType="@{obj.inputType}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textDigits"
+               android:digits="@{obj.digits}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textPhoneNumber"
+               android:phoneNumber="@{obj.phoneNumber}"
+            />
+    <TextView  android:layout_width="match_parent" android:layout_height="match_parent"
+               android:id="@+id/textInputMethod"
+               android:inputMethod="@{obj.inputMethod}"
+            />
+
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml
new file mode 100644
index 0000000..73660ce
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml
@@ -0,0 +1,73 @@
+<?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="obj" type="android.databinding.testapp.vo.ViewBindingObject"/>
+    <View
+            android:id="@+id/padding"
+            android:layout_width="10dp"
+            android:layout_height="10dp"
+            android:padding="@{obj.padding}"
+            />
+    <View
+            android:id="@+id/paddingStartEnd"
+            android:layout_width="10dp"
+            android:layout_height="10dp"
+            android:paddingEnd="@{obj.paddingEnd}"
+            android:paddingStart="@{obj.paddingStart}"
+            />
+    <View
+            android:id="@+id/paddingTopBottom"
+            android:layout_width="10dp"
+            android:layout_height="10dp"
+            android:paddingBottom="@{obj.paddingBottom}"
+            android:paddingTop="@{obj.paddingTop}"
+            />
+    <View
+            android:id="@+id/paddingLeftRight"
+            android:layout_width="10dp"
+            android:layout_height="10dp"
+            android:paddingLeft="@{obj.paddingLeft}"
+            android:paddingRight="@{obj.paddingRight}"
+            />
+    <View
+            android:id="@+id/backgroundTint"
+            android:backgroundTint="@{obj.backgroundTint}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+    <View
+            android:id="@+id/fadeScrollbars"
+            android:fadeScrollbars="@{obj.fadeScrollbars}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+    <View
+            android:id="@+id/nextFocus"
+            android:nextFocusForward="@{obj.nextFocusForward}"
+            android:nextFocusLeft="@{obj.nextFocusLeft}"
+            android:nextFocusRight="@{obj.nextFocusRight}"
+            android:nextFocusUp="@{obj.nextFocusUp}"
+            android:nextFocusDown="@{obj.nextFocusDown}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+    <View
+            android:id="@+id/requiresFadingEdge"
+            android:requiresFadingEdge="@{obj.requiresFadingEdge}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+    <View
+            android:id="@+id/scrollbar"
+            android:scrollbarDefaultDelayBeforeFade="@{obj.scrollbarDefaultDelayBeforeFade}"
+            android:scrollbarFadeDuration="@{obj.scrollbarFadeDuration}"
+            android:scrollbarSize="@{obj.scrollbarSize}"
+            android:scrollbarStyle="@{obj.scrollbarStyle}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+    <View
+            android:id="@+id/transformPivot"
+            android:transformPivotX="@{obj.transformPivotX}"
+            android:transformPivotY="@{obj.transformPivotY}"
+            android:layout_width="10dp"
+            android:layout_height="10dp"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml
new file mode 100644
index 0000000..af40a27
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml
@@ -0,0 +1,15 @@
+<?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="obj" type="android.databinding.testapp.vo.ViewGroupBindingObject"/>
+    <FrameLayout android:layout_width="match_parent"
+                 android:layout_height="match_parent"
+                 android:id="@+id/view"
+                 android:alwaysDrawnWithCache="@{obj.alwaysDrawnWithCache}"
+                 android:animationCache="@{obj.animationCache}"
+                 android:splitMotionEvents="@{obj.splitMotionEvents}"
+                 android:animateLayoutChanges="@{obj.animateLayoutChanges}"
+            />
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml
new file mode 100644
index 0000000..af0796e
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml
@@ -0,0 +1,25 @@
+<?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"
+              xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <variable name="viewStubVisibility" type="int"/>
+    <variable name="firstName" type="String"/>
+    <variable name="lastName" type="String"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{firstName}"
+              android:id="@+id/firstName"
+            />
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{lastName}"
+              android:id="@+id/lastName"
+            />
+
+    <ViewStub android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/viewStub"
+            android:visibility="@{viewStubVisibility}"
+            android:layout="@layout/view_stub_contents"
+            bind:firstName="@{firstName}"
+            bind:lastName="@{lastName}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml
new file mode 100644
index 0000000..e0b922b
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml
@@ -0,0 +1,10 @@
+<?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="obj" type="android.databinding.testapp.vo.ViewStubBindingObject"/>
+    <ViewStub android:layout_width="match_parent" android:layout_height="match_parent"
+            android:id="@+id/view"
+            android:layout="@{obj.layout}"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml
new file mode 100644
index 0000000..9305189
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml
@@ -0,0 +1,14 @@
+<?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="firstName" type="String"/>
+    <variable name="lastName" type="String"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{firstName}"
+              android:id="@+id/firstNameContents"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+              android:text="@{lastName}"
+              android:id="@+id/lastNameContents"/>
+</LinearLayout>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..0a2c6bec
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml
new file mode 100644
index 0000000..8817316
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <fraction name="myFraction">150%</fraction>
+    <fraction name="myParentFraction">300%p</fraction>
+
+</resources>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e53e327
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <string name="app_name">TestApp</string>
+    <string name="rain">Rain</string>
+    <string name="nameWithTitle">%1$s %2$s</string>
+    <plurals name="orange">
+        <item quantity="one">orange</item>
+        <item quantity="other">oranges</item>
+    </plurals>
+</resources>
diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..c0d5471
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Holo">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>
diff --git a/tools/data-binding/integration-tests/TestApp/build.gradle b/tools/data-binding/integration-tests/TestApp/build.gradle
new file mode 100644
index 0000000..85d7f88
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    println "loaded config"
+
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
diff --git a/tools/data-binding/integration-tests/TestApp/gradle.properties b/tools/data-binding/integration-tests/TestApp/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/integration-tests/TestApp/gradlew b/tools/data-binding/integration-tests/TestApp/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/integration-tests/TestApp/gradlew.bat b/tools/data-binding/integration-tests/TestApp/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/integration-tests/TestApp/settings.gradle b/tools/data-binding/integration-tests/TestApp/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/tools/data-binding/integration-tests/TestApp/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/tools/data-binding/library/build.gradle b/tools/data-binding/library/build.gradle
new file mode 100644
index 0000000..4679d3f
--- /dev/null
+++ b/tools/data-binding/library/build.gradle
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+        exclude 'android/databinding/DataBinderMapper.class'
+    }
+}
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(":baseLibrary")
+    compile 'com.android.support:support-v4:+'
+}
+
+configurations {
+    jarArchives
+}
+
+
+//create jar tasks
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+
+    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
+        return; // Skip debug builds.
+    }
+    // @Jar version is needed to run compiler tests
+    def task = project.tasks.create "jar${name.capitalize()}", Jar
+    task.dependsOn variant.javaCompile
+    task.from variant.javaCompile.destinationDir
+    artifacts.add('jarArchives', task);
+}
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'library'
+        }
+    }
+}
+
+afterEvaluate {
+    tasks['packageReleaseJar'].exclude('android/databinding/DataBinderMapper.*')
+    tasks['packageDebugJar'].exclude('android/databinding/DataBinderMapper.*')
+}
+
+uploadJarArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://${config.mavenRepoDir}")
+            pom.artifactId = "libraryJar"
+        }
+    }
+}
+
+uploadArchives.dependsOn uploadJarArchives
\ No newline at end of file
diff --git a/tools/data-binding/library/src/main/AndroidManifest.xml b/tools/data-binding/library/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8596caf
--- /dev/null
+++ b/tools/data-binding/library/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.databinding.library">
+
+</manifest>
diff --git a/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java b/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java
new file mode 100644
index 0000000..3aa9d35
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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;
+
+public class BaseObservable implements Observable {
+    private PropertyChangeRegistry mCallbacks;
+
+    public BaseObservable() {
+    }
+
+    @Override
+    public synchronized void addOnPropertyChangedListener(OnPropertyChangedListener listener) {
+        if (mCallbacks == null) {
+            mCallbacks = new PropertyChangeRegistry();
+        }
+        mCallbacks.add(listener);
+    }
+
+    @Override
+    public synchronized void removeOnPropertyChangedListener(OnPropertyChangedListener listener) {
+        if (mCallbacks != null) {
+            mCallbacks.remove(listener);
+        }
+    }
+
+    public synchronized void notifyChange() {
+        if (mCallbacks != null) {
+            mCallbacks.notifyCallbacks(this, 0, null);
+        }
+    }
+
+    public void notifyPropertyChanged(int fieldId) {
+        if (mCallbacks != null) {
+            mCallbacks.notifyCallbacks(this, fieldId, null);
+        }
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java b/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java
new file mode 100644
index 0000000..9fc7f4b
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.view.View;
+
+/**
+ * This class will be stripped from the jar and then replaced by the annotation processor
+ * as part of the code generation step. This class's existence is just to ensure that
+ * compile works and no reflection is needed to access the generated class.
+ */
+class DataBinderMapper {
+    public ViewDataBinding getDataBinder(View view, int layoutId) {
+        return null;
+    }
+    public int getId(String key) {
+        return 0;
+    }
+    public static int TARGET_MIN_SDK = 0;
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java b/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java
new file mode 100644
index 0000000..cb5d209
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.databinding;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Utility class to create {@link ViewDataBinding} from layouts.
+ */
+public class DataBindingUtil {
+    private static DataBinderMapper sMapper = new DataBinderMapper();
+
+    public static <T extends ViewDataBinding> T inflate(Context context, int layoutId,
+            ViewGroup parent, boolean attachToParent) {
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final View view = inflater.inflate(layoutId, parent, attachToParent);
+        return bindTo(view, layoutId);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends ViewDataBinding> T bindTo(View root, int layoutId) {
+        return (T) sMapper.getDataBinder(root, layoutId);
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java
new file mode 100644
index 0000000..e4c65bde
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.databinding;
+
+import android.support.v4.util.Pools;
+
+public class ListChangeRegistry
+        extends
+        CallbackRegistry<OnListChangedListener, ObservableList, ListChangeRegistry.ListChanges> {
+    private static final Pools.SynchronizedPool<ListChanges> sListChanges =
+            new Pools.SynchronizedPool<>(10);
+
+    private static final int ALL = 0;
+    private static final int CHANGED = 1;
+    private static final int INSERTED = 2;
+    private static final int MOVED = 3;
+    private static final int REMOVED = 4;
+
+    private static final CallbackRegistry.NotifierCallback<OnListChangedListener, ObservableList, ListChanges> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<OnListChangedListener, ObservableList, ListChanges>() {
+        @Override
+        public void onNotifyCallback(OnListChangedListener callback, ObservableList sender,
+                int notificationType, ListChanges listChanges) {
+            switch (notificationType) {
+                case CHANGED:
+                    callback.onItemRangeChanged(listChanges.start, listChanges.count);
+                    break;
+                case INSERTED:
+                    callback.onItemRangeInserted(listChanges.start, listChanges.count);
+                    break;
+                case MOVED:
+                    callback.onItemRangeMoved(listChanges.start, listChanges.to, listChanges.count);
+                    break;
+                case REMOVED:
+                    callback.onItemRangeRemoved(listChanges.start, listChanges.count);
+                    break;
+                default:
+                    callback.onChanged();
+                    break;
+            }
+            if (listChanges != null) {
+                sListChanges.release(listChanges);
+            }
+        }
+    };
+
+    public void notifyChanged(ObservableList list) {
+        notifyCallbacks(list, ALL, null);
+    }
+
+    public void notifyChanged(ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, CHANGED, listChanges);
+    }
+
+    public void notifyInserted(ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, INSERTED, listChanges);
+    }
+
+    public void notifyMoved(ObservableList list, int from, int to, int count) {
+        ListChanges listChanges = acquire(from, to, count);
+        notifyCallbacks(list, MOVED, listChanges);
+    }
+
+    public void notifyRemoved(ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, REMOVED, listChanges);
+    }
+
+    private static ListChanges acquire(int start, int to, int count) {
+        ListChanges listChanges = sListChanges.acquire();
+        if (listChanges == null) {
+            listChanges = new ListChanges();
+        }
+        listChanges.start = start;
+        listChanges.to = to;
+        listChanges.count = count;
+        return listChanges;
+    }
+
+    /**
+     * Creates an EventRegistry that notifies the event with notifier.
+     */
+    public ListChangeRegistry() {
+        super(NOTIFIER_CALLBACK);
+    }
+
+    static class ListChanges {
+        public int start;
+        public int count;
+        public int to;
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java
new file mode 100644
index 0000000..6403cce
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.databinding;
+
+public class MapChangeRegistry
+        extends CallbackRegistry<OnMapChangedListener, ObservableMap, Object> {
+
+    private static NotifierCallback<OnMapChangedListener, ObservableMap, Object> NOTIFIER_CALLBACK =
+            new NotifierCallback<OnMapChangedListener, ObservableMap, Object>() {
+                @Override
+                public void onNotifyCallback(OnMapChangedListener callback, ObservableMap sender,
+                        int arg, Object arg2) {
+                    callback.onMapChanged(sender, arg2);
+                }
+            };
+
+    public MapChangeRegistry() {
+        super(NOTIFIER_CALLBACK);
+    }
+
+    public void notifyChange(ObservableMap sender, Object key) {
+        notifyCallbacks(sender, 0, key);
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java
new file mode 100644
index 0000000..d70bc68
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java
@@ -0,0 +1,124 @@
+/*
+ * 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 android.databinding;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T> {
+    private ListChangeRegistry mListeners = new ListChangeRegistry();
+
+    @Override
+    public void addOnListChangedListener(OnListChangedListener listener) {
+        if (mListeners == null) {
+            mListeners = new ListChangeRegistry();
+        }
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnListChangedListener(OnListChangedListener listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    @Override
+    public boolean add(T object) {
+        super.add(object);
+        notifyAdd(size() - 1, 1);
+        return true;
+    }
+
+    @Override
+    public void add(int index, T object) {
+        super.add(index, object);
+        notifyAdd(index, 1);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends T> collection) {
+        int oldSize = size();
+        boolean added = super.addAll(collection);
+        if (added) {
+            notifyAdd(oldSize, size() - oldSize);
+        }
+        return added;
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends T> collection) {
+        boolean added = super.addAll(index, collection);
+        if (added) {
+            notifyAdd(index, collection.size());
+        }
+        return added;
+    }
+
+    @Override
+    public void clear() {
+        int oldSize = size();
+        super.clear();
+        if (oldSize != 0) {
+            notifyRemove(0, oldSize);
+        }
+    }
+
+    @Override
+    public T remove(int index) {
+        T val = super.remove(index);
+        notifyRemove(index, 1);
+        return val;
+    }
+
+    @Override
+    public boolean remove(Object object) {
+        int index = indexOf(object);
+        if (index >= 0) {
+            remove(index);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public T set(int index, T object) {
+        T val = super.set(index, object);
+        if (mListeners != null) {
+            mListeners.notifyChanged(this, index, 1);
+        }
+        return val;
+    }
+
+    @Override
+    protected void removeRange(int fromIndex, int toIndex) {
+        super.removeRange(fromIndex, toIndex);
+        notifyRemove(fromIndex, toIndex - fromIndex);
+    }
+
+    private void notifyAdd(int start, int count) {
+        if (mListeners != null) {
+            mListeners.notifyInserted(this, start, count);
+        }
+    }
+
+    private void notifyRemove(int start, int count) {
+        if (mListeners != null) {
+            mListeners.notifyRemoved(this, start, count);
+        }
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java
new file mode 100644
index 0000000..c8b57b7
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java
@@ -0,0 +1,107 @@
+/*
+ * 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 android.databinding;
+
+import android.support.v4.util.ArrayMap;
+
+import java.util.Collection;
+
+public class ObservableArrayMap<K, V> extends ArrayMap<K, V> implements ObservableMap<K, V> {
+
+    private MapChangeRegistry mListeners;
+
+    @Override
+    public void addOnMapChangedListener(
+            OnMapChangedListener<? extends ObservableMap<K, V>, K> listener) {
+        if (mListeners == null) {
+            mListeners = new MapChangeRegistry();
+        }
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnMapChangedListener(
+            OnMapChangedListener<? extends ObservableMap<K, V>, K> listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    @Override
+    public void clear() {
+        boolean wasEmpty = isEmpty();
+        if (!wasEmpty) {
+            super.clear();
+            notifyChange(null);
+        }
+    }
+
+    public V put(K k, V v) {
+        V val = super.put(k, v);
+        notifyChange(k);
+        return v;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> collection) {
+        boolean removed = false;
+        for (Object key : collection) {
+            int index = indexOfKey(key);
+            if (index >= 0) {
+                removed = true;
+                removeAt(index);
+            }
+        }
+        return removed;
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> collection) {
+        boolean removed = false;
+        for (int i = size() - 1; i >= 0; i--) {
+            Object key = keyAt(i);
+            if (!collection.contains(key)) {
+                removeAt(i);
+                removed = true;
+            }
+        }
+        return removed;
+    }
+
+    @Override
+    public V removeAt(int index) {
+        K key = keyAt(index);
+        V value = super.removeAt(index);
+        if (value != null) {
+            notifyChange(key);
+        }
+        return value;
+    }
+
+    @Override
+    public V setValueAt(int index, V value) {
+        K key = keyAt(index);
+        V oldValue = super.setValueAt(index, value);
+        notifyChange(key);
+        return oldValue;
+    }
+
+    private void notifyChange(Object key) {
+        if (mListeners != null) {
+            mListeners.notifyCallbacks(this, 0, key);
+        }
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java
new file mode 100644
index 0000000..afead95
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableBoolean extends BaseObservable {
+    private boolean mValue;
+
+    public boolean get() {
+        return mValue;
+    }
+
+    public void set(boolean value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java
new file mode 100644
index 0000000..3ee73a5
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableByte extends BaseObservable {
+    private byte mValue;
+
+    public byte get() {
+        return mValue;
+    }
+
+    public void set(byte value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java
new file mode 100644
index 0000000..f78231c
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableChar extends BaseObservable {
+    private char mValue;
+
+    public char get() {
+        return mValue;
+    }
+
+    public void set(char value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java
new file mode 100644
index 0000000..ee7bd05
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableDouble extends BaseObservable {
+    private double mValue;
+
+    public double get() {
+        return mValue;
+    }
+
+    public void set(double value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java
new file mode 100644
index 0000000..05a61b7
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableField<T> extends BaseObservable {
+    private T mValue;
+
+    public T get() {
+        return mValue;
+    }
+
+    public void set(T value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java
new file mode 100644
index 0000000..e8a063a
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableFloat extends BaseObservable {
+    private float mValue;
+
+    public float get() {
+        return mValue;
+    }
+
+    public void set(float value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java
new file mode 100644
index 0000000..77140f8
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableInt extends BaseObservable {
+    private int mValue;
+
+    public int get() {
+        return mValue;
+    }
+
+    public void set(int value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java
new file mode 100644
index 0000000..b1621e1
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableLong extends BaseObservable {
+    private long mValue;
+
+    public long get() {
+        return mValue;
+    }
+
+    public void set(long value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java
new file mode 100644
index 0000000..6d46705
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.databinding;
+
+public class ObservableShort extends BaseObservable {
+    private short mValue;
+
+    public short get() {
+        return mValue;
+    }
+
+    public void set(short value) {
+        mValue = value;
+        notifyChange();
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java
new file mode 100644
index 0000000..c675c39
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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;
+
+public class PropertyChangeRegistry extends
+        CallbackRegistry<OnPropertyChangedListener, Observable, Void> {
+
+    private static final CallbackRegistry.NotifierCallback<OnPropertyChangedListener, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<OnPropertyChangedListener, Observable, Void>() {
+        @Override
+        public void onNotifyCallback(OnPropertyChangedListener callback, Observable sender,
+                int arg, Void notUsed) {
+            callback.onPropertyChanged(sender, arg);
+        }
+    };
+
+    public PropertyChangeRegistry() {
+        super(NOTIFIER_CALLBACK);
+    }
+
+    public void notifyChange(Observable observable, int propertyId) {
+        notifyCallbacks(observable, propertyId, null);
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java
new file mode 100644
index 0000000..a5ba6e3
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2014 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;
+
+import com.android.databinding.library.R;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+
+import java.lang.ref.WeakReference;
+
+public abstract class ViewDataBinding {
+
+    /**
+     * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
+     * we can test API dependent behavior.
+     */
+    static int SDK_INT = VERSION.SDK_INT;
+
+    /**
+     * Prefix for android:tag on Views with binding. The root View and include tags will not have
+     * android:tag attributes and will use ids instead.
+     */
+    public static final String BINDING_TAG_PREFIX = "bindingTag";
+
+    // The length of BINDING_TAG_PREFIX prevents calling length repeatedly.
+    private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length();
+
+    // ICS (v 14) fixes a leak when using setTag(int, Object)
+    private static final boolean USE_TAG_ID = DataBinderMapper.TARGET_MIN_SDK >= 14;
+
+    /**
+     * Method object extracted out to attach a listener to a bound Observable object.
+     */
+    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
+        @Override
+        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
+            return new WeakPropertyListener(viewDataBinding, localFieldId);
+        }
+    };
+
+    /**
+     * Method object extracted out to attach a listener to a bound ObservableList object.
+     */
+    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
+        @Override
+        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
+            return new WeakListListener(viewDataBinding, localFieldId);
+        }
+    };
+
+    /**
+     * Method object extracted out to attach a listener to a bound ObservableMap object.
+     */
+    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
+        @Override
+        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
+            return new WeakMapListener(viewDataBinding, localFieldId);
+        }
+    };
+
+    private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;
+
+    static {
+        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
+            ROOT_REATTACHED_LISTENER = null;
+        } else {
+            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
+                @TargetApi(VERSION_CODES.KITKAT)
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    // execute the pending bindings.
+                    final ViewDataBinding binding;
+                    if (USE_TAG_ID) {
+                        binding = (ViewDataBinding) v.getTag(R.id.dataBinding);
+                    } else {
+                        binding = (ViewDataBinding) v.getTag();
+                    }
+                    v.post(binding.mRebindRunnable);
+                    v.removeOnAttachStateChangeListener(this);
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                }
+            };
+        }
+    }
+
+    /**
+     * Runnable executed on animation heartbeat to rebind the dirty Views.
+     */
+    private Runnable mRebindRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mPendingRebind) {
+                boolean rebind = true;
+                if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+                    rebind = mRoot.isAttachedToWindow();
+                    if (!rebind) {
+                        // Don't execute the pending bindings until the View
+                        // is attached again.
+                        mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
+                    }
+                }
+                if (rebind) {
+                    mPendingRebind = false;
+                    executePendingBindings();
+                }
+            }
+        }
+    };
+
+    /**
+     * Flag indicates that there are pending bindings that need to be reevaluated.
+     */
+    private boolean mPendingRebind = false;
+
+    /**
+     * The observed expressions.
+     */
+    private WeakListener[] mLocalFieldObservers;
+
+    /**
+     * The root View that this Binding is associated with.
+     */
+    private final View mRoot;
+
+    protected ViewDataBinding(View root, int localFieldCount) {
+        mLocalFieldObservers = new WeakListener[localFieldCount];
+        this.mRoot = root;
+        if (USE_TAG_ID) {
+            this.mRoot.setTag(R.id.dataBinding, this);
+        } else {
+            this.mRoot.setTag(this);
+        }
+    }
+
+    public static int getBuildSdkInt() {
+        return SDK_INT;
+    }
+
+    /**
+     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
+     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
+     * @param object The object that has changed.
+     * @param fieldId The BR ID of the field being changed or _all if
+     *                no specific field is being notified.
+     * @return true if this change should cause a change to the UI.
+     */
+    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);
+
+    public abstract boolean setVariable(int variableId, Object variable);
+
+    /**
+     * Evaluates the pending bindings, updating any Views that have expressions bound to
+     * modified variables. This <b>must</b> be run on the UI thread.
+     */
+    public abstract void executePendingBindings();
+
+    /**
+     * Used internally to invalidate flags of included layouts.
+     */
+    public abstract void invalidateAll();
+
+    /**
+     * Removes binding listeners to expression variables.
+     */
+    public void unbind() {
+        for (WeakListener weakListener : mLocalFieldObservers) {
+            if (weakListener != null) {
+                weakListener.unregister();
+            }
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        unbind();
+    }
+
+    /**
+     * Returns the outermost View in the layout file associated with the Binding.
+     * @return the outermost View in the layout file associated with the Binding.
+     */
+    public View getRoot() {
+        return mRoot;
+    }
+
+    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
+        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
+        if (result) {
+            requestRebind();
+        }
+    }
+
+    protected boolean unregisterFrom(int localFieldId) {
+        WeakListener listener = mLocalFieldObservers[localFieldId];
+        if (listener != null) {
+            return listener.unregister();
+        }
+        return false;
+    }
+
+    protected void requestRebind() {
+        if (mPendingRebind) {
+            return;
+        }
+        mPendingRebind = true;
+        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+            mRoot.postOnAnimation(mRebindRunnable);
+        } else {
+            mRoot.post(mRebindRunnable);
+        }
+    }
+
+    protected Object getObservedField(int localFieldId) {
+        WeakListener listener = mLocalFieldObservers[localFieldId];
+        if (listener == null) {
+            return null;
+        }
+        return listener.getTarget();
+    }
+
+    private boolean updateRegistration(int localFieldId, Object observable,
+            CreateWeakListener listenerCreator) {
+        if (observable == null) {
+            return unregisterFrom(localFieldId);
+        }
+        WeakListener listener = mLocalFieldObservers[localFieldId];
+        if (listener == null) {
+            registerTo(localFieldId, observable, listenerCreator);
+            return true;
+        }
+        if (listener.getTarget() == observable) {
+            return false;//nothing to do, same object
+        }
+        unregisterFrom(localFieldId);
+        registerTo(localFieldId, observable, listenerCreator);
+        return true;
+    }
+
+    protected boolean updateRegistration(int localFieldId, Observable observable) {
+        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
+    }
+
+    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
+        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
+    }
+
+    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
+        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
+    }
+
+    protected void registerTo(int localFieldId, Object observable,
+            CreateWeakListener listenerCreator) {
+        if (observable == null) {
+            return;
+        }
+        WeakListener listener = mLocalFieldObservers[localFieldId];
+        if (listener == null) {
+            listener = listenerCreator.create(this, localFieldId);
+            mLocalFieldObservers[localFieldId] = listener;
+        }
+        listener.setTarget(observable);
+    }
+
+    /**
+     * Walk all children of root and assign tagged Views to associated indices in views.
+     *
+     * @param root The base of the View hierarchy to walk.
+     * @param views An array of all Views with binding expressions and all Views with IDs. This
+     *              will start empty and will contain the found Views when this method completes.
+     * @param includes A mapping of include tag IDs to the index into the views array.
+     * @param viewsWithIds A mapping of views with IDs but without expressions to the index
+     *                     into the views array.
+     */
+    private static void mapTaggedChildViews(View root, View[] views, SparseIntArray includes,
+            SparseIntArray viewsWithIds) {
+        if (root instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) root;
+            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
+                mapTaggedViews(viewGroup.getChildAt(i), views, includes, viewsWithIds);
+            }
+        }
+    }
+
+    private static void mapTaggedViews(View view, View[] views, SparseIntArray includes,
+            SparseIntArray viewsWithIds) {
+        boolean visitChildren = true;
+        String tag = (String) view.getTag();
+        if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
+            int tagIndex = parseTagInt(tag);
+            views[tagIndex] = view;
+        } else {
+            visitChildren = addViewWithId(view, views, includes, viewsWithIds);
+        }
+        if (visitChildren) {
+            mapTaggedChildViews(view, views, includes, viewsWithIds);
+        }
+    }
+
+    /**
+     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
+     * IDs into a View[] that is returned. This is used to walk the view hierarchy once to find
+     * all bound and ID'd views.
+     *
+     * @param root The root of the view hierarchy to walk.
+     * @param numViews The total number of ID'd views and views with expressions.
+     * @param includes Views that are considered includes and should be treated as separate
+     *                 binders.
+     * @param viewsWithIds Views that don't have tags, but have IDs.
+     * @return An array of size numViews containing all Views in the hierarchy that have IDs
+     * (with elements in viewsWithIds) or are tagged containing expressions.
+     */
+    protected static View[] mapChildViews(View root, int numViews, SparseIntArray includes,
+            SparseIntArray viewsWithIds) {
+        View[] views = new View[numViews];
+        boolean visitChildren = addViewWithId(root, views, includes, viewsWithIds);
+        if (visitChildren) {
+            mapTaggedChildViews(root, views, includes, viewsWithIds);
+        }
+        return views;
+    }
+
+    private static boolean addViewWithId(View view, View[] views, SparseIntArray includes,
+            SparseIntArray viewsWithIds) {
+        final int id = view.getId();
+        boolean visitChildren = true;
+        if (id > 0) {
+            int index;
+            if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) {
+                views[index] = view;
+            } else if (includes != null && (index = includes.get(id, -1)) >= 0) {
+                views[index] = view;
+                visitChildren = false;
+            }
+        }
+        return visitChildren;
+    }
+
+    /**
+     * Parse the tag without creating a new String object. This is fast and assumes the
+     * tag is in the correct format.
+     * @param str The tag string.
+     * @return The binding tag number parsed from the tag string.
+     */
+    private static int parseTagInt(String str) {
+        final int end = str.length();
+        int val = 0;
+        for (int i = BINDING_NUMBER_START; i < end; i++) {
+            val *= 10;
+            char c = str.charAt(i);
+            val += (c - '0');
+        }
+        return val;
+    }
+
+    private static abstract class WeakListener<T> {
+        private final WeakReference<ViewDataBinding> mBinder;
+        protected final int mLocalFieldId;
+        private T mTarget;
+
+        public WeakListener(ViewDataBinding binder, int localFieldId) {
+            mBinder = new WeakReference<ViewDataBinding>(binder);
+            mLocalFieldId = localFieldId;
+        }
+
+        public void setTarget(T object) {
+            unregister();
+            mTarget = object;
+            if (mTarget != null) {
+                addListener(mTarget);
+            }
+        }
+
+        public boolean unregister() {
+            boolean unregistered = false;
+            if (mTarget != null) {
+                removeListener(mTarget);
+                unregistered = true;
+            }
+            mTarget = null;
+            return unregistered;
+        }
+
+        public T getTarget() {
+            return mTarget;
+        }
+
+        protected ViewDataBinding getBinder() {
+            ViewDataBinding binder = mBinder.get();
+            if (binder == null) {
+                unregister(); // The binder is dead
+            }
+            return binder;
+        }
+
+        protected abstract void addListener(T target);
+        protected abstract void removeListener(T target);
+    }
+
+    private static class WeakPropertyListener extends WeakListener<Observable>
+            implements OnPropertyChangedListener {
+        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
+            super(binder, localFieldId);
+        }
+
+        @Override
+        protected void addListener(Observable target) {
+            target.addOnPropertyChangedListener(this);
+        }
+
+        @Override
+        protected void removeListener(Observable target) {
+            target.removeOnPropertyChangedListener(this);
+        }
+
+        @Override
+        public void onPropertyChanged(Observable sender, int fieldId) {
+            ViewDataBinding binder = getBinder();
+            if (binder == null) {
+                return;
+            }
+            Observable obj = getTarget();
+            if (obj != sender) {
+                return; // notification from the wrong object?
+            }
+            binder.handleFieldChange(mLocalFieldId, sender, fieldId);
+        }
+    }
+
+    private static class WeakListListener extends WeakListener<ObservableList>
+            implements OnListChangedListener {
+
+        public WeakListListener(ViewDataBinding binder, int localFieldId) {
+            super(binder, localFieldId);
+        }
+
+        @Override
+        public void onChanged() {
+            ViewDataBinding binder = getBinder();
+            if (binder == null) {
+                return;
+            }
+            ObservableList target = getTarget();
+            if (target == null) {
+                return; // We don't expect any notifications from null targets
+            }
+            binder.handleFieldChange(mLocalFieldId, target, 0);
+        }
+
+        @Override
+        public void onItemRangeChanged(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        protected void addListener(ObservableList target) {
+            target.addOnListChangedListener(this);
+        }
+
+        @Override
+        protected void removeListener(ObservableList target) {
+            target.removeOnListChangedListener(this);
+        }
+    }
+
+    private static class WeakMapListener extends WeakListener<ObservableMap>
+            implements OnMapChangedListener {
+        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
+            super(binder, localFieldId);
+        }
+
+        @Override
+        protected void addListener(ObservableMap target) {
+            target.addOnMapChangedListener(this);
+        }
+
+        @Override
+        protected void removeListener(ObservableMap target) {
+            target.removeOnMapChangedListener(this);
+        }
+
+        @Override
+        public void onMapChanged(ObservableMap sender, Object key) {
+            ViewDataBinding binder = getBinder();
+            if (binder == null || sender != getTarget()) {
+                return;
+            }
+            binder.handleFieldChange(mLocalFieldId, sender, 0);
+        }
+    }
+
+    private interface CreateWeakListener {
+        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
+    }
+}
diff --git a/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java b/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java
new file mode 100644
index 0000000..01b878a
--- /dev/null
+++ b/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java
@@ -0,0 +1,102 @@
+/*
+ * 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 android.databinding;
+
+import android.view.View;
+import android.view.ViewStub;
+import android.view.ViewStub.OnInflateListener;
+
+/**
+ * This class represents a ViewStub before and after inflation. Before inflation,
+ * the ViewStub is accessible. After inflation, the ViewDataBinding is accessible
+ * if the inflated View has bindings. If not, the root View will be accessible.
+ */
+public class ViewStubProxy {
+    private ViewStub mViewStub;
+    private ViewDataBinding mViewDataBinding;
+    private View mRoot;
+    private OnInflateListener mOnInflateListener;
+    private ViewDataBinding mContainingBinding;
+
+    private OnInflateListener mProxyListener = new OnInflateListener() {
+        @Override
+        public void onInflate(ViewStub stub, View inflated) {
+            mRoot = inflated;
+            mViewDataBinding = DataBindingUtil.bindTo(inflated, stub.getLayoutResource());
+            mViewStub = null;
+
+            if (mOnInflateListener != null) {
+                mOnInflateListener.onInflate(stub, inflated);
+                mOnInflateListener = null;
+            }
+            mContainingBinding.invalidateAll();
+            mContainingBinding.executePendingBindings();
+        }
+    };
+
+    public ViewStubProxy(ViewStub viewStub) {
+        mViewStub = viewStub;
+        mViewStub.setOnInflateListener(mProxyListener);
+    }
+
+    public void setContainingBinding(ViewDataBinding containingBinding) {
+        mContainingBinding = containingBinding;
+    }
+
+    /**
+     * @return <code>true</code> if the ViewStub has replaced itself with the inflated layout
+     * or <code>false</code> if not.
+     */
+    public boolean isInflated() {
+        return mRoot != null;
+    }
+
+    /**
+     * @return The root View of the layout replacing the ViewStub once it has been inflated.
+     * <code>null</code> is returned prior to inflation.
+     */
+    public View getRoot() {
+        return mRoot;
+    }
+
+    /**
+     * @return The data binding associated with the inflated layout once it has been inflated.
+     * <code>null</code> prior to inflation or if there is no binding associated with the layout.
+     */
+    public ViewDataBinding getBinding() {
+        return mViewDataBinding;
+    }
+
+    /**
+     * @return The ViewStub in the layout or <code>null</code> if the ViewStub has been inflated.
+     */
+    public ViewStub getViewStub() {
+        return mViewStub;
+    }
+
+    /**
+     * Sets the {@link OnInflateListener} to be called when the ViewStub inflates. The proxy must
+     * have an OnInflateListener, so <code>listener</code> will be called immediately after
+     * the proxy's listener is called.
+     *
+     * @param listener The OnInflateListener to notify of successful inflation
+     */
+    public void setOnInflateListener(OnInflateListener listener) {
+        if (mViewStub != null) {
+            mOnInflateListener = listener;
+        }
+    }
+}
diff --git a/tools/data-binding/library/src/main/res/values/ids.xml b/tools/data-binding/library/src/main/res/values/ids.xml
new file mode 100644
index 0000000..a7b89d3
--- /dev/null
+++ b/tools/data-binding/library/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="id" name="dataBinding"/>
+</resources>
\ No newline at end of file
diff --git a/tools/data-binding/samples/BindingDemo/.gitignore b/tools/data-binding/samples/BindingDemo/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/tools/data-binding/samples/BindingDemo/app/.gitignore b/tools/data-binding/samples/BindingDemo/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/tools/data-binding/samples/BindingDemo/app/build.gradle b/tools/data-binding/samples/BindingDemo/app/build.gradle
new file mode 100644
index 0000000..fa54aab
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/build.gradle
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'com.android.databinding'
+
+def generatedSources = "$buildDir/generated/source/br"
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.1"
+
+    defaultConfig {
+        applicationId "com.android.bindingdemo"
+        minSdkVersion 15
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+    }
+}
+
+android.applicationVariants.all { variant ->
+    variant.javaCompile.doFirst {
+        println "*** compile doFirst ${variant.name}"
+        new File(generatedSources).mkdirs()
+        variant.javaCompile.options.compilerArgs += [
+                '-s', generatedSources
+        ]
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile 'com.android.support:appcompat-v7:21.+'
+    compile 'com.android.databinding:library:0.3-SNAPSHOT@aar'
+    compile 'com.android.support:recyclerview-v7:21.0.2'
+    compile 'com.android.support:gridlayout-v7:21.+'
+    compile 'com.android.support:cardview-v7:21.+'
+    compile 'com.android.databinding:baseLibrary:0.3-SNAPSHOT'
+    compile 'com.android.databinding:adapters:0.3-SNAPSHOT'
+    provided 'com.android.databinding:annotationprocessor:0.3-SNAPSHOT'
+    provided fileTree(dir : 'build/databinder/src', include : ['*.java'])
+
+    testCompile 'junit:junit:4.12'
+    testCompile 'org.mockito:mockito-core:1.9.5'
+}
diff --git a/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro b/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml b/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3455d7c
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.bindingdemo" >
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java
new file mode 100644
index 0000000..89eef68
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java
@@ -0,0 +1,31 @@
+package com.android.example.bindingdemo;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+
+import android.databinding.DataBindingUtil;
+import android.databinding.ViewDataBinding;
+
+abstract public class DataBoundAdapter<T extends ViewDataBinding>
+        extends RecyclerView.Adapter<DataBoundAdapter.DataBoundViewHolder<T>> {
+    final int mLayoutId;
+    final Class<T> mBinderInterface;
+    public DataBoundAdapter(int mLayoutId, Class<T> mBinderInterface) {
+        this.mLayoutId = mLayoutId;
+        this.mBinderInterface = mBinderInterface;
+    }
+
+    @Override
+    public DataBoundAdapter.DataBoundViewHolder<T> onCreateViewHolder(ViewGroup viewGroup, int type) {
+        T binder = DataBindingUtil.inflate(viewGroup.getContext(), mLayoutId, viewGroup, false);
+        return new DataBoundViewHolder(binder);
+    }
+
+    static class DataBoundViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
+        public final T dataBinder;
+        public DataBoundViewHolder(T mViewBinder) {
+            super(mViewBinder.getRoot());
+            this.dataBinder = mViewBinder;
+        }
+    }
+}
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
new file mode 100644
index 0000000..bfd5e4a
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
@@ -0,0 +1,225 @@
+package com.android.example.bindingdemo;
+
+import android.databinding.Bindable;
+import android.databinding.Observable;
+import android.databinding.OnPropertyChangedListener;
+import android.support.v7.app.ActionBarActivity;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import android.databinding.DataBindingUtil;
+import android.databinding.PropertyChangeRegistry;
+import com.android.example.bindingdemo.databinding.ListItemBinding;
+import com.android.example.bindingdemo.databinding.MainActivityBinding;
+import com.android.example.bindingdemo.vo.User;
+import com.android.example.bindingdemo.vo.Users;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import  com.android.example.bindingdemo.BR;
+public class MainActivity extends ActionBarActivity implements Observable {
+    @Bindable
+    UserAdapter tkAdapter;
+    @Bindable
+    UserAdapter robotAdapter;
+    @Bindable
+    MainActivityBinding dataBinder;
+    @Bindable
+    User selected;
+
+    @Bindable
+    User selected2;
+
+   private final PropertyChangeRegistry mListeners = new PropertyChangeRegistry();
+
+    public User getSelected2() {
+        return selected2;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        dataBinder =  MainActivityBinding.inflate(this);
+        setContentView(dataBinder.getRoot());
+        dataBinder.robotList.setHasFixedSize(true);
+        dataBinder.toolkittyList.setHasFixedSize(true);
+        tkAdapter = new UserAdapter(Users.toolkities);
+        robotAdapter = new UserAdapter(Users.robots);
+        dataBinder.toolkittyList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
+        dataBinder.robotList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
+        dataBinder.setActivity(this);
+        dataBinder.executePendingBindings();
+    }
+
+    public UserAdapter getTkAdapter() {
+        return tkAdapter;
+    }
+
+    public UserAdapter getRobotAdapter() {
+        return robotAdapter;
+    }
+
+    public User getSelected() {
+        return selected;
+    }
+
+    private void setSelected(User selected) {
+        if (selected == this.selected) {
+            return;
+        }
+        this.selected = selected;
+        mListeners.notifyChange(this, BR.selected);
+    }
+
+    @Bindable
+    public View.OnClickListener onSave = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (selected == null) {
+                return;
+            }
+            selected.setName(dataBinder.selectedName.getText().toString());
+            selected.setLastName(dataBinder.selectedLastname.getText().toString());
+        }
+    };
+
+    @Bindable
+    public View.OnClickListener onUnselect = new View.OnClickListener() {
+
+        @Override
+        public void onClick(View v) {
+            setSelected(null);
+        }
+    };
+
+    @Bindable
+    public View.OnClickListener onDelete = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (selected == null) {
+                return;
+            }
+            if (selected.getGroup() == User.TOOLKITTY) {
+                tkAdapter.remove(selected);
+                selected.setGroup(User.ROBOT);
+                robotAdapter.add(selected);
+                dataBinder.robotList.smoothScrollToPosition(robotAdapter.getItemCount() - 1);
+            } else {
+                tkAdapter.add(selected);
+                dataBinder.toolkittyList.smoothScrollToPosition(tkAdapter.getItemCount() - 1);
+                selected.setGroup(User.TOOLKITTY);
+                robotAdapter.remove(selected);
+            }
+        }
+    };
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+
+        //noinspection SimplifiableIfStatement
+        if (id == R.id.action_settings) {
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void addOnPropertyChangedListener(OnPropertyChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnPropertyChangedListener(OnPropertyChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    public class UserAdapter extends DataBoundAdapter<ListItemBinding> implements View.OnClickListener, Observable {
+        final private List<User> userList = new ArrayList<>();
+        final private PropertyChangeRegistry mListeners = new PropertyChangeRegistry();
+
+        public UserAdapter(User[] toolkities) {
+            super(R.layout.list_item, ListItemBinding.class);
+            userList.addAll(Arrays.asList(toolkities));
+        }
+
+        @Override
+        public DataBoundViewHolder<ListItemBinding> onCreateViewHolder(ViewGroup viewGroup, int type) {
+            DataBoundViewHolder<ListItemBinding> vh = super.onCreateViewHolder(viewGroup, type);
+            vh.dataBinder.setClickListener(this);
+            return vh;
+        }
+
+        @Override
+        public void onBindViewHolder(DataBoundViewHolder<ListItemBinding> vh, int index) {
+            vh.dataBinder.setUser(userList.get(index));
+            vh.dataBinder.executePendingBindings();
+        }
+
+        @Bindable
+        @Override
+        public int getItemCount() {
+            return userList.size();
+        }
+
+        public void add(User user) {
+            if (userList.contains(user)) {
+                return;
+            }
+            userList.add(user);
+            notifyItemInserted(userList.size() - 1);
+            mListeners.notifyChange(this, BR.itemCount);
+        }
+
+        public void remove(User user) {
+            int i = userList.indexOf(user);
+            if (i < 0) {
+                return;
+            }
+            userList.remove(i);
+            notifyItemRemoved(i);
+            mListeners.notifyChange(this, BR.itemCount);
+        }
+
+        @Override
+        public void onClick(View v) {
+            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) v.getLayoutParams();
+            final int pos = lp.getViewPosition();
+            if (pos > -1 && pos < userList.size()) {
+                v.requestFocus();
+                setSelected(userList.get(pos));
+            } else {
+                setSelected(null);
+            }
+        }
+
+        @Override
+        public void addOnPropertyChangedListener(OnPropertyChangedListener listener) {
+            mListeners.add(listener);
+        }
+
+        @Override
+        public void removeOnPropertyChangedListener(OnPropertyChangedListener listener) {
+            mListeners.remove(listener);
+        }
+    }
+}
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java
new file mode 100644
index 0000000..5b7e5c7
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java
@@ -0,0 +1,83 @@
+package com.android.example.bindingdemo.vo;
+
+import android.databinding.Bindable;
+import android.graphics.Color;
+
+import android.databinding.BaseObservable;
+import com.android.example.bindingdemo.BR;
+
+import java.util.Objects;
+
+public class User extends BaseObservable {
+    @Bindable
+    private String name;
+    @Bindable
+    private String lastName;
+    @Bindable
+    private int photoResource = 0;
+    @Bindable
+    private int favoriteColor = Color.RED;
+    @Bindable
+    private int group;
+    public static final int TOOLKITTY = 1;
+    public static final int ROBOT = 2;
+
+    public User(String name, String lastName, int photoResource, int group) {
+        this.name = name;
+        this.lastName = lastName;
+        this.photoResource = photoResource;
+        this.group = group;
+    }
+
+    public void setGroup(int group) {
+        if (this.group == group) {
+            return;
+        }
+        this.group = group;
+        notifyPropertyChanged(BR.group);
+    }
+
+    public int getGroup() {
+        return group;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        if (Objects.equals(name, this.name)) {
+            return;
+        }
+        this.name = name;
+        notifyPropertyChanged(BR.name);
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        if (Objects.equals(lastName, this.lastName)) {
+            return;
+        }
+        this.lastName = lastName;
+        notifyPropertyChanged(BR.lastName);
+    }
+
+    public int getPhotoResource() {
+        return photoResource;
+    }
+
+    public void setPhotoResource(int photoResource) {
+        if (this.photoResource == photoResource) {
+            return;
+        }
+        this.photoResource = photoResource;
+        notifyPropertyChanged(BR.photoResource);
+    }
+
+    public int getFavoriteColor() {
+        return favoriteColor;
+    }
+}
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java
new file mode 100644
index 0000000..d954888
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java
@@ -0,0 +1,22 @@
+package com.android.example.bindingdemo.vo;
+
+import com.android.example.bindingdemo.R;
+
+public class Users {
+    public static final User[] robots = new User[]{
+            new User("romain", "guy", R.drawable.romain, User.ROBOT),
+    };
+    public static final User[] toolkities = new User[]{
+            new User("chet", "haase", R.drawable.chet, User.TOOLKITTY),
+            new User("adam", "powell", R.drawable.adam, User.TOOLKITTY),
+            new User("alan", "viverette", R.drawable.alan, User.TOOLKITTY),
+            new User("chris", "craik", R.drawable.chris, User.TOOLKITTY),
+            new User("george", "mount", R.drawable.george, User.TOOLKITTY),
+            new User("john", "reck", R.drawable.john, User.TOOLKITTY),
+            new User("rob", "tsuk", R.drawable.rob, User.TOOLKITTY),
+            new User("Teng-Hui", "Zhu", R.drawable.tenghui, User.TOOLKITTY),
+            new User("yigit", "boyar", R.drawable.yigit, User.TOOLKITTY),
+
+
+    };
+}
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png
new file mode 100644
index 0000000..583a065
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png
new file mode 100644
index 0000000..c0c9161
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png
new file mode 100644
index 0000000..06cc751
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png
new file mode 100644
index 0000000..11686c5
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png
new file mode 100644
index 0000000..fe744e0
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png
new file mode 100644
index 0000000..7bd0108
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png
new file mode 100644
index 0000000..fd41cb07
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png
new file mode 100644
index 0000000..7a9af15
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png
new file mode 100644
index 0000000..13442b0544
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png
new file mode 100644
index 0000000..57e9baf
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml
new file mode 100644
index 0000000..1aaf2f0
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    xmlns:bind="http://schemas.android.com/apk/res-auto"
+    xmlns:bind_var="http://schemas.android.com/apk/res-auto"
+    card_view:cardUseCompatPadding="true"
+    card_view:contentPadding="8dp"
+    android:orientation="horizontal" android:layout_width="wrap_content"
+    android:id="@+id/root"
+    android:focusable="true"
+    android:gravity="center_vertical"
+    bind:onClickListener="@{clickListener}"
+    android:layout_height="match_parent">
+    <variable name="user" type="com.android.example.bindingdemo.vo.User"/>
+    <variable name="clickListener" type="android.view.View.OnClickListener"/>
+    <ImageView
+        android:id="@+id/user_photo"
+        android:backgroundResource="@{user.photoResource}"
+        android:scaleType="fitCenter"
+        android:layout_width="@dimen/user_photo"
+        android:layout_height="@dimen/user_photo" />
+
+    <TextView
+        android:layout_marginLeft="@dimen/user_name_margin_left"
+        android:id="@+id/fullname"
+        android:gravity="center"
+        android:text='@{user.name.substring(0,1).toUpperCase() + "." + user.lastName}'
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent" />
+</android.support.v7.widget.CardView>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml
new file mode 100644
index 0000000..03d7368
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml
@@ -0,0 +1,141 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:bind="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:id="@+id/activityRoot"
+    tools:activity=".MainActivity"
+    android:clickable="true"
+    android:onClickListener="@{activity.onUnselect}">
+    <variable name="activity" type="com.android.example.bindingdemo.MainActivity"/>
+    <!---->
+    <import
+            type="android.view.View"
+            />
+    <!---->
+    <import type="com.android.example.bindingdemo.R.string" alias="Strings"/>
+    <import type="com.android.example.bindingdemo.vo.User"/>
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@{activity.getString(Strings.toolkitties, activity.tkAdapter.itemCount)}">
+
+    </TextView>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/toolkittyList"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/list_height"
+        bind:adapter="@{activity.tkAdapter}"
+        ></android.support.v7.widget.RecyclerView>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="10dp" />
+
+    <TextView android:text="@{activity.selected2 == activity.selected ? `same` : `different`}"
+              android:id="@+id/deleteme"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"/>
+
+    <android.support.v7.widget.CardView
+        android:id="@+id/selected_card"
+        bind:contentPadding="@{activity.selected == null ? 5 : activity.selected.name.length()}"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        bind:visibility="@{activity.selected == null ? View.INVISIBLE : View.VISIBLE}">
+
+        <GridLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:columnCount="2"
+            android:rowCount="4">
+
+            <ImageView
+                android:id="@+id/selected_photo"
+                android:layout_width="@dimen/big_user_photo"
+                android:layout_height="@dimen/big_user_photo"
+                android:layout_column="0"
+                android:layout_row="0"
+                android:layout_rowSpan="2"
+                android:scaleType="fitCenter"
+                android:backgroundResource="@{activity.selected.photoResource}" />
+
+            <EditText
+                android:id="@+id/selected_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_column="1"
+                android:layout_gravity="fill"
+                android:layout_row="0"
+                android:background="@android:color/holo_blue_dark"
+                android:gravity="center"
+                android:text="@{activity.selected.name}" />
+
+            <EditText
+                android:id="@+id/selected_lastname"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_column="1"
+                android:layout_gravity="fill"
+                android:layout_row="1"
+                android:background="@android:color/holo_blue_bright"
+                android:gravity="center"
+                android:text="@{activity.selected.lastName}" />
+            <Button
+                android:id="@+id/edit_button"
+                bind:onClickListener="@{activity.onSave}"
+                android:text="@{`Save changes to ` + activity.selected.name}"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_column="1"
+                android:layout_gravity="right"
+                android:layout_row="2"/>
+
+            <Button
+                android:id="@+id/delete_button"
+                bind:onClickListener="@{activity.onDelete}"
+                android:text="@{activity.getString(activity.selected.group == User.TOOLKITTY ? Strings.became_robot : Strings.became_kitten, activity.selected.name)}"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_column="1"
+                android:layout_gravity="right"
+                android:layout_row="3"/>
+
+        </GridLayout>
+    </android.support.v7.widget.CardView>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="10dp" />
+    <TextView
+        android:id="@+id/robotsTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@{activity.robotAdapter.itemCount + &quot; Robots &quot;}" />
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/robotList"
+        android:layout_width="match_parent" bind:adapter="@{activity.robotAdapter}"  android:layout_height="@dimen/list_height"
+        ></android.support.v7.widget.RecyclerView>
+</LinearLayout>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..ab38265
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" app:showAsAction="never" />
+</menu>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..793d18d
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..7c360c6
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="list_height">80dp</dimen>
+    <dimen name="user_photo">48dp</dimen>
+    <dimen name="user_name_margin_left">56dp</dimen>
+    <dimen name="big_user_photo">96dp</dimen>
+    <dimen name="item_width">200dp</dimen>
+</resources>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ea6f485
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<resources>
+
+    <string name="app_name">BindingDemo</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+    <string name="toolkitty_list">toolkitties</string>
+    <string name="became_robot">%s joined robots</string>
+    <string name="became_kitten">%s joined tool kitties</string>
+    <string name="toolkitties">%s ToolKitties</string>
+
+</resources>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..00d89fa
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>
diff --git a/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java b/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java
new file mode 100644
index 0000000..65449ce
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java
@@ -0,0 +1,40 @@
+package com.android.example.bindingdemo.vo;
+
+import android.databinding.OnPropertyChangedListener;
+
+import com.android.example.bindingdemo.R;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import com.android.example.bindingdemo.BR;
+public class UnitTest {
+
+    private User testUser;
+
+    @Before
+    public void setUp() throws Exception {
+        testUser = new User("Ted", "Tester", R.drawable.george, User.ROBOT);
+    }
+
+    @Test
+    public void settersWorkFineOnTheJvm() throws Exception {
+        assertEquals("Ted", testUser.getName());
+        testUser.setName("Tom");
+        assertEquals("Tom", testUser.getName());
+    }
+
+    @Test
+    public void listeners() throws Exception {
+        OnPropertyChangedListener mockListener = mock(OnPropertyChangedListener.class);
+        testUser.addOnPropertyChangedListener(mockListener);
+        testUser.setName("Tom");
+        verify(mockListener).onPropertyChanged(testUser, BR.name);
+        verifyNoMoreInteractions(mockListener);
+    }
+}
diff --git a/tools/data-binding/samples/BindingDemo/build.gradle b/tools/data-binding/samples/BindingDemo/build.gradle
new file mode 100644
index 0000000..18f0f56
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    repositories {
+        jcenter()
+        maven {
+            url "$projectDir/../../maven-repo"
+        }
+        mavenCentral()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:1.1.3"
+        classpath  'com.android.databinding:dataBinder:0.3-SNAPSHOT'
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url "$projectDir/../../../maven-repo"
+        }
+        mavenCentral()
+    }
+}
diff --git a/tools/data-binding/samples/BindingDemo/gradle.properties b/tools/data-binding/samples/BindingDemo/gradle.properties
new file mode 100644
index 0000000..f60a634
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/gradle.properties
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2014 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f02c72a
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Dec 17 11:22:31 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/data-binding/samples/BindingDemo/gradlew b/tools/data-binding/samples/BindingDemo/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/data-binding/samples/BindingDemo/gradlew.bat b/tools/data-binding/samples/BindingDemo/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/tools/data-binding/samples/BindingDemo/settings.gradle b/tools/data-binding/samples/BindingDemo/settings.gradle
new file mode 100644
index 0000000..c465666
--- /dev/null
+++ b/tools/data-binding/samples/BindingDemo/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+include ':app'
diff --git a/tools/data-binding/settings.gradle b/tools/data-binding/settings.gradle
new file mode 100644
index 0000000..2190518
--- /dev/null
+++ b/tools/data-binding/settings.gradle
@@ -0,0 +1,7 @@
+include ':baseLibrary', ':app'
+include ':library'
+include ':compiler'
+include ':gradlePlugin'
+include ':grammarBuilder'
+include ':annotationprocessor'
+include ':xmlGrammar'
diff --git a/tools/data-binding/xmlGrammar/XMLLexer.g4 b/tools/data-binding/xmlGrammar/XMLLexer.g4
new file mode 100644
index 0000000..ea7a23c
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/XMLLexer.g4
@@ -0,0 +1,93 @@
+/*
+ [The "BSD licence"]
+ Copyright (c) 2013 Terence Parr
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** XML lexer derived from ANTLR v4 ref guide book example */
+lexer grammar XMLLexer;
+
+// Default "mode": Everything OUTSIDE of a tag
+COMMENT     :   '<!--' .*? '-->' ;
+CDATA       :   '<![CDATA[' .*? ']]>' ;
+/** Scarf all DTD stuff, Entity Declarations like <!ENTITY ...>,
+ *  and Notation Declarations <!NOTATION ...>
+ */
+DTD         :   '<!' .*? '>'            -> skip ; 
+EntityRef   :   '&' Name ';' ;
+CharRef     :   '&#' DIGIT+ ';'
+            |   '&#x' HEXDIGIT+ ';'
+            ;
+SEA_WS      :   (' '|'\t'|'\r'? '\n')+ ;
+
+OPEN        :   '<'                     -> pushMode(INSIDE) ;
+XMLDeclOpen :   '<?xml' S               -> pushMode(INSIDE) ;
+SPECIAL_OPEN:   '<?' Name               -> more, pushMode(PROC_INSTR) ;
+
+TEXT        :   ~[<&]+ ;        // match any 16 bit char other than < and &
+
+// ----------------- Everything INSIDE of a tag ---------------------
+mode INSIDE;
+
+CLOSE       :   '>'                     -> popMode ;
+SPECIAL_CLOSE:  '?>'                    -> popMode ; // close <?xml...?>
+SLASH_CLOSE :   '/>'                    -> popMode ;
+SLASH       :   '/' ;
+EQUALS      :   '=' ;
+STRING      :   '"' ~[<"]* '"'
+            |   '\'' ~[<']* '\''
+            ;
+Name        :   NameStartChar NameChar* ;
+S           :   [ \t\r\n]               -> skip ;
+
+fragment
+HEXDIGIT    :   [a-fA-F0-9] ;
+
+fragment
+DIGIT       :   [0-9] ;
+
+fragment
+NameChar    :   NameStartChar
+            |   '-' | '_' | '.' | DIGIT 
+            |   '\u00B7'
+            |   '\u0300'..'\u036F'
+            |   '\u203F'..'\u2040'
+            ;
+
+fragment
+NameStartChar
+            :   [:a-zA-Z]
+            |   '\u2070'..'\u218F' 
+            |   '\u2C00'..'\u2FEF' 
+            |   '\u3001'..'\uD7FF' 
+            |   '\uF900'..'\uFDCF' 
+            |   '\uFDF0'..'\uFFFD'
+            ;
+
+// ----------------- Handle <? ... ?> ---------------------
+mode PROC_INSTR;
+
+PI          :   '?>'                    -> popMode ; // close <?...?>
+IGNORE      :   .                       -> more ;
diff --git a/tools/data-binding/xmlGrammar/XMLParser.g4 b/tools/data-binding/xmlGrammar/XMLParser.g4
new file mode 100644
index 0000000..1b03e2d
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/XMLParser.g4
@@ -0,0 +1,54 @@
+/*
+ [The "BSD licence"]
+ Copyright (c) 2013 Terence Parr
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** XML parser derived from ANTLR v4 ref guide book example */
+parser grammar XMLParser;
+
+options { tokenVocab=XMLLexer; }
+
+document    :   prolog? misc* element misc*;
+
+prolog      :   XMLDeclOpen attribute* SPECIAL_CLOSE ;
+
+content     :   chardata?
+                ((element | reference | CDATA | PI | COMMENT) chardata?)* ;
+
+element     :   '<' elmName=Name attribute* '>' content '<' '/' Name '>'
+            |   '<' elmName=Name attribute* '/>'
+            ;
+
+reference   :   EntityRef | CharRef ;
+
+attribute   :   attrName=Name '=' attrValue=STRING ; // Our STRING is AttValue in spec
+
+/** ``All text that is not markup constitutes the character data of
+ *  the document.''
+ */
+chardata    :   TEXT | SEA_WS ;
+
+misc        :   COMMENT | PI | SEA_WS ;
diff --git a/tools/data-binding/xmlGrammar/build.gradle b/tools/data-binding/xmlGrammar/build.gradle
new file mode 100644
index 0000000..c9d3607
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'java'
+apply plugin: 'kotlin'
+apply plugin: 'application'
+apply plugin: 'maven'
+
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+
+mainClassName = "org.antlr.v4.Tool"
+
+
+repositories {
+    mavenCentral()
+}
+
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}"
+    }
+}
+
+
+run {
+    args "XMLParser.g4", "-visitor", "-o", "src/main/java/android/databinding/parser", "-package", "android.databinding.parser", "-lib", "."
+}
+
+dependencies {
+    compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}"
+    compile 'com.tunnelvisionlabs:antlr4:4.4'
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.artifactId = 'xmlGrammer'
+        }
+    }
+}
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java
new file mode 100644
index 0000000..b8c79bb
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java
@@ -0,0 +1,136 @@
+// Generated from XMLLexer.g4 by ANTLR 4.4
+package android.databinding.parser;
+
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.atn.ATN;
+import org.antlr.v4.runtime.atn.ATNDeserializer;
+import org.antlr.v4.runtime.atn.LexerATNSimulator;
+
+public class XMLLexer extends Lexer {
+	public static final int
+		COMMENT=1, CDATA=2, DTD=3, EntityRef=4, CharRef=5, SEA_WS=6, OPEN=7, XMLDeclOpen=8, 
+		TEXT=9, CLOSE=10, SPECIAL_CLOSE=11, SLASH_CLOSE=12, SLASH=13, EQUALS=14, 
+		STRING=15, Name=16, S=17, PI=18;
+	public static final int INSIDE = 1;
+	public static final int PROC_INSTR = 2;
+	public static String[] modeNames = {
+		"DEFAULT_MODE", "INSIDE", "PROC_INSTR"
+	};
+
+	public static final String[] tokenNames = {
+		"'\\u0000'", "'\\u0001'", "'\\u0002'", "'\\u0003'", "'\\u0004'", "'\\u0005'", 
+		"'\\u0006'", "'\\u0007'", "'\b'", "'\t'", "'\n'", "'\\u000B'", "'\f'", 
+		"'\r'", "'\\u000E'", "'\\u000F'", "'\\u0010'", "'\\u0011'", "'\\u0012'"
+	};
+	public static final String[] ruleNames = {
+		"COMMENT", "CDATA", "DTD", "EntityRef", "CharRef", "SEA_WS", "OPEN", "XMLDeclOpen", 
+		"SPECIAL_OPEN", "TEXT", "CLOSE", "SPECIAL_CLOSE", "SLASH_CLOSE", "SLASH", 
+		"EQUALS", "STRING", "Name", "S", "HEXDIGIT", "DIGIT", "NameChar", "NameStartChar", 
+		"PI", "IGNORE"
+	};
+
+
+	public XMLLexer(CharStream input) {
+		super(input);
+		_interp = new LexerATNSimulator(this,_ATN);
+	}
+
+	@Override
+	public String getGrammarFileName() { return "XMLLexer.g4"; }
+
+	@Override
+	public String[] getTokenNames() { return tokenNames; }
+
+	@Override
+	public String[] getRuleNames() { return ruleNames; }
+
+	@Override
+	public String getSerializedATN() { return _serializedATN; }
+
+	@Override
+	public String[] getModeNames() { return modeNames; }
+
+	public static final String _serializedATN =
+		"\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\2\24\u00e9\b\1\b\1"+
+		"\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4"+
+		"\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t"+
+		"\21\4\22\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t"+
+		"\30\4\31\t\31\3\2\3\2\3\2\3\2\3\2\3\2\7\2<\n\2\f\2\16\2?\13\2\3\2\3\2"+
+		"\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\7\3P\n\3\f\3\16\3"+
+		"S\13\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\7\4]\n\4\f\4\16\4`\13\4\3\4\3\4"+
+		"\3\4\3\4\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\6\6n\n\6\r\6\16\6o\3\6\3\6\3"+
+		"\6\3\6\3\6\3\6\3\6\6\6y\n\6\r\6\16\6z\3\6\3\6\5\6\177\n\6\3\7\3\7\5\7"+
+		"\u0083\n\7\3\7\6\7\u0086\n\7\r\7\16\7\u0087\3\b\3\b\3\b\3\b\3\t\3\t\3"+
+		"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\6"+
+		"\13\u00a1\n\13\r\13\16\13\u00a2\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3"+
+		"\16\3\16\3\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\7\21\u00b9\n\21"+
+		"\f\21\16\21\u00bc\13\21\3\21\3\21\3\21\7\21\u00c1\n\21\f\21\16\21\u00c4"+
+		"\13\21\3\21\5\21\u00c7\n\21\3\22\3\22\7\22\u00cb\n\22\f\22\16\22\u00ce"+
+		"\13\22\3\23\3\23\3\23\3\23\3\24\3\24\3\25\3\25\3\26\3\26\3\26\3\26\5\26"+
+		"\u00dc\n\26\3\27\5\27\u00df\n\27\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3"+
+		"\31\3\31\5=Q^\2\2\32\5\2\3\7\2\4\t\2\5\13\2\6\r\2\7\17\2\b\21\2\t\23\2"+
+		"\n\25\2\2\27\2\13\31\2\f\33\2\r\35\2\16\37\2\17!\2\20#\2\21%\2\22\'\2"+
+		"\23)\2\2+\2\2-\2\2/\2\2\61\2\24\63\2\2\5\2\3\4\f\4\2\13\13\"\"\4\2((>"+
+		">\4\2$$>>\4\2))>>\5\2\13\f\17\17\"\"\5\2\62;CHch\3\2\62;\4\2/\60aa\5\2"+
+		"\u00b9\u00b9\u0302\u0371\u2041\u2042\n\2<<C\\c|\u2072\u2191\u2c02\u2ff1"+
+		"\u3003\ud801\uf902\ufdd1\ufdf2\uffff\u00f3\2\5\3\2\2\2\2\7\3\2\2\2\2\t"+
+		"\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2"+
+		"\2\2\25\3\2\2\2\2\27\3\2\2\2\3\31\3\2\2\2\3\33\3\2\2\2\3\35\3\2\2\2\3"+
+		"\37\3\2\2\2\3!\3\2\2\2\3#\3\2\2\2\3%\3\2\2\2\3\'\3\2\2\2\4\61\3\2\2\2"+
+		"\4\63\3\2\2\2\5\65\3\2\2\2\7D\3\2\2\2\tX\3\2\2\2\13e\3\2\2\2\r~\3\2\2"+
+		"\2\17\u0085\3\2\2\2\21\u0089\3\2\2\2\23\u008d\3\2\2\2\25\u0097\3\2\2\2"+
+		"\27\u00a0\3\2\2\2\31\u00a4\3\2\2\2\33\u00a8\3\2\2\2\35\u00ad\3\2\2\2\37"+
+		"\u00b2\3\2\2\2!\u00b4\3\2\2\2#\u00c6\3\2\2\2%\u00c8\3\2\2\2\'\u00cf\3"+
+		"\2\2\2)\u00d3\3\2\2\2+\u00d5\3\2\2\2-\u00db\3\2\2\2/\u00de\3\2\2\2\61"+
+		"\u00e0\3\2\2\2\63\u00e5\3\2\2\2\65\66\7>\2\2\66\67\7#\2\2\678\7/\2\28"+
+		"9\7/\2\29=\3\2\2\2:<\13\2\2\2;:\3\2\2\2<?\3\2\2\2=>\3\2\2\2=;\3\2\2\2"+
+		">@\3\2\2\2?=\3\2\2\2@A\7/\2\2AB\7/\2\2BC\7@\2\2C\6\3\2\2\2DE\7>\2\2EF"+
+		"\7#\2\2FG\7]\2\2GH\7E\2\2HI\7F\2\2IJ\7C\2\2JK\7V\2\2KL\7C\2\2LM\7]\2\2"+
+		"MQ\3\2\2\2NP\13\2\2\2ON\3\2\2\2PS\3\2\2\2QR\3\2\2\2QO\3\2\2\2RT\3\2\2"+
+		"\2SQ\3\2\2\2TU\7_\2\2UV\7_\2\2VW\7@\2\2W\b\3\2\2\2XY\7>\2\2YZ\7#\2\2Z"+
+		"^\3\2\2\2[]\13\2\2\2\\[\3\2\2\2]`\3\2\2\2^_\3\2\2\2^\\\3\2\2\2_a\3\2\2"+
+		"\2`^\3\2\2\2ab\7@\2\2bc\3\2\2\2cd\b\4\2\2d\n\3\2\2\2ef\7(\2\2fg\5%\22"+
+		"\2gh\7=\2\2h\f\3\2\2\2ij\7(\2\2jk\7%\2\2km\3\2\2\2ln\5+\25\2ml\3\2\2\2"+
+		"no\3\2\2\2om\3\2\2\2op\3\2\2\2pq\3\2\2\2qr\7=\2\2r\177\3\2\2\2st\7(\2"+
+		"\2tu\7%\2\2uv\7z\2\2vx\3\2\2\2wy\5)\24\2xw\3\2\2\2yz\3\2\2\2zx\3\2\2\2"+
+		"z{\3\2\2\2{|\3\2\2\2|}\7=\2\2}\177\3\2\2\2~i\3\2\2\2~s\3\2\2\2\177\16"+
+		"\3\2\2\2\u0080\u0086\t\2\2\2\u0081\u0083\7\17\2\2\u0082\u0081\3\2\2\2"+
+		"\u0082\u0083\3\2\2\2\u0083\u0084\3\2\2\2\u0084\u0086\7\f\2\2\u0085\u0080"+
+		"\3\2\2\2\u0085\u0082\3\2\2\2\u0086\u0087\3\2\2\2\u0087\u0085\3\2\2\2\u0087"+
+		"\u0088\3\2\2\2\u0088\20\3\2\2\2\u0089\u008a\7>\2\2\u008a\u008b\3\2\2\2"+
+		"\u008b\u008c\b\b\3\2\u008c\22\3\2\2\2\u008d\u008e\7>\2\2\u008e\u008f\7"+
+		"A\2\2\u008f\u0090\7z\2\2\u0090\u0091\7o\2\2\u0091\u0092\7n\2\2\u0092\u0093"+
+		"\3\2\2\2\u0093\u0094\5\'\23\2\u0094\u0095\3\2\2\2\u0095\u0096\b\t\3\2"+
+		"\u0096\24\3\2\2\2\u0097\u0098\7>\2\2\u0098\u0099\7A\2\2\u0099\u009a\3"+
+		"\2\2\2\u009a\u009b\5%\22\2\u009b\u009c\3\2\2\2\u009c\u009d\b\n\4\2\u009d"+
+		"\u009e\b\n\5\2\u009e\26\3\2\2\2\u009f\u00a1\n\3\2\2\u00a0\u009f\3\2\2"+
+		"\2\u00a1\u00a2\3\2\2\2\u00a2\u00a0\3\2\2\2\u00a2\u00a3\3\2\2\2\u00a3\30"+
+		"\3\2\2\2\u00a4\u00a5\7@\2\2\u00a5\u00a6\3\2\2\2\u00a6\u00a7\b\f\6\2\u00a7"+
+		"\32\3\2\2\2\u00a8\u00a9\7A\2\2\u00a9\u00aa\7@\2\2\u00aa\u00ab\3\2\2\2"+
+		"\u00ab\u00ac\b\r\6\2\u00ac\34\3\2\2\2\u00ad\u00ae\7\61\2\2\u00ae\u00af"+
+		"\7@\2\2\u00af\u00b0\3\2\2\2\u00b0\u00b1\b\16\6\2\u00b1\36\3\2\2\2\u00b2"+
+		"\u00b3\7\61\2\2\u00b3 \3\2\2\2\u00b4\u00b5\7?\2\2\u00b5\"\3\2\2\2\u00b6"+
+		"\u00ba\7$\2\2\u00b7\u00b9\n\4\2\2\u00b8\u00b7\3\2\2\2\u00b9\u00bc\3\2"+
+		"\2\2\u00ba\u00b8\3\2\2\2\u00ba\u00bb\3\2\2\2\u00bb\u00bd\3\2\2\2\u00bc"+
+		"\u00ba\3\2\2\2\u00bd\u00c7\7$\2\2\u00be\u00c2\7)\2\2\u00bf\u00c1\n\5\2"+
+		"\2\u00c0\u00bf\3\2\2\2\u00c1\u00c4\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c2\u00c3"+
+		"\3\2\2\2\u00c3\u00c5\3\2\2\2\u00c4\u00c2\3\2\2\2\u00c5\u00c7\7)\2\2\u00c6"+
+		"\u00b6\3\2\2\2\u00c6\u00be\3\2\2\2\u00c7$\3\2\2\2\u00c8\u00cc\5/\27\2"+
+		"\u00c9\u00cb\5-\26\2\u00ca\u00c9\3\2\2\2\u00cb\u00ce\3\2\2\2\u00cc\u00ca"+
+		"\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd&\3\2\2\2\u00ce\u00cc\3\2\2\2\u00cf"+
+		"\u00d0\t\6\2\2\u00d0\u00d1\3\2\2\2\u00d1\u00d2\b\23\2\2\u00d2(\3\2\2\2"+
+		"\u00d3\u00d4\t\7\2\2\u00d4*\3\2\2\2\u00d5\u00d6\t\b\2\2\u00d6,\3\2\2\2"+
+		"\u00d7\u00dc\5/\27\2\u00d8\u00dc\t\t\2\2\u00d9\u00dc\5+\25\2\u00da\u00dc"+
+		"\t\n\2\2\u00db\u00d7\3\2\2\2\u00db\u00d8\3\2\2\2\u00db\u00d9\3\2\2\2\u00db"+
+		"\u00da\3\2\2\2\u00dc.\3\2\2\2\u00dd\u00df\t\13\2\2\u00de\u00dd\3\2\2\2"+
+		"\u00df\60\3\2\2\2\u00e0\u00e1\7A\2\2\u00e1\u00e2\7@\2\2\u00e2\u00e3\3"+
+		"\2\2\2\u00e3\u00e4\b\30\6\2\u00e4\62\3\2\2\2\u00e5\u00e6\13\2\2\2\u00e6"+
+		"\u00e7\3\2\2\2\u00e7\u00e8\b\31\4\2\u00e8\64\3\2\2\2\25\2\3\4=Q^oz~\u0082"+
+		"\u0085\u0087\u00a2\u00ba\u00c2\u00c6\u00cc\u00db\u00de\7\b\2\2\7\3\2\5"+
+		"\2\2\7\4\2\6\2\2";
+	public static final ATN _ATN =
+		new ATNDeserializer().deserialize(_serializedATN.toCharArray());
+	static {
+	}
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens
new file mode 100644
index 0000000..cd122a4
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens
@@ -0,0 +1,23 @@
+OPEN=7
+CDATA=2
+SLASH=13
+CharRef=5
+SEA_WS=6
+SPECIAL_CLOSE=11
+CLOSE=10
+DTD=3
+Name=16
+EQUALS=14
+PI=18
+S=17
+SLASH_CLOSE=12
+TEXT=9
+COMMENT=1
+XMLDeclOpen=8
+EntityRef=4
+STRING=15
+'='=14
+'/'=13
+'<'=7
+'/>'=12
+'>'=10
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java
new file mode 100644
index 0000000..f18a5f0
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java
@@ -0,0 +1,660 @@
+// Generated from XMLParser.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.atn.*;
+import org.antlr.v4.runtime.dfa.DFA;
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.misc.*;
+import org.antlr.v4.runtime.tree.*;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+public class XMLParser extends Parser {
+	public static final int
+		OPEN=7, CDATA=2, SLASH=13, CharRef=5, SEA_WS=6, SPECIAL_CLOSE=11, CLOSE=10, 
+		DTD=3, Name=16, EQUALS=14, PI=18, S=17, SLASH_CLOSE=12, TEXT=9, COMMENT=1, 
+		XMLDeclOpen=8, EntityRef=4, STRING=15;
+	public static final String[] tokenNames = {
+		"<INVALID>", "COMMENT", "CDATA", "DTD", "EntityRef", "CharRef", "SEA_WS", 
+		"'<'", "XMLDeclOpen", "TEXT", "'>'", "SPECIAL_CLOSE", "'/>'", "'/'", "'='", 
+		"STRING", "Name", "S", "PI"
+	};
+	public static final int
+		RULE_document = 0, RULE_prolog = 1, RULE_content = 2, RULE_element = 3, 
+		RULE_reference = 4, RULE_attribute = 5, RULE_chardata = 6, RULE_misc = 7;
+	public static final String[] ruleNames = {
+		"document", "prolog", "content", "element", "reference", "attribute", 
+		"chardata", "misc"
+	};
+
+	@Override
+	public String getGrammarFileName() { return "XMLParser.g4"; }
+
+	@Override
+	public String[] getTokenNames() { return tokenNames; }
+
+	@Override
+	public String[] getRuleNames() { return ruleNames; }
+
+	@Override
+	public String getSerializedATN() { return _serializedATN; }
+
+	public XMLParser(TokenStream input) {
+		super(input);
+		_interp = new ParserATNSimulator(this,_ATN);
+	}
+	public static class DocumentContext extends ParserRuleContext {
+		public ElementContext element() {
+			return getRuleContext(ElementContext.class,0);
+		}
+		public List<? extends MiscContext> misc() {
+			return getRuleContexts(MiscContext.class);
+		}
+		public PrologContext prolog() {
+			return getRuleContext(PrologContext.class,0);
+		}
+		public MiscContext misc(int i) {
+			return getRuleContext(MiscContext.class,i);
+		}
+		public DocumentContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_document; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterDocument(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitDocument(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitDocument(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final DocumentContext document() throws RecognitionException {
+		DocumentContext _localctx = new DocumentContext(_ctx, getState());
+		enterRule(_localctx, 0, RULE_document);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(17);
+			_la = _input.LA(1);
+			if (_la==XMLDeclOpen) {
+				{
+				setState(16); prolog();
+				}
+			}
+
+			setState(22);
+			_errHandler.sync(this);
+			_la = _input.LA(1);
+			while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) {
+				{
+				{
+				setState(19); misc();
+				}
+				}
+				setState(24);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+			}
+			setState(25); element();
+			setState(29);
+			_errHandler.sync(this);
+			_la = _input.LA(1);
+			while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) {
+				{
+				{
+				setState(26); misc();
+				}
+				}
+				setState(31);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class PrologContext extends ParserRuleContext {
+		public TerminalNode SPECIAL_CLOSE() { return getToken(XMLParser.SPECIAL_CLOSE, 0); }
+		public List<? extends AttributeContext> attribute() {
+			return getRuleContexts(AttributeContext.class);
+		}
+		public AttributeContext attribute(int i) {
+			return getRuleContext(AttributeContext.class,i);
+		}
+		public TerminalNode XMLDeclOpen() { return getToken(XMLParser.XMLDeclOpen, 0); }
+		public PrologContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_prolog; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterProlog(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitProlog(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitProlog(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final PrologContext prolog() throws RecognitionException {
+		PrologContext _localctx = new PrologContext(_ctx, getState());
+		enterRule(_localctx, 2, RULE_prolog);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(32); match(XMLDeclOpen);
+			setState(36);
+			_errHandler.sync(this);
+			_la = _input.LA(1);
+			while (_la==Name) {
+				{
+				{
+				setState(33); attribute();
+				}
+				}
+				setState(38);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+			}
+			setState(39); match(SPECIAL_CLOSE);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ContentContext extends ParserRuleContext {
+		public List<? extends TerminalNode> PI() { return getTokens(XMLParser.PI); }
+		public List<? extends TerminalNode> CDATA() { return getTokens(XMLParser.CDATA); }
+		public List<? extends ElementContext> element() {
+			return getRuleContexts(ElementContext.class);
+		}
+		public TerminalNode PI(int i) {
+			return getToken(XMLParser.PI, i);
+		}
+		public ElementContext element(int i) {
+			return getRuleContext(ElementContext.class,i);
+		}
+		public TerminalNode COMMENT(int i) {
+			return getToken(XMLParser.COMMENT, i);
+		}
+		public TerminalNode CDATA(int i) {
+			return getToken(XMLParser.CDATA, i);
+		}
+		public ReferenceContext reference(int i) {
+			return getRuleContext(ReferenceContext.class,i);
+		}
+		public List<? extends TerminalNode> COMMENT() { return getTokens(XMLParser.COMMENT); }
+		public ChardataContext chardata(int i) {
+			return getRuleContext(ChardataContext.class,i);
+		}
+		public List<? extends ChardataContext> chardata() {
+			return getRuleContexts(ChardataContext.class);
+		}
+		public List<? extends ReferenceContext> reference() {
+			return getRuleContexts(ReferenceContext.class);
+		}
+		public ContentContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_content; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterContent(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitContent(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitContent(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ContentContext content() throws RecognitionException {
+		ContentContext _localctx = new ContentContext(_ctx, getState());
+		enterRule(_localctx, 4, RULE_content);
+		int _la;
+		try {
+			int _alt;
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(42);
+			_la = _input.LA(1);
+			if (_la==SEA_WS || _la==TEXT) {
+				{
+				setState(41); chardata();
+				}
+			}
+
+			setState(56);
+			_errHandler.sync(this);
+			_alt = getInterpreter().adaptivePredict(_input,7,_ctx);
+			while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
+				if ( _alt==1 ) {
+					{
+					{
+					setState(49);
+					switch (_input.LA(1)) {
+					case OPEN:
+						{
+						setState(44); element();
+						}
+						break;
+					case EntityRef:
+					case CharRef:
+						{
+						setState(45); reference();
+						}
+						break;
+					case CDATA:
+						{
+						setState(46); match(CDATA);
+						}
+						break;
+					case PI:
+						{
+						setState(47); match(PI);
+						}
+						break;
+					case COMMENT:
+						{
+						setState(48); match(COMMENT);
+						}
+						break;
+					default:
+						throw new NoViableAltException(this);
+					}
+					setState(52);
+					_la = _input.LA(1);
+					if (_la==SEA_WS || _la==TEXT) {
+						{
+						setState(51); chardata();
+						}
+					}
+
+					}
+					} 
+				}
+				setState(58);
+				_errHandler.sync(this);
+				_alt = getInterpreter().adaptivePredict(_input,7,_ctx);
+			}
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ElementContext extends ParserRuleContext {
+		public Token elmName;
+		public List<? extends AttributeContext> attribute() {
+			return getRuleContexts(AttributeContext.class);
+		}
+		public AttributeContext attribute(int i) {
+			return getRuleContext(AttributeContext.class,i);
+		}
+		public TerminalNode Name(int i) {
+			return getToken(XMLParser.Name, i);
+		}
+		public List<? extends TerminalNode> Name() { return getTokens(XMLParser.Name); }
+		public ContentContext content() {
+			return getRuleContext(ContentContext.class,0);
+		}
+		public ElementContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_element; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterElement(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitElement(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitElement(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ElementContext element() throws RecognitionException {
+		ElementContext _localctx = new ElementContext(_ctx, getState());
+		enterRule(_localctx, 6, RULE_element);
+		int _la;
+		try {
+			setState(83);
+			switch ( getInterpreter().adaptivePredict(_input,10,_ctx) ) {
+			case 1:
+				enterOuterAlt(_localctx, 1);
+				{
+				setState(59); match(OPEN);
+				setState(60); _localctx.elmName = match(Name);
+				setState(64);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+				while (_la==Name) {
+					{
+					{
+					setState(61); attribute();
+					}
+					}
+					setState(66);
+					_errHandler.sync(this);
+					_la = _input.LA(1);
+				}
+				setState(67); match(CLOSE);
+				setState(68); content();
+				setState(69); match(OPEN);
+				setState(70); match(SLASH);
+				setState(71); match(Name);
+				setState(72); match(CLOSE);
+				}
+				break;
+
+			case 2:
+				enterOuterAlt(_localctx, 2);
+				{
+				setState(74); match(OPEN);
+				setState(75); _localctx.elmName = match(Name);
+				setState(79);
+				_errHandler.sync(this);
+				_la = _input.LA(1);
+				while (_la==Name) {
+					{
+					{
+					setState(76); attribute();
+					}
+					}
+					setState(81);
+					_errHandler.sync(this);
+					_la = _input.LA(1);
+				}
+				setState(82); match(SLASH_CLOSE);
+				}
+				break;
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ReferenceContext extends ParserRuleContext {
+		public TerminalNode CharRef() { return getToken(XMLParser.CharRef, 0); }
+		public TerminalNode EntityRef() { return getToken(XMLParser.EntityRef, 0); }
+		public ReferenceContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_reference; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterReference(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitReference(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitReference(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ReferenceContext reference() throws RecognitionException {
+		ReferenceContext _localctx = new ReferenceContext(_ctx, getState());
+		enterRule(_localctx, 8, RULE_reference);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(85);
+			_la = _input.LA(1);
+			if ( !(_la==EntityRef || _la==CharRef) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class AttributeContext extends ParserRuleContext {
+		public Token attrName;
+		public Token attrValue;
+		public TerminalNode Name() { return getToken(XMLParser.Name, 0); }
+		public TerminalNode STRING() { return getToken(XMLParser.STRING, 0); }
+		public AttributeContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_attribute; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterAttribute(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitAttribute(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitAttribute(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final AttributeContext attribute() throws RecognitionException {
+		AttributeContext _localctx = new AttributeContext(_ctx, getState());
+		enterRule(_localctx, 10, RULE_attribute);
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(87); _localctx.attrName = match(Name);
+			setState(88); match(EQUALS);
+			setState(89); _localctx.attrValue = match(STRING);
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class ChardataContext extends ParserRuleContext {
+		public TerminalNode SEA_WS() { return getToken(XMLParser.SEA_WS, 0); }
+		public TerminalNode TEXT() { return getToken(XMLParser.TEXT, 0); }
+		public ChardataContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_chardata; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterChardata(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitChardata(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitChardata(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final ChardataContext chardata() throws RecognitionException {
+		ChardataContext _localctx = new ChardataContext(_ctx, getState());
+		enterRule(_localctx, 12, RULE_chardata);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(91);
+			_la = _input.LA(1);
+			if ( !(_la==SEA_WS || _la==TEXT) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static class MiscContext extends ParserRuleContext {
+		public TerminalNode SEA_WS() { return getToken(XMLParser.SEA_WS, 0); }
+		public TerminalNode PI() { return getToken(XMLParser.PI, 0); }
+		public TerminalNode COMMENT() { return getToken(XMLParser.COMMENT, 0); }
+		public MiscContext(ParserRuleContext parent, int invokingState) {
+			super(parent, invokingState);
+		}
+		@Override public int getRuleIndex() { return RULE_misc; }
+		@Override
+		public void enterRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterMisc(this);
+		}
+		@Override
+		public void exitRule(ParseTreeListener listener) {
+			if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitMisc(this);
+		}
+		@Override
+		public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) {
+			if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitMisc(this);
+			else return visitor.visitChildren(this);
+		}
+	}
+
+	@RuleVersion(0)
+	public final MiscContext misc() throws RecognitionException {
+		MiscContext _localctx = new MiscContext(_ctx, getState());
+		enterRule(_localctx, 14, RULE_misc);
+		int _la;
+		try {
+			enterOuterAlt(_localctx, 1);
+			{
+			setState(93);
+			_la = _input.LA(1);
+			if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) ) {
+			_errHandler.recoverInline(this);
+			}
+			consume();
+			}
+		}
+		catch (RecognitionException re) {
+			_localctx.exception = re;
+			_errHandler.reportError(this, re);
+			_errHandler.recover(this, re);
+		}
+		finally {
+			exitRule();
+		}
+		return _localctx;
+	}
+
+	public static final String _serializedATN =
+		"\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\3\24b\4\2\t\2\4\3\t"+
+		"\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\3\2\5\2\24\n\2\3\2"+
+		"\7\2\27\n\2\f\2\16\2\32\13\2\3\2\3\2\7\2\36\n\2\f\2\16\2!\13\2\3\3\3\3"+
+		"\7\3%\n\3\f\3\16\3(\13\3\3\3\3\3\3\4\5\4-\n\4\3\4\3\4\3\4\3\4\3\4\5\4"+
+		"\64\n\4\3\4\5\4\67\n\4\7\49\n\4\f\4\16\4<\13\4\3\5\3\5\3\5\7\5A\n\5\f"+
+		"\5\16\5D\13\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\7\5P\n\5\f\5\16"+
+		"\5S\13\5\3\5\5\5V\n\5\3\6\3\6\3\7\3\7\3\7\3\7\3\b\3\b\3\t\3\t\3\t\2\2"+
+		"\2\n\2\2\4\2\6\2\b\2\n\2\f\2\16\2\20\2\2\5\3\2\6\7\4\2\b\b\13\13\5\2\3"+
+		"\3\b\b\24\24g\2\23\3\2\2\2\4\"\3\2\2\2\6,\3\2\2\2\bU\3\2\2\2\nW\3\2\2"+
+		"\2\fY\3\2\2\2\16]\3\2\2\2\20_\3\2\2\2\22\24\5\4\3\2\23\22\3\2\2\2\23\24"+
+		"\3\2\2\2\24\30\3\2\2\2\25\27\5\20\t\2\26\25\3\2\2\2\27\32\3\2\2\2\30\26"+
+		"\3\2\2\2\30\31\3\2\2\2\31\33\3\2\2\2\32\30\3\2\2\2\33\37\5\b\5\2\34\36"+
+		"\5\20\t\2\35\34\3\2\2\2\36!\3\2\2\2\37\35\3\2\2\2\37 \3\2\2\2 \3\3\2\2"+
+		"\2!\37\3\2\2\2\"&\7\n\2\2#%\5\f\7\2$#\3\2\2\2%(\3\2\2\2&$\3\2\2\2&\'\3"+
+		"\2\2\2\')\3\2\2\2(&\3\2\2\2)*\7\r\2\2*\5\3\2\2\2+-\5\16\b\2,+\3\2\2\2"+
+		",-\3\2\2\2-:\3\2\2\2.\64\5\b\5\2/\64\5\n\6\2\60\64\7\4\2\2\61\64\7\24"+
+		"\2\2\62\64\7\3\2\2\63.\3\2\2\2\63/\3\2\2\2\63\60\3\2\2\2\63\61\3\2\2\2"+
+		"\63\62\3\2\2\2\64\66\3\2\2\2\65\67\5\16\b\2\66\65\3\2\2\2\66\67\3\2\2"+
+		"\2\679\3\2\2\28\63\3\2\2\29<\3\2\2\2:8\3\2\2\2:;\3\2\2\2;\7\3\2\2\2<:"+
+		"\3\2\2\2=>\7\t\2\2>B\7\22\2\2?A\5\f\7\2@?\3\2\2\2AD\3\2\2\2B@\3\2\2\2"+
+		"BC\3\2\2\2CE\3\2\2\2DB\3\2\2\2EF\7\f\2\2FG\5\6\4\2GH\7\t\2\2HI\7\17\2"+
+		"\2IJ\7\22\2\2JK\7\f\2\2KV\3\2\2\2LM\7\t\2\2MQ\7\22\2\2NP\5\f\7\2ON\3\2"+
+		"\2\2PS\3\2\2\2QO\3\2\2\2QR\3\2\2\2RT\3\2\2\2SQ\3\2\2\2TV\7\16\2\2U=\3"+
+		"\2\2\2UL\3\2\2\2V\t\3\2\2\2WX\t\2\2\2X\13\3\2\2\2YZ\7\22\2\2Z[\7\20\2"+
+		"\2[\\\7\21\2\2\\\r\3\2\2\2]^\t\3\2\2^\17\3\2\2\2_`\t\4\2\2`\21\3\2\2\2"+
+		"\r\23\30\37&,\63\66:BQU";
+	public static final ATN _ATN =
+		new ATNDeserializer().deserialize(_serializedATN.toCharArray());
+	static {
+	}
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens
new file mode 100644
index 0000000..b1423a1
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens
@@ -0,0 +1,23 @@
+OPEN=7
+CDATA=2
+SLASH=13
+CharRef=5
+SEA_WS=6
+SPECIAL_CLOSE=11
+CLOSE=10
+DTD=3
+Name=16
+EQUALS=14
+PI=18
+SLASH_CLOSE=12
+S=17
+TEXT=9
+XMLDeclOpen=8
+COMMENT=1
+EntityRef=4
+STRING=15
+'='=14
+'<'=7
+'/'=13
+'/>'=12
+'>'=10
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java
new file mode 100644
index 0000000..4c2bae2
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java
@@ -0,0 +1,144 @@
+// Generated from XMLParser.g4 by ANTLR 4.4
+package android.databinding.parser;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ * This class provides an empty implementation of {@link XMLParserListener},
+ * which can be extended to create a listener which only needs to handle a subset
+ * of the available methods.
+ */
+public class XMLParserBaseListener implements XMLParserListener {
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterContent(@NotNull XMLParser.ContentContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitContent(@NotNull XMLParser.ContentContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterElement(@NotNull XMLParser.ElementContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitElement(@NotNull XMLParser.ElementContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterProlog(@NotNull XMLParser.PrologContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitProlog(@NotNull XMLParser.PrologContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterDocument(@NotNull XMLParser.DocumentContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitDocument(@NotNull XMLParser.DocumentContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterAttribute(@NotNull XMLParser.AttributeContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitAttribute(@NotNull XMLParser.AttributeContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterChardata(@NotNull XMLParser.ChardataContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitChardata(@NotNull XMLParser.ChardataContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterReference(@NotNull XMLParser.ReferenceContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitReference(@NotNull XMLParser.ReferenceContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterMisc(@NotNull XMLParser.MiscContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitMisc(@NotNull XMLParser.MiscContext ctx) { }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void enterEveryRule(@NotNull ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void exitEveryRule(@NotNull ParserRuleContext ctx) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitTerminal(@NotNull TerminalNode node) { }
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation does nothing.</p>
+	 */
+	@Override public void visitErrorNode(@NotNull ErrorNode node) { }
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java
new file mode 100644
index 0000000..6b04b77
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java
@@ -0,0 +1,79 @@
+// Generated from XMLParser.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
+
+/**
+ * This class provides an empty implementation of {@link XMLParserVisitor},
+ * which can be extended to create a visitor which only needs to handle a subset
+ * of the available methods.
+ *
+ * @param <Result> The return type of the visit operation. Use {@link Void} for
+ * operations with no return type.
+ */
+public class XMLParserBaseVisitor<Result> extends AbstractParseTreeVisitor<Result> implements XMLParserVisitor<Result> {
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitContent(@NotNull XMLParser.ContentContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitElement(@NotNull XMLParser.ElementContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitProlog(@NotNull XMLParser.PrologContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitDocument(@NotNull XMLParser.DocumentContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitAttribute(@NotNull XMLParser.AttributeContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitChardata(@NotNull XMLParser.ChardataContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitReference(@NotNull XMLParser.ReferenceContext ctx) { return visitChildren(ctx); }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p>The default implementation returns the result of calling
+	 * {@link #visitChildren} on {@code ctx}.</p>
+	 */
+	@Override public Result visitMisc(@NotNull XMLParser.MiscContext ctx) { return visitChildren(ctx); }
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java
new file mode 100644
index 0000000..6bee172
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java
@@ -0,0 +1,99 @@
+// Generated from XMLParser.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTreeListener;
+
+/**
+ * This interface defines a complete listener for a parse tree produced by
+ * {@link XMLParser}.
+ */
+public interface XMLParserListener extends ParseTreeListener {
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#content}.
+	 * @param ctx the parse tree
+	 */
+	void enterContent(@NotNull XMLParser.ContentContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#content}.
+	 * @param ctx the parse tree
+	 */
+	void exitContent(@NotNull XMLParser.ContentContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#element}.
+	 * @param ctx the parse tree
+	 */
+	void enterElement(@NotNull XMLParser.ElementContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#element}.
+	 * @param ctx the parse tree
+	 */
+	void exitElement(@NotNull XMLParser.ElementContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#prolog}.
+	 * @param ctx the parse tree
+	 */
+	void enterProlog(@NotNull XMLParser.PrologContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#prolog}.
+	 * @param ctx the parse tree
+	 */
+	void exitProlog(@NotNull XMLParser.PrologContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#document}.
+	 * @param ctx the parse tree
+	 */
+	void enterDocument(@NotNull XMLParser.DocumentContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#document}.
+	 * @param ctx the parse tree
+	 */
+	void exitDocument(@NotNull XMLParser.DocumentContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#attribute}.
+	 * @param ctx the parse tree
+	 */
+	void enterAttribute(@NotNull XMLParser.AttributeContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#attribute}.
+	 * @param ctx the parse tree
+	 */
+	void exitAttribute(@NotNull XMLParser.AttributeContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#chardata}.
+	 * @param ctx the parse tree
+	 */
+	void enterChardata(@NotNull XMLParser.ChardataContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#chardata}.
+	 * @param ctx the parse tree
+	 */
+	void exitChardata(@NotNull XMLParser.ChardataContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#reference}.
+	 * @param ctx the parse tree
+	 */
+	void enterReference(@NotNull XMLParser.ReferenceContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#reference}.
+	 * @param ctx the parse tree
+	 */
+	void exitReference(@NotNull XMLParser.ReferenceContext ctx);
+
+	/**
+	 * Enter a parse tree produced by {@link XMLParser#misc}.
+	 * @param ctx the parse tree
+	 */
+	void enterMisc(@NotNull XMLParser.MiscContext ctx);
+	/**
+	 * Exit a parse tree produced by {@link XMLParser#misc}.
+	 * @param ctx the parse tree
+	 */
+	void exitMisc(@NotNull XMLParser.MiscContext ctx);
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java
new file mode 100644
index 0000000..6a76a00
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java
@@ -0,0 +1,70 @@
+// Generated from XMLParser.g4 by ANTLR 4.4
+package android.databinding.parser;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
+
+/**
+ * This interface defines a complete generic visitor for a parse tree produced
+ * by {@link XMLParser}.
+ *
+ * @param <Result> The return type of the visit operation. Use {@link Void} for
+ * operations with no return type.
+ */
+public interface XMLParserVisitor<Result> extends ParseTreeVisitor<Result> {
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#content}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitContent(@NotNull XMLParser.ContentContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#element}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitElement(@NotNull XMLParser.ElementContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#prolog}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitProlog(@NotNull XMLParser.PrologContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#document}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitDocument(@NotNull XMLParser.DocumentContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#attribute}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitAttribute(@NotNull XMLParser.AttributeContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#chardata}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitChardata(@NotNull XMLParser.ChardataContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#reference}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitReference(@NotNull XMLParser.ReferenceContext ctx);
+
+	/**
+	 * Visit a parse tree produced by {@link XMLParser#misc}.
+	 * @param ctx the parse tree
+	 * @return the visitor result
+	 */
+	Result visitMisc(@NotNull XMLParser.MiscContext ctx);
+}
\ No newline at end of file
diff --git a/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt b/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt
new file mode 100644
index 0000000..48356ad
--- /dev/null
+++ b/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014 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.parser
+
+import java.io.File
+import org.antlr.v4.runtime.ANTLRInputStream
+import org.antlr.v4.runtime.CommonTokenStream
+import java.io.FileReader
+import org.antlr.v4.runtime.Token
+import java.util.Comparator
+import kotlin.properties.Delegates
+
+fun main(vararg args : String) {
+    val f = File("/Volumes/ssd/src/data-binding/KDataBinder/samples/BindingDemo/app/src/main/res/layout/main_activity.xml")
+    antlrTest(f);
+}
+
+fun log(f : () -> String) {
+    System.out.println("LOG: ${f()}");
+}
+
+fun antlrTest(f: File) : String? {
+    val inputStream = ANTLRInputStream(FileReader(f))
+    val lexer = XMLLexer(inputStream)
+    val tokenStream = CommonTokenStream(lexer)
+    val parser = XMLParser(tokenStream)
+    val expr = parser.document()
+    log{"exp tree: ${expr.toStringTree(parser)}"}
+    val reservedElementNames = arrayListOf("variable", "import")
+    val visitor = object : XMLParserBaseVisitor<MutableList<Pair<Position, Position>>>() {
+        override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList<Pair<Position, Position>>? {
+            log{"attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()}"}
+            if (ctx.attrName.getText().startsWith("bind:")) {
+
+                return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition()))
+            } else if (ctx.attrValue.getText().startsWith("\"@{") && ctx.attrValue.getText().endsWith("}\"")) {
+                return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition()))
+            }
+
+            //log{"visiting attr: ${ctx.getText()} at location ${ctx.getStart().toS()} ${ctx.getStop().toS()}"}
+            return super<XMLParserBaseVisitor>.visitAttribute(ctx)
+        }
+
+        override fun visitElement(ctx: XMLParser.ElementContext): MutableList<Pair<Position, Position>>? {
+            log{"elm ${ctx.elmName.getText()} || ${ctx.Name()}"}
+            if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) {
+                return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition()))
+            }
+            return super< XMLParserBaseVisitor>.visitElement(ctx)
+        }
+
+        override fun defaultResult(): MutableList<Pair<Position, Position>>? = arrayListOf()
+
+        override fun aggregateResult(aggregate: MutableList<Pair<Position, Position>>?, nextResult: MutableList<Pair<Position, Position>>?): MutableList<Pair<Position, Position>>? {
+            return if (aggregate == null) {
+                return nextResult 
+            } else if (nextResult == null) {
+                return aggregate
+            } else {
+                aggregate.addAll(nextResult)
+                return aggregate
+            }
+        }
+    }
+    val parsedExpr = expr.accept(visitor)
+    if (parsedExpr.size() == 0) {
+        return null//nothing to strip
+    }
+    log {"result ${parsedExpr.joinToString("\n-> ")}"}
+    parsedExpr.forEach {
+        log {"${it.first.line} ${it.first.charIndex}"}
+    }
+    val out = StringBuilder()
+    val lines = f.readLines("utf-8")
+    lines.forEach { out.appendln(it) }
+
+    val sorted = parsedExpr.sortBy(object : Comparator<Pair<Position, Position>> {
+        override fun compare(o1: Pair<Position, Position>, o2: Pair<Position, Position>): Int {
+            val lineCmp = o1.first.line.compareTo(o2.first.charIndex)
+            if (lineCmp != 0) {
+                return lineCmp
+            }
+            return o1.first.line.compareTo(o2.first.charIndex)
+        }
+    })
+
+    var lineStarts = arrayListOf(0)
+
+    lines.withIndices().forEach {
+        if (it.first > 0) {
+            lineStarts.add(lineStarts[it.first - 1] + lines[it.first - 1].length() + 1)
+        }
+    }
+
+    val seperator = System.lineSeparator().charAt(0)
+
+    sorted.forEach {
+        val posStart = lineStarts[it.first.line] + it.first.charIndex
+        val posEnd = lineStarts[it.second.line] + it.second.charIndex
+        for( i in posStart..(posEnd - 1)) {
+            if (out.charAt(i) != seperator) {
+                out.setCharAt(i, ' ')
+            }
+        }
+    }
+
+    return out.toString()
+}
+
+
+fun org.antlr.v4.runtime.Token.toS() : String = "[L:${getLine()} CH:${getCharPositionInLine()}]"
+
+fun org.antlr.v4.runtime.Token.toPosition() : Position = Position(getLine() -1 , getCharPositionInLine())
+
+fun org.antlr.v4.runtime.Token.toEndPosition() : Position = Position(getLine() - 1 , getCharPositionInLine() + getText().size)
+
+data class Position(var line : Int, var charIndex : Int) {
+}