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<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<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<String>"/>
+ <variable name="map" type="Object"/>
+
+ <TextView
+ android:id="@+id/textView0"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{((ArrayList<String>)list)[0]}"/>
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{((Map<String, String>)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<String>"/>
+ <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<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 + " Robots "}" />
+ <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) {
+}