Move shared code between build and compiler to a separate project

This CL encapsulates the shared code between build time and annotation
processing into a separate project. This project has no dependency on
kotlin so we can easily use it in gradle plugin.

Bug: 21815393
Change-Id: I9fd2e88885d06ff5423121747817ffcd056e8c74
diff --git a/compiler/build.gradle b/compiler/build.gradle
index 2dddd03..6337d71 100644
--- a/compiler/build.gradle
+++ b/compiler/build.gradle
@@ -1,5 +1,3 @@
-import android.databinding.LicenseCollector
-
 /*
  * Copyright (C) 2014 The Android Open Source Project
  *
@@ -15,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+import android.databinding.LicenseCollector
 apply plugin: 'java'
 apply plugin: "kotlin"
 
@@ -33,17 +31,8 @@
     }
 }
 
-sourceSets {
-    main {
-        java {
-            srcDir 'src/main/java'
-            srcDir 'src/main/grammar-gen'
-            srcDir 'src/main/xml-gen'
-        }
-    }
-}
-
 dependencies {
+    compile project(":compilerCommon")
     compile project(':baseLibrary')
     compile 'org.apache.commons:commons-lang3:3.3.2'
     compile 'commons-io:commons-io:2.4'
@@ -77,19 +66,6 @@
     tasks['compileTestKotlin'].dependsOn libProject.tasks['uploadJarArchives']
 }
 
-project.tasks.create(name : "generateXmlParser", type : JavaExec) {
-    classpath configurations.runtime
-    main "org.antlr.v4.Tool"
-    workingDir projectDir
-    args "XMLParser.g4", "-visitor", "-o", "src/main/java/android/databinding/parser", "-package", "android.databinding.parser", "-lib", "."
-}
-
-project.tasks.create(name : "generateGrammar", type : JavaExec) {
-    classpath configurations.runtime
-    main "org.antlr.v4.Tool"
-    args "BindingExpression.g4", "-visitor", "-o", "src/main/grammar-gen/android/databinding/parser", "-package", "android.databinding.parser"
-}
-
 task fatJar(type: Jar) {
     baseName = project.name + '-all'
     doFirst {
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
index 52947a8..01aa88f 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
@@ -32,6 +32,7 @@
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
 
 public class AnnotationAnalyzer extends ModelAnalyzer {
 
@@ -52,6 +53,12 @@
     public AnnotationAnalyzer(ProcessingEnvironment processingEnvironment) {
         mProcessingEnv = processingEnvironment;
         setInstance(this);
+        L.setClient(new L.Client() {
+            @Override
+            public void printMessage(Diagnostic.Kind kind, String message) {
+                mProcessingEnv.getMessager().printMessage(kind, message);
+            }
+        });
     }
 
     public static AnnotationAnalyzer get() {
diff --git a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
index 4f07546..d5911ea 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -15,6 +15,7 @@
  */
 package android.databinding.tool.store;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 
 import org.apache.commons.lang3.StringUtils;
@@ -35,7 +36,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 
@@ -796,7 +796,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(viewType, attributeIndices.keySet());
+            return Objects.hashCode(viewType, attributeIndices.keySet());
         }
     }
 
@@ -833,7 +833,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(type, method);
+            return Objects.hashCode(type, method);
         }
 
         @Override
@@ -857,7 +857,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(viewType, valueType);
+            return Objects.hashCode(viewType, valueType);
         }
 
         @Override
diff --git a/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt b/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt
deleted file mode 100644
index 772349e..0000000
--- a/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *      http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.databinding.tool.util
-
-object ParserHelper {
-    public fun toClassName(name:String) : String  {
-        return stripExtension(name).split("[_-]".toRegex()).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/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt b/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt
deleted file mode 100644
index f5bc143..0000000
--- a/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * 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 android.databinding.parser.BindingExpressionLexer
-import android.databinding.parser.BindingExpressionParser
-import android.databinding.parser.XMLParser
-import android.databinding.parser.XMLLexer
-import android.databinding.tool.ext
-import java.io.File
-import org.antlr.v4.runtime.ANTLRInputStream
-import java.io.FileReader
-import org.antlr.v4.runtime.CommonTokenStream
-import java.util.Comparator
-import com.google.common.base.Preconditions
-import org.antlr.v4.runtime.Token
-import org.apache.commons.lang3.StringEscapeUtils
-import java.util.ArrayList
-import java.util.regex.Pattern
-
-fun Token.toS() : String = "[L:${getLine()} CH:${getCharPositionInLine()}]"
-
-fun Token.toPosition() : Position = Position(getLine() -1 , getCharPositionInLine())
-
-fun Token.toEndPosition() : Position = Position(getLine() - 1 , getCharPositionInLine() + getText().length())
-
-data class Position(var line : Int, var charIndex : Int) {
-}
-
-/**
- * Ugly inefficient class to strip unwanted tags from XML.
- * Band-aid solution to unblock development
- */
-object XmlEditor {
-    fun XMLParser.ElementContext.nodeName() = this.elmName.getText()
-
-    fun XMLParser.ElementContext.attributes() =
-            if (attribute() == null) {
-                arrayListOf<XMLParser.AttributeContext>()
-            } else {
-                attribute()
-            }
-
-    fun XMLParser.ElementContext.hasExpressionAttributes() : kotlin.Boolean {
-        val expressions = expressionAttributes()
-        return expressions.size() > 1 || (expressions.size() == 1 &&
-                !expressions.get(0).attrName.getText().equals("android:tag"))
-    }
-
-    fun XMLParser.ElementContext.expressionAttributes() = attributes().filter {
-        val attrName = it.attrName.getText();
-        val value = it.attrValue.getText()
-        attrName.equals("android:tag") ||
-                (value.startsWith("\"@{") && value.endsWith("}\"")) ||
-                (value.startsWith("\'@{") && value.endsWith("}\'"))
-    }
-
-    fun XMLParser.ElementContext.endTagPosition(): Position {
-        if (content() == null) {
-            // no content, so just subtract from the "/>"
-            val endTag = getStop().toEndPosition()
-            Preconditions.checkState(endTag.charIndex > 0)
-            endTag.charIndex -= 2
-            return endTag
-        } else {
-            // tag with no attributes, but with content
-            val position = content().getStart().toPosition()
-            Preconditions.checkState(position.charIndex > 0)
-            position.charIndex--
-            return position
-        }
-    }
-
-    fun XMLParser.ElementContext.elements() : List<XMLParser.ElementContext> {
-        if (content() != null && content().element() != null) {
-            return content().element()
-        } else {
-            return arrayListOf<XMLParser.ElementContext>()
-        }
-    }
-
-    fun strip(f: File, newTag: String? = null): String? {
-        val inputStream = ANTLRInputStream(FileReader(f))
-        val lexer = XMLLexer(inputStream)
-        val tokenStream = CommonTokenStream(lexer)
-        val parser = XMLParser(tokenStream)
-        val expr = parser.document()
-        val root = expr.element()
-
-        if (root == null || !"layout".equals(root.nodeName())) {
-            return null; // not a binding layout
-        }
-
-        val dataNodes = root.elements().filter { "data".equals(it.nodeName()) }
-        Preconditions.checkState(dataNodes.size() < 2,
-                "Multiple binding data tags. Expecting a maximum of one.");
-
-        val lines = arrayListOf<String>()
-        lines.addAll(f.readLines("utf-8"))
-
-        dataNodes.forEach {
-            replace(lines, it.getStart().toPosition(), it.getStop().toEndPosition(), "")
-        }
-
-        val layoutNodes = root.elements().filter { !"data".equals(it.nodeName()) }
-        Preconditions.checkState(layoutNodes.size() == 1,
-                "Only one layout element and one data element are allowed")
-
-        val layoutNode = layoutNodes.get(0)
-
-        val noTag = arrayListOf<Pair<String, XMLParser.ElementContext>>()
-
-        recurseReplace(layoutNode, lines, noTag, newTag, 0)
-
-        // Remove the <layout>
-        val rootStartTag = root.getStart().toPosition()
-        val rootEndTag = root.content().getStart().toPosition();
-        replace(lines, rootStartTag, rootEndTag, "")
-
-        // Remove the </layout>
-        val endLayoutPositions = findTerminalPositions(root, lines)
-        replace(lines, endLayoutPositions.first, endLayoutPositions.second, "")
-
-        val rootAttributes = StringBuilder()
-        root.attributes().fold(rootAttributes) {
-            str : StringBuilder, attr -> str.append(' ').append(attr.getText())
-        }
-
-        val noTagRoot = noTag.firstOrNull() { it.second == layoutNode }
-        if (noTagRoot != null) {
-            val newRootTag = Pair(noTagRoot.first + rootAttributes.toString(), layoutNode)
-            val index = noTag.indexOf(noTagRoot)
-            noTag.set(index, newRootTag)
-        } else {
-            val newRootTag = Pair(rootAttributes.toString(), layoutNode)
-            noTag.add(newRootTag)
-        }
-
-        noTag.sortBy(object : Comparator<Pair<String, XMLParser.ElementContext>> {
-            override fun compare(o1: Pair<String, XMLParser.ElementContext>,
-                    o2: Pair<String, XMLParser.ElementContext>): Int {
-                val start1 = o1.second.getStart().toPosition()
-                val start2 = o2.second.getStart().toPosition()
-                val lineCmp = start2.line.compareTo(start1.line)
-                if (lineCmp != 0) {
-                    return lineCmp
-                }
-                return start2.charIndex.compareTo(start2.charIndex)
-            }
-        }).forEach {
-            val element = it.second
-            val tag = it.first;
-            val endTagPosition = element.endTagPosition()
-            fixPosition(lines, endTagPosition)
-            val line = lines.get(endTagPosition.line)
-            val newLine = line.substring(0, endTagPosition.charIndex) +
-                " ${tag}" + line.substring(endTagPosition.charIndex)
-            lines.set(endTagPosition.line, newLine)
-        }
-
-        return lines.fold(StringBuilder()) { sb, line ->
-            sb.append(line).append(System.getProperty("line.separator"))
-        }.toString()
-    }
-
-    fun findTerminalPositions(node : XMLParser.ElementContext, lines : ArrayList<String>) :
-            Pair<Position, Position> {
-        val endPosition = node.getStop().toEndPosition()
-        val startPosition = node.getStop().toPosition()
-        var index : kotlin.Int
-        do {
-            index = lines.get(startPosition.line).lastIndexOf("</")
-            startPosition.line--
-        } while (index < 0)
-        startPosition.line++
-        startPosition.charIndex = index
-        return Pair(startPosition, endPosition)
-    }
-
-    fun recurseReplace(node : XMLParser.ElementContext, lines : ArrayList<String>,
-            noTag : ArrayList<Pair<String, XMLParser.ElementContext>>,
-            newTag : String?, bindingIndex : kotlin.Int) : kotlin.Int {
-        var nextBindingIndex = bindingIndex
-        val isMerge = "merge".equals(node.nodeName())
-        if (!isMerge && (node.hasExpressionAttributes() || newTag != null)) {
-            var tag = ""
-            if (newTag != null) {
-                tag = "android:tag=\"${newTag}_${bindingIndex}\""
-                nextBindingIndex++
-            } else if (!"include".equals(node.nodeName())) {
-                tag = "android:tag=\"binding_${bindingIndex}\""
-                nextBindingIndex++
-            }
-            node.expressionAttributes().forEach {
-                val start = it.getStart().toPosition()
-                val end = it.getStop().toEndPosition()
-                val defaultVal = defaultReplacement(it)
-                if (defaultVal != null) {
-                    replace(lines, start, end, "${it.attrName.getText()}=\"${defaultVal}\"")
-                } else if (replace(lines, start, end, tag)) {
-                    tag = ""
-                }
-            }
-            if (tag.length() != 0) {
-                noTag.add(Pair(tag, node))
-            }
-        }
-
-        val nextTag : String?
-        if (bindingIndex == 0 && isMerge) {
-            nextTag = newTag
-        } else {
-            nextTag = null
-        }
-        node.elements().forEach {
-            nextBindingIndex = recurseReplace(it, lines, noTag, nextTag, nextBindingIndex)
-        }
-        return nextBindingIndex
-    }
-
-    fun defaultReplacement(attr : XMLParser.AttributeContext) : String? {
-        val textWithQuotes = attr.attrValue.getText()
-        val escapedText = textWithQuotes.substring(1, textWithQuotes.length() - 1)
-        if (!escapedText.startsWith("@{") || !escapedText.endsWith("}")) {
-            return null;
-        }
-        val text = StringEscapeUtils.unescapeXml(escapedText.substring(2, escapedText.length() - 1))
-        val inputStream = ANTLRInputStream(text)
-        val lexer = BindingExpressionLexer(inputStream)
-        val tokenStream = CommonTokenStream(lexer)
-        val parser = BindingExpressionParser(tokenStream)
-        val root = parser.bindingSyntax()
-        val defaults = root.defaults()
-        if (defaults != null) {
-            val constantValue = defaults.constantValue()
-            val literal = constantValue.literal()
-            if (literal != null) {
-                val stringLiteral = literal.stringLiteral()
-                if (stringLiteral != null) {
-                    val doubleQuote = stringLiteral.DoubleQuoteString()
-                    if (doubleQuote != null) {
-                        val quotedStr = doubleQuote.getText()
-                        val unquoted = quotedStr.substring(1, quotedStr.length() - 1)
-                        return StringEscapeUtils.escapeXml10(unquoted)
-                    } else {
-                        val quotedStr = stringLiteral.SingleQuoteString().getText()
-                        val unquoted = quotedStr.substring(1, quotedStr.length() - 1)
-                        val unescaped = unquoted.replace("\"", "\\\"").replace("\\`", "`")
-                        return StringEscapeUtils.escapeXml10(unescaped)
-                    }
-                }
-            }
-            return constantValue.getText()
-        }
-        return null
-    }
-
-    fun replace(lines : ArrayList<String>, start: Position, end: Position, text: String) :
-            kotlin.Boolean {
-        fixPosition(lines, start)
-        fixPosition(lines, end)
-        if (start.line != end.line) {
-            val startLine = lines.get(start.line)
-            val newStartLine = startLine.substring(0, start.charIndex) +
-                    text;
-            lines.set(start.line, newStartLine)
-            for (i in start.line + 1 .. end.line - 1) {
-                val line = lines.get(i)
-                lines.set(i, replaceWithSpaces(line, 0, line.length() - 1))
-            }
-            val endLine = lines.get(end.line)
-            val newEndLine = replaceWithSpaces(endLine, 0, end.charIndex - 1)
-            lines.set(end.line, newEndLine)
-            return true
-        } else if (end.charIndex - start.charIndex >= text.length()) {
-            val line = lines.get(start.line)
-            val endTextIndex = start.charIndex + text.length()
-            val replacedText = line.replaceRange(start.charIndex, endTextIndex, text)
-            val spacedText = replaceWithSpaces(replacedText, endTextIndex, end.charIndex - 1)
-            lines.set(start.line, spacedText)
-            return true
-        } else {
-            val line = lines.get(start.line)
-            val newLine = replaceWithSpaces(line, start.charIndex, end.charIndex - 1)
-            lines.set(start.line, newLine)
-            return false;
-        }
-    }
-
-    fun replaceWithSpaces(line : String, start : kotlin.Int, end : kotlin.Int) : String {
-        val lineBuilder = StringBuilder(line)
-        for (i in start..end) {
-            lineBuilder.setCharAt(i, ' ')
-        }
-        return lineBuilder.toString()
-    }
-
-    fun fixPosition(lines : ArrayList<String>, pos : Position) {
-        val line = lines.get(pos.line)
-        while (pos.charIndex > line.length()) {
-            pos.charIndex--
-        }
-    }
-}
diff --git a/compiler/BindingExpression.g4 b/compilerCommon/BindingExpression.g4
similarity index 100%
rename from compiler/BindingExpression.g4
rename to compilerCommon/BindingExpression.g4
diff --git a/compiler/XMLLexer.g4 b/compilerCommon/XMLLexer.g4
similarity index 100%
rename from compiler/XMLLexer.g4
rename to compilerCommon/XMLLexer.g4
diff --git a/compiler/XMLParser.g4 b/compilerCommon/XMLParser.g4
similarity index 100%
rename from compiler/XMLParser.g4
rename to compilerCommon/XMLParser.g4
diff --git a/compilerCommon/build.gradle b/compilerCommon/build.gradle
new file mode 100644
index 0000000..2531c02
--- /dev/null
+++ b/compilerCommon/build.gradle
@@ -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.
+ */
+
+apply plugin: 'java'
+
+
+version = '1.0'
+sourceCompatibility = config.javaTargetCompatibility
+targetCompatibility = config.javaSourceCompatibility
+repositories {
+    mavenCentral()
+}
+
+sourceSets {
+    main {
+        java {
+            srcDir 'src/main/java'
+            srcDir 'src/main/xml-gen'
+            srcDir 'src/main/grammar-gen'
+        }
+    }
+}
+
+dependencies {
+    testCompile group: 'junit', name: 'junit', version: '4.11'
+    compile project(':baseLibrary')
+    compile 'org.apache.commons:commons-lang3:3.3.2'
+    compile 'com.google.guava:guava:17.0'
+    compile 'com.tunnelvisionlabs:antlr4:4.5'
+    compile 'commons-io:commons-io:2.4'
+}
+
+project.tasks.create(name : "generateXmlParser", type : JavaExec) {
+    classpath configurations.runtime
+    main "org.antlr.v4.Tool"
+    workingDir projectDir
+    args "XMLParser.g4", "-visitor", "-o", "src/main/java/android/databinding/parser", "-package", "android.databinding.parser", "-lib", "."
+}
+
+project.tasks.create(name : "generateGrammar", type : JavaExec) {
+    classpath configurations.runtime
+    main "org.antlr.v4.Tool"
+    args "BindingExpression.g4", "-visitor", "-o", "src/main/grammar-gen/android/databinding/parser", "-package", "android.databinding.parser"
+}
+
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpression.tokens b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpression.tokens
similarity index 100%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpression.tokens
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpression.tokens
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java
similarity index 99%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java
index db87538..400c422 100644
--- a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java
+++ b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseListener.java
@@ -2,7 +2,6 @@
 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;
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
similarity index 99%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
index d7d426e..a664007 100644
--- a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
+++ b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionBaseVisitor.java
@@ -1,6 +1,5 @@
 // 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;
 
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.java
similarity index 100%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.java
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.tokens b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.tokens
similarity index 100%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.tokens
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionLexer.tokens
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java
similarity index 99%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java
index 020be83..80e4a78 100644
--- a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java
+++ b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionListener.java
@@ -1,6 +1,5 @@
 // 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;
 
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java
similarity index 99%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java
index 0d41591..8463e9f 100644
--- a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java
+++ b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionParser.java
@@ -1,13 +1,9 @@
 // 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
diff --git a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java
similarity index 99%
rename from compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java
rename to compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java
index d589a7d..1f80a34 100644
--- a/compiler/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java
+++ b/compilerCommon/src/main/grammar-gen/android/databinding/parser/BindingExpressionVisitor.java
@@ -1,6 +1,5 @@
 // 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;
 
diff --git a/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java b/compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
similarity index 100%
rename from compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
rename to compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
diff --git a/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
similarity index 98%
rename from compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java
rename to compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
index 2c9ad19..6cfbd96 100644
--- a/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
@@ -64,7 +64,7 @@
     public ResourceBundle.LayoutFileBundle parseXml(File xml, String pkg, int minSdk)
             throws ParserConfigurationException, IOException, SAXException,
             XPathExpressionException {
-        final String xmlNoExtension = ParserHelper.INSTANCE$.stripExtension(xml.getName());
+        final String xmlNoExtension = ParserHelper.stripExtension(xml.getName());
         final String newTag = xml.getParentFile().getName() + '/' + xmlNoExtension;
         File original = stripFileAndGetOriginal(xml, newTag);
         if (original == null) {
@@ -256,7 +256,7 @@
     }
 
     private void stripBindingTags(File xml, String newTag) throws IOException {
-        String res = XmlEditor.INSTANCE$.strip(xml, newTag);
+        String res = XmlEditor.strip(xml, newTag);
         if (res != null) {
             L.d("file %s has changed, overwriting %s", xml.getName(), xml.getAbsolutePath());
             FileUtils.writeStringToFile(xml, res);
diff --git a/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java b/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
similarity index 98%
rename from compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java
rename to compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
index 732cc9b..b426170 100644
--- a/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
@@ -237,7 +237,7 @@
                     if ("layout".equals(parentFileName)) {
                         configName = "";
                     } else {
-                        configName = ParserHelper.INSTANCE$.toClassName(parentFileName.substring("layout-".length()));
+                        configName = ParserHelper.toClassName(parentFileName.substring("layout-".length()));
                     }
                 } else {
                     configName = "";
@@ -383,7 +383,7 @@
             if (mFullBindingClass == null) {
                 if (mBindingClass == null) {
                     mFullBindingClass = getModulePackage() + ".databinding." +
-                            ParserHelper.INSTANCE$.toClassName(getFileName()) + "Binding";
+                            ParserHelper.toClassName(getFileName()) + "Binding";
                 } else if (mBindingClass.startsWith(".")) {
                     mFullBindingClass = getModulePackage() + mBindingClass;
                 } else if (mBindingClass.indexOf('.') < 0) {
diff --git a/compiler/src/main/java/android/databinding/tool/util/L.java b/compilerCommon/src/main/java/android/databinding/tool/util/L.java
similarity index 72%
rename from compiler/src/main/java/android/databinding/tool/util/L.java
rename to compilerCommon/src/main/java/android/databinding/tool/util/L.java
index aee1fea..478ff58 100644
--- a/compiler/src/main/java/android/databinding/tool/util/L.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/L.java
@@ -18,14 +18,28 @@
 
 import org.apache.commons.lang3.exception.ExceptionUtils;
 
-import android.databinding.tool.reflection.ModelAnalyzer;
-import android.databinding.tool.reflection.annotation.AnnotationAnalyzer;
-
 import javax.tools.Diagnostic;
 import javax.tools.Diagnostic.Kind;
 
 public class L {
-    static boolean sEnableDebug = false;
+    private static boolean sEnableDebug = false;
+    private static final Client sSystemClient = new Client() {
+        @Override
+        public void printMessage(Kind kind, String message) {
+            if (kind == Kind.ERROR) {
+                System.err.println(message);
+            } else {
+                System.out.println(message);
+            }
+        }
+    };
+
+    private static Client sClient = sSystemClient;
+
+    public static void setClient(Client systemClient) {
+        L.sClient = systemClient;
+    }
+
     public static void setDebugLog(boolean enabled) {
         sEnableDebug = enabled;
     }
@@ -62,20 +76,13 @@
     }
 
     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);
-            }
+        sClient.printMessage(kind, message);
+        if (kind == Diagnostic.Kind.ERROR) {
+            throw new RuntimeException("failure, see logs for details.\n" + message);
         }
     }
 
+    public static interface Client {
+        public void printMessage(Diagnostic.Kind kind, String message);
+    }
 }
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java b/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java
new file mode 100644
index 0000000..c3740e5
--- /dev/null
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.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.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class ParserHelper {
+    public static String toClassName(String name) {
+        StringBuilder builder = new StringBuilder();
+        for (String item : name.split("[_-]")) {
+            builder.append(StringUtils.capitalize(item));
+        }
+        return builder.toString();
+    }
+
+    public static String stripExtension(String name) {
+        final int dot = name.lastIndexOf('.');
+        return dot < 0 ? name : name.substring(0, dot);
+    }
+}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java b/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java
new file mode 100644
index 0000000..ff2896d
--- /dev/null
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java
@@ -0,0 +1,383 @@
+/*
+ * 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 com.google.common.base.Optional;
+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 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.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import android.databinding.parser.BindingExpressionLexer;
+import android.databinding.parser.BindingExpressionParser;
+import android.databinding.parser.XMLLexer;
+import android.databinding.parser.XMLParser;
+import android.databinding.parser.XMLParser.AttributeContext;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Ugly inefficient class to strip unwanted tags from XML.
+ * Band-aid solution to unblock development
+ */
+public class XmlEditor {
+
+    public static String strip(File f, String newTag) throws IOException {
+        ANTLRInputStream inputStream = new ANTLRInputStream(new FileReader(f));
+        XMLLexer lexer = new XMLLexer(inputStream);
+        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+        XMLParser parser = new XMLParser(tokenStream);
+        XMLParser.DocumentContext expr = parser.document();
+        XMLParser.ElementContext root = expr.element();
+
+        if (root == null || !"layout".equals(nodeName(root))) {
+            return null; // not a binding layout
+        }
+
+        Iterable<? extends XMLParser.ElementContext> dataNodes = Iterables
+                .filter(elements(root), new Predicate<XMLParser.ElementContext>() {
+                    @Override
+                    public boolean apply(XMLParser.ElementContext input) {
+                        return "data".equals(nodeName(input));
+                    }
+                });
+        Preconditions.checkState(Iterables.size(dataNodes) < 2,
+                "Multiple binding data tags. Expecting a maximum of one.");
+
+        ArrayList<String> lines = Lists.newArrayList();
+        lines.addAll(FileUtils.readLines(f, "utf-8"));
+
+        for (android.databinding.parser.XMLParser.ElementContext it : dataNodes) {
+            replace(lines, toPosition(it.getStart()), toEndPosition(it.getStop()), "");
+        }
+        Iterable<? extends XMLParser.ElementContext> layoutNodes = Iterables
+                .filter(elements(root), new Predicate<XMLParser.ElementContext>() {
+                    @Override
+                    public boolean apply(XMLParser.ElementContext input) {
+                        return !"data".equals(nodeName(input));
+                    }
+                });
+        Preconditions.checkState(Iterables.size(layoutNodes) == 1,
+                "Only one layout element and one data element are allowed");
+
+        final XMLParser.ElementContext layoutNode = Iterables.getFirst(layoutNodes, null);
+
+        ArrayList<Pair<String, android.databinding.parser.XMLParser.ElementContext>> noTag = Lists
+                .newArrayList();
+
+        recurseReplace(layoutNode, lines, noTag, newTag, 0);
+
+        // Remove the <layout>
+        Position rootStartTag = toPosition(root.getStart());
+        Position rootEndTag = toPosition(root.content().getStart());
+        replace(lines, rootStartTag, rootEndTag, "");
+
+        // Remove the </layout>
+        ImmutablePair<Position, Position> endLayoutPositions = findTerminalPositions(root, lines);
+        replace(lines, endLayoutPositions.left, endLayoutPositions.right, "");
+
+        StringBuilder rootAttributes = new StringBuilder();
+        for (AttributeContext attr : attributes(root)) {
+            rootAttributes.append(' ').append(attr.getText());
+        }
+        Optional<Pair<String, XMLParser.ElementContext>> pairOptional = Iterables
+                .tryFind(noTag, new Predicate<Pair<String, XMLParser.ElementContext>>() {
+                    @Override
+                    public boolean apply(Pair<String, XMLParser.ElementContext> input) {
+                        return input.getRight() == layoutNode;
+                    }
+                });
+        if (pairOptional.isPresent()) {
+            Pair<String, XMLParser.ElementContext> noTagRoot = pairOptional.get();
+            ImmutablePair<String, XMLParser.ElementContext>
+                    newRootTag = new ImmutablePair<>(
+                    noTagRoot.getLeft() + rootAttributes.toString(), layoutNode);
+            int index = noTag.indexOf(noTagRoot);
+            noTag.set(index, newRootTag);
+        } else {
+            ImmutablePair<String, XMLParser.ElementContext> newRootTag =
+                    new ImmutablePair<>(rootAttributes.toString(), layoutNode);
+            noTag.add(newRootTag);
+        }
+        //noinspection NullableProblems
+        Collections.sort(noTag, new Comparator<Pair<String, XMLParser.ElementContext>>() {
+            @Override
+            public int compare(Pair<String, XMLParser.ElementContext> o1,
+                    Pair<String, XMLParser.ElementContext> o2) {
+                Position start1 = toPosition(o1.getRight().getStart());
+                Position start2 = toPosition(o2.getRight().getStart());
+                int lineCmp = Integer.compare(start2.line, start1.line);
+                if (lineCmp != 0) {
+                    return lineCmp;
+                }
+                return Integer.compare(start2.charIndex, start1.charIndex);
+            }
+        });
+        for (Pair<String, android.databinding.parser.XMLParser.ElementContext> it : noTag) {
+            XMLParser.ElementContext element = it.getRight();
+            String tag = it.getLeft();
+            Position endTagPosition = endTagPosition(element);
+            fixPosition(lines, endTagPosition);
+            String line = lines.get(endTagPosition.line);
+            String newLine = line.substring(0, endTagPosition.charIndex) + " " + tag +
+                    line.substring(endTagPosition.charIndex);
+            lines.set(endTagPosition.line, newLine);
+        }
+        return StringUtils.join(lines, System.getProperty("line.separator"));
+    }
+
+    private static Position toPosition(Token token) {
+        return new Position(token.getLine() - 1, token.getCharPositionInLine());
+    }
+
+    private static Position toEndPosition(Token token) {
+        return new Position(token.getLine() - 1,
+                token.getCharPositionInLine() + token.getText().length());
+    }
+
+    private static String nodeName(XMLParser.ElementContext elementContext) {
+        return elementContext.elmName.getText();
+    }
+
+    private static List<? extends AttributeContext> attributes(XMLParser.ElementContext elementContext) {
+        if (elementContext.attribute() == null) {
+            return Lists.newArrayList();
+        } else {
+            return elementContext.attribute();
+        }
+    }
+
+    private static Iterable<? extends AttributeContext> expressionAttributes(
+            XMLParser.ElementContext elementContext) {
+        return Iterables.filter(attributes(elementContext), new Predicate<AttributeContext>() {
+            @Override
+            public boolean apply(AttributeContext input) {
+                String attrName = input.attrName.getText();
+                String value = input.attrValue.getText();
+                return attrName.equals("android:tag") ||
+                        (value.startsWith("\"@{") && value.endsWith("}\"")) ||
+                        (value.startsWith("'@{") && value.endsWith("}'"));
+            }
+        });
+    }
+
+    private static Position endTagPosition(XMLParser.ElementContext context) {
+        if (context.content() == null) {
+            // no content, so just subtract from the "/>"
+            Position endTag = toEndPosition(context.getStop());
+            Preconditions.checkState(endTag.charIndex > 0);
+            endTag.charIndex -= 2;
+            return endTag;
+        } else {
+            // tag with no attributes, but with content
+            Position position = toPosition(context.content().getStart());
+            Preconditions.checkState(position.charIndex > 0);
+            position.charIndex--;
+            return position;
+        }
+    }
+
+    private static List<? extends android.databinding.parser.XMLParser.ElementContext> elements(
+            XMLParser.ElementContext context) {
+        if (context.content() != null && context.content().element() != null) {
+            return context.content().element();
+        }
+        return Lists.newArrayList();
+    }
+
+    private static boolean replace(ArrayList<String> lines, Position start, Position end,
+            String text) {
+        fixPosition(lines, start);
+        fixPosition(lines, end);
+        if (start.line != end.line) {
+            String startLine = lines.get(start.line);
+            String newStartLine = startLine.substring(0, start.charIndex) + text;
+            lines.set(start.line, newStartLine);
+            for (int i = start.line + 1; i < end.line; i++) {
+                String line = lines.get(i);
+                lines.set(i, replaceWithSpaces(line, 0, line.length() - 1));
+            }
+            String endLine = lines.get(end.line);
+            String newEndLine = replaceWithSpaces(endLine, 0, end.charIndex - 1);
+            lines.set(end.line, newEndLine);
+            return true;
+        } else if (end.charIndex - start.charIndex >= text.length()) {
+            String line = lines.get(start.line);
+            int endTextIndex = start.charIndex + text.length();
+            String replacedText = replaceRange(line, start.charIndex, endTextIndex, text);
+            String spacedText = replaceWithSpaces(replacedText, endTextIndex, end.charIndex - 1);
+            lines.set(start.line, spacedText);
+            return true;
+        } else {
+            String line = lines.get(start.line);
+            String newLine = replaceWithSpaces(line, start.charIndex, end.charIndex - 1);
+            lines.set(start.line, newLine);
+            return false;
+        }
+    }
+
+    private static String replaceRange(String line, int start, int end, String newText) {
+        return line.substring(0, start) + newText + line.substring(end);
+    }
+
+    private static boolean hasExpressionAttributes(XMLParser.ElementContext context) {
+        Iterable<? extends AttributeContext> expressions = expressionAttributes(context);
+        int size = Iterables.size(expressions);
+        //noinspection ConstantConditions
+        return size > 1 || (size == 1 &&
+                !Iterables.getFirst(expressions, null).attrName.getText().equals("android:tag"));
+    }
+
+    private static int recurseReplace(XMLParser.ElementContext node, ArrayList<String> lines,
+            ArrayList<Pair<String, XMLParser.ElementContext>> noTag,
+            String newTag, int bindingIndex) {
+        int nextBindingIndex = bindingIndex;
+        boolean isMerge = "merge".equals(nodeName(node));
+        if (!isMerge && (hasExpressionAttributes(node) || newTag != null)) {
+            String tag = "";
+            if (newTag != null) {
+                tag = "android:tag=\"" + newTag + "_" + bindingIndex + "\"";
+                nextBindingIndex++;
+            } else if (!"include".equals(nodeName(node))) {
+                tag = "android:tag=\"binding_" + bindingIndex + "\"";
+                nextBindingIndex++;
+            }
+            for (AttributeContext it : expressionAttributes(node)) {
+                Position start = toPosition(it.getStart());
+                Position end = toEndPosition(it.getStop());
+                String defaultVal = defaultReplacement(it);
+                if (defaultVal != null) {
+                    replace(lines, start, end, it.attrName.getText() + "=\"" + defaultVal + "\"");
+                } else if (replace(lines, start, end, tag)) {
+                    tag = "";
+                }
+            }
+            if (tag.length() != 0) {
+                noTag.add(new ImmutablePair<>(tag, node));
+            }
+        }
+
+        String nextTag;
+        if (bindingIndex == 0 && isMerge) {
+            nextTag = newTag;
+        } else {
+            nextTag = null;
+        }
+        for (XMLParser.ElementContext it : elements(node)) {
+            nextBindingIndex = recurseReplace(it, lines, noTag, nextTag, nextBindingIndex);
+        }
+        return nextBindingIndex;
+    }
+
+    private static String defaultReplacement(XMLParser.AttributeContext attr) {
+        String textWithQuotes = attr.attrValue.getText();
+        String escapedText = textWithQuotes.substring(1, textWithQuotes.length() - 1);
+        if (!escapedText.startsWith("@{") || !escapedText.endsWith("}")) {
+            return null;
+        }
+        String text = StringEscapeUtils
+                .unescapeXml(escapedText.substring(2, escapedText.length() - 1));
+        ANTLRInputStream inputStream = new ANTLRInputStream(text);
+        BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream);
+        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+        BindingExpressionParser parser = new BindingExpressionParser(tokenStream);
+        BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax();
+        BindingExpressionParser.DefaultsContext defaults = root.defaults();
+        if (defaults != null) {
+            BindingExpressionParser.ConstantValueContext constantValue = defaults
+                    .constantValue();
+            BindingExpressionParser.LiteralContext literal = constantValue.literal();
+            if (literal != null) {
+                BindingExpressionParser.StringLiteralContext stringLiteral = literal
+                        .stringLiteral();
+                if (stringLiteral != null) {
+                    TerminalNode doubleQuote = stringLiteral.DoubleQuoteString();
+                    if (doubleQuote != null) {
+                        String quotedStr = doubleQuote.getText();
+                        String unquoted = quotedStr.substring(1, quotedStr.length() - 1);
+                        return StringEscapeUtils.escapeXml10(unquoted);
+                    } else {
+                        String quotedStr = stringLiteral.SingleQuoteString().getText();
+                        String unquoted = quotedStr.substring(1, quotedStr.length() - 1);
+                        String unescaped = unquoted.replace("\"", "\\\"").replace("\\`", "`");
+                        return StringEscapeUtils.escapeXml10(unescaped);
+                    }
+                }
+            }
+            return constantValue.getText();
+        }
+        return null;
+    }
+
+    private static ImmutablePair<Position, Position> findTerminalPositions(
+            XMLParser.ElementContext node,  ArrayList<String> lines) {
+        Position endPosition = toEndPosition(node.getStop());
+        Position startPosition = toPosition(node.getStop());
+        int index;
+        do {
+            index = lines.get(startPosition.line).lastIndexOf("</");
+            startPosition.line--;
+        } while (index < 0);
+        startPosition.line++;
+        startPosition.charIndex = index;
+        //noinspection unchecked
+        return new ImmutablePair<>(startPosition, endPosition);
+    }
+
+    private static String replaceWithSpaces(String line, int start, int end) {
+        StringBuilder lineBuilder = new StringBuilder(line);
+        for (int i = start; i <= end; i++) {
+            lineBuilder.setCharAt(i, ' ');
+        }
+        return lineBuilder.toString();
+    }
+
+    private static void fixPosition(ArrayList<String> lines, Position pos) {
+        String line = lines.get(pos.line);
+        while (pos.charIndex > line.length()) {
+            pos.charIndex--;
+        }
+    }
+
+    private static class Position {
+
+        int line;
+        int charIndex;
+
+        public Position(int line, int charIndex) {
+            this.line = line;
+            this.charIndex = charIndex;
+        }
+    }
+
+}
diff --git a/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java b/compilerCommon/src/main/java/android/databinding/tool/writer/JavaFileWriter.java
similarity index 100%
rename from compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java
rename to compilerCommon/src/main/java/android/databinding/tool/writer/JavaFileWriter.java
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLLexer.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLLexer.java
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLLexer.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLLexer.java
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLLexer.tokens b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLLexer.tokens
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLLexer.tokens
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLLexer.tokens
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParser.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParser.java
similarity index 99%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParser.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParser.java
index f18a5f0..a375780 100644
--- a/compiler/src/main/xml-gen/android/databinding/parser/XMLParser.java
+++ b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParser.java
@@ -6,8 +6,6 @@
 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
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParser.tokens b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParser.tokens
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParser.tokens
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParser.tokens
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParserBaseListener.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserBaseListener.java
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParserBaseListener.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserBaseListener.java
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParserBaseVisitor.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserBaseVisitor.java
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParserBaseVisitor.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserBaseVisitor.java
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParserListener.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserListener.java
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParserListener.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserListener.java
diff --git a/compiler/src/main/xml-gen/android/databinding/parser/XMLParserVisitor.java b/compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserVisitor.java
similarity index 100%
rename from compiler/src/main/xml-gen/android/databinding/parser/XMLParserVisitor.java
rename to compilerCommon/src/main/xml-gen/android/databinding/parser/XMLParserVisitor.java
diff --git a/databinding.properties b/databinding.properties
index 02460f9..2fc8988 100644
--- a/databinding.properties
+++ b/databinding.properties
@@ -3,8 +3,8 @@
 version = 1.0-rc1-SNAPSHOT
 releaseVersion = 1.0-rc1-SNAPSHOT
 androidPluginVersion = 1.2.3
-javaTargetCompatibility = 1.6
-javaSourceCompatibility = 1.6
+javaTargetCompatibility = 1.7
+javaSourceCompatibility = 1.7
 
 prebuildFolderName=prebuilds
 group=com.android.databinding
diff --git a/gradlePlugin/build.gradle b/gradlePlugin/build.gradle
index cd1099e..f22985b 100644
--- a/gradlePlugin/build.gradle
+++ b/gradlePlugin/build.gradle
@@ -37,7 +37,7 @@
 dependencies {
     compile "com.android.tools.build:gradle:${config.androidPluginVersion}"
     compile gradleApi()
-    compile project(":compiler")
+    compile project(":compilerCommon")
 }
 
 compileJava {
diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java
index ad64057..c400b98 100644
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java
+++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java
@@ -46,6 +46,7 @@
 import org.gradle.api.tasks.bundling.Jar;
 import org.gradle.api.tasks.compile.AbstractCompile;
 
+import android.databinding.tool.util.L;
 import android.databinding.tool.writer.JavaFileWriter;
 
 import java.io.File;
@@ -56,6 +57,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+import javax.tools.Diagnostic;
 import javax.xml.bind.JAXBException;
 
 public class DataBinderPlugin implements Plugin<Project> {
@@ -95,7 +97,7 @@
         if (project == null) {
             return;
         }
-        logger = project.getLogger();
+        setupLogger(project);
 
         String myVersion = readMyVersion();
         logD("data binding plugin version is %s", myVersion);
@@ -132,6 +134,20 @@
         });
     }
 
+    private void setupLogger(Project project) {
+        logger = project.getLogger();
+        L.setClient(new L.Client() {
+            @Override
+            public void printMessage(Diagnostic.Kind kind, String message) {
+                if (kind == Diagnostic.Kind.ERROR) {
+                    logE(null, message);
+                } else {
+                    logD(message);
+                }
+            }
+        });
+    }
+
     String readMyVersion() {
         try {
             InputStream stream = getClass().getResourceAsStream("/data_binding_build_info");
@@ -148,6 +164,7 @@
 
     private void createXmlProcessor(Project project)
             throws NoSuchFieldException, IllegalAccessException {
+        L.d("creating xml processor for " + project);
         Object androidExt = project.getExtensions().getByName("android");
         if (!(androidExt instanceof BaseExtension)) {
             return;
@@ -165,6 +182,7 @@
     private void createXmlProcessorForLibrary(Project project, LibraryExtension lib)
             throws NoSuchFieldException, IllegalAccessException {
         File sdkDir = lib.getSdkDirectory();
+        L.d("create xml processor for " + lib);
         for (TestVariant variant : lib.getTestVariants()) {
             logD("test variant %s. dir name %s", variant, variant.getDirName());
             BaseVariantData variantData = getVariantData(variant);
@@ -179,6 +197,7 @@
 
     private void createXmlProcessorForApp(Project project, AppExtension appExt)
             throws NoSuchFieldException, IllegalAccessException {
+        L.d("create xml processor for " + appExt);
         File sdkDir = appExt.getSdkDirectory();
         for (TestVariant testVariant : appExt.getTestVariants()) {
             TestVariantData variantData = getVariantData(testVariant);
diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java
index 5477467..4eb06b2 100644
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java
+++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java
@@ -18,7 +18,6 @@
 import android.databinding.tool.util.L;
 import org.gradle.api.DefaultTask;
 import org.gradle.api.tasks.TaskAction;
-import kotlin.properties.Delegates;
 import java.io.File;
 
 /**
diff --git a/settings.gradle b/settings.gradle
index 4c1696d..0d4e57c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,3 +3,5 @@
 include ':compiler'
 include ':gradlePlugin'
 include 'compilationTests'
+include 'compilerCommon'
+