RoomProcessor and Integration test

This CL puts together the very first working version :').

Bug: 32342709
Test: SimpleEntityReadWriteTest.java, DatabaseWriterTest.kt
Change-Id: I7aa449f92b40aac5b682dccc17d276a4e349115b
diff --git a/flatfoot-common/init.gradle b/flatfoot-common/init.gradle
index 5deb735..7de382c 100644
--- a/flatfoot-common/init.gradle
+++ b/flatfoot-common/init.gradle
@@ -37,6 +37,7 @@
 ext.compile_sdk_version = 24
 ext.build_tools_version = "24.0.0"
 ext.intellij_annotation = "12.0"
+ext.espresso_version = "2.2.2"
 ext.release_version = "1.0-SNAPSHOT"
 ext.enablePublicRepos = System.getenv("ALLOW_PUBLIC_REPOS")
 
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
new file mode 100644
index 0000000..256d0d9
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.support.room
+
+import com.android.support.room.processor.Context
+import com.android.support.room.processor.DaoProcessor
+import com.android.support.room.processor.DatabaseProcessor
+import com.android.support.room.processor.EntityProcessor
+import com.android.support.room.writer.DaoWriter
+import com.android.support.room.writer.DatabaseWriter
+import com.android.support.room.writer.EntityCursorConverterWriter
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.collect.SetMultimap
+import javax.annotation.processing.SupportedSourceVersion
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Element
+
+/**
+ * The annotation processor for Room.
+ */
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+class RoomProcessor : BasicAnnotationProcessor() {
+    override fun initSteps(): MutableIterable<ProcessingStep>? {
+        val context = Context(processingEnv)
+        return arrayListOf(EntityProcessingStep(context),
+                DaoProcessingStep(context),
+                DatabaseProcessingStep(context))
+    }
+
+    class DaoProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+                : MutableSet<Element> {
+            elementsByAnnotation[Dao::class.java]
+                    ?.map {
+                        DaoProcessor(context).parse(MoreElements.asType(it))
+                    }
+                    ?.forEach {
+                        DaoWriter(it).write(context.processingEnv)
+                    }
+            return mutableSetOf()
+        }
+
+        override fun annotations(): MutableSet<out Class<out Annotation>> {
+            return mutableSetOf(Dao::class.java)
+        }
+    }
+
+    class DatabaseProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+                : MutableSet<Element> {
+            elementsByAnnotation[Database::class.java]
+                    ?.map {
+                        DatabaseProcessor(context).parse(MoreElements.asType(it))
+                    }
+                    ?.forEach {
+                        DatabaseWriter(it).write(context.processingEnv)
+                    }
+            return mutableSetOf()
+        }
+
+        override fun annotations(): MutableSet<out Class<out Annotation>> {
+            return mutableSetOf(Database::class.java)
+        }
+    }
+
+    class EntityProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
+        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
+                : MutableSet<Element> {
+            elementsByAnnotation[Entity::class.java]
+                    ?.map {
+                        EntityProcessor(context).parse(MoreElements.asType(it))
+                    }
+                    ?.forEach {
+                        EntityCursorConverterWriter(it).write(context.processingEnv)
+                    }
+            return mutableSetOf()
+        }
+
+        override fun annotations(): MutableSet<out Class<out Annotation>> {
+            return mutableSetOf(Entity::class.java)
+        }
+    }
+
+    abstract class ContextBoundProcessingStep(val context: Context) : ProcessingStep
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
index 5ae73d8..5f2a2b3 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
@@ -22,8 +22,7 @@
 import javax.annotation.processing.ProcessingEnvironment
 import javax.annotation.processing.RoundEnvironment
 
-data class Context(val roundEnv: RoundEnvironment,
-                   val processingEnv: ProcessingEnvironment) {
+data class Context(val processingEnv: ProcessingEnvironment) {
     val logger = RLog(processingEnv)
     val checker = Checks(logger)
     val COMMON_TYPES = CommonTypes(processingEnv)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
index c6be9c7..cc1af11 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
@@ -28,4 +28,8 @@
     val implClassName by lazy {
         "${typeName.simpleName()}_Impl"
     }
+
+    val implTypeName by lazy {
+        ClassName.get(typeName.packageName(), implClassName)
+    }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
index b3b82cf..989fec4 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
@@ -25,7 +25,7 @@
                  val primaryKey: Boolean, val columnName: String = name) {
     lateinit var getter: FieldGetter
     lateinit var setter: FieldSetter
-    val typeName by lazy { type.typeName() }
+    val typeName: TypeName by lazy { type.typeName() }
     /**
      * List of names that include variations.
      * e.g. if it is mUser, user is added to the list
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
index 3544b87..738ca2c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
@@ -50,7 +50,7 @@
     }
 
     override fun createTypeSpec(): TypeSpec {
-        val builder = TypeSpec.classBuilder(dao.implClassName)
+        val builder = TypeSpec.classBuilder(dao.implTypeName)
         val scope = CodeGenScope()
 
         val insertionMethods = groupAndCreateInsertionMethods(scope)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
index 9ae0510..69ca7f2 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
@@ -20,13 +20,21 @@
 import com.android.support.room.ext.N
 import com.android.support.room.ext.RoomTypeNames
 import com.android.support.room.ext.SupportDbTypeNames
+import com.android.support.room.ext.T
 import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.DaoMethod
 import com.android.support.room.vo.Database
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.TypeSpec
+import stripNonJava
 import javax.lang.model.element.Modifier
+import javax.lang.model.element.Modifier.PRIVATE
 import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.VOLATILE
 
 /**
  * Writes implementation of classes that were annotated with @Database.
@@ -40,9 +48,43 @@
             addMethod(createConstructor())
             addMethod(createCreateOpenHelper())
         }
+        addDaoImpls(builder)
         return builder.build()
     }
 
+    private fun  addDaoImpls(builder: TypeSpec.Builder) {
+        val scope = CodeGenScope()
+        builder.apply {
+            database.daoMethods.forEach { method ->
+                val name = method.dao.typeName.simpleName().decapitalize().stripNonJava()
+                var fieldName = scope.getTmpVar("_$name")
+                val field = FieldSpec.builder(method.dao.typeName, fieldName,
+                        PRIVATE, VOLATILE).build()
+                addField(field)
+                addMethod(createDaoGetter(field, method))
+            }
+        }
+    }
+
+    private fun createDaoGetter(field: FieldSpec, method: DaoMethod) : MethodSpec {
+        return MethodSpec.overriding(MoreElements.asExecutable(method.element)).apply {
+            beginControlFlow("if ($N != null)", field).apply {
+                addStatement("return $N", field)
+            }
+            nextControlFlow("else").apply {
+                beginControlFlow("synchronized(this)").apply {
+                    beginControlFlow("if($N == null)", field).apply {
+                        addStatement("$N = new $T(this)", field, method.dao.implTypeName)
+                    }
+                    endControlFlow()
+                    addStatement("return $N", field)
+                }
+                endControlFlow()
+            }
+            endControlFlow()
+        }.build()
+    }
+
     private fun createConstructor(): MethodSpec {
         return MethodSpec.constructorBuilder().apply {
             addModifiers(Modifier.PUBLIC)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
index 15d0a16..f7d1c4c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityInsertionAdapterWriter.kt
@@ -70,7 +70,7 @@
                         } else {
                             "$valueParam.${field.getter.name}()"
                         }
-                        adapter.bindToStmt(stmtParam, "$index", varName, bindScope)
+                        adapter.bindToStmt(stmtParam, "${index + 1}", varName, bindScope)
                     }
                 }
                 addCode(bindScope.builder().build())
diff --git a/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..db92d17
--- /dev/null
+++ b/room/compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.android.support.room.RoomProcessor
\ No newline at end of file
diff --git a/room/compiler/src/test/data/daoWriter/output/WriterDao.java b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
index 6ca04ca..34f2729 100644
--- a/room/compiler/src/test/data/daoWriter/output/WriterDao.java
+++ b/room/compiler/src/test/data/daoWriter/output/WriterDao.java
@@ -42,18 +42,18 @@
 
             @Override
             public void bind(SupportSQLiteStatement stmt, User value) {
-                stmt.bindLong(0, value.uid);
+                stmt.bindLong(1, value.uid);
                 if (value.name == null) {
-                    stmt.bindNull(1);
-                } else {
-                    stmt.bindString(1, value.name);
-                }
-                if (value.getLastName() == null) {
                     stmt.bindNull(2);
                 } else {
-                    stmt.bindString(2, value.getLastName());
+                    stmt.bindString(2, value.name);
                 }
-                stmt.bindLong(3, value.age);
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
             }
         };
         this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
@@ -65,18 +65,18 @@
 
             @Override
             public void bind(SupportSQLiteStatement stmt, User value) {
-                stmt.bindLong(0, value.uid);
+                stmt.bindLong(1, value.uid);
                 if (value.name == null) {
-                    stmt.bindNull(1);
-                } else {
-                    stmt.bindString(1, value.name);
-                }
-                if (value.getLastName() == null) {
                     stmt.bindNull(2);
                 } else {
-                    stmt.bindString(2, value.getLastName());
+                    stmt.bindString(2, value.name);
                 }
-                stmt.bindLong(3, value.age);
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
             }
         };
     }
diff --git a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
index 0a582b7..c71f3ae 100644
--- a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
@@ -22,4 +22,5 @@
     public ComplexDatabase(DatabaseConfiguration configuration) {
         super(configuration);
     }
+    abstract ComplexDao getComplexDao();
 }
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index 16652a5..19060f8 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -5,8 +5,11 @@
 import com.android.support.db.SupportSQLiteOpenHelper.Callback;
 import com.android.support.db.SupportSQLiteOpenHelper.Configuration;
 import com.android.support.room.DatabaseConfiguration;
+import java.lang.Override;
 
 public class ComplexDatabase_Impl extends ComplexDatabase {
+    private volatile ComplexDao _complexDao;
+
     public ComplexDatabase_Impl(DatabaseConfiguration configuration) {
         super(configuration);
     }
@@ -36,4 +39,18 @@
         final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
         return _helper;
     }
+
+    @Override
+    ComplexDao getComplexDao() {
+        if (_complexDao != null) {
+            return _complexDao;
+        } else {
+            synchronized(this) {
+                if(_complexDao == null) {
+                    _complexDao = new ComplexDao_Impl(this);
+                }
+                return _complexDao;
+            }
+        }
+    }
 }
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
index 48f880b..6682e36 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -80,7 +80,7 @@
     @Test
     fun bind() {
         simpleRun { invocation ->
-            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val adapter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
             adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(scope.generate().trim(), `is`(bindCode))
@@ -94,7 +94,7 @@
             return // no-op for those
         }
         simpleRun { invocation ->
-            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val adapter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
             adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(scope.generate().trim(), `is`(
@@ -130,7 +130,7 @@
     @Test
     fun read() {
         simpleRun { invocation ->
-            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val adapter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
             adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(scope.generate().trim(), `is`(cursorCode))
@@ -144,7 +144,7 @@
             return // no-op for those
         }
         simpleRun { invocation ->
-            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val adapter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
             adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(scope.generate().trim(), `is`(
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
index e3f5b52..43ed64d 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
@@ -111,7 +111,7 @@
     fun forward() {
         simpleRun { invocation ->
             val stringTypeMirror = invocation.context.COMMON_TYPES.STRING
-            val converter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val converter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findTypeConverter(input.getTypeMirror(invocation.processingEnv),
                             stringTypeMirror)!!
             converter.convertForward("inp", "out", scope)
@@ -125,7 +125,7 @@
     fun backward() {
         simpleRun { invocation ->
             val stringTypeMirror = invocation.context.COMMON_TYPES.STRING
-            val converter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val converter = TypeAdapterStore(Context(invocation.processingEnv))
                     .findTypeConverter(input.getTypeMirror(invocation.processingEnv),
                             stringTypeMirror)!!
             converter.convertBackward("inp", "out", scope)
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
index b62025d..f47483f 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/TypeAdapterStoreTest.kt
@@ -46,7 +46,7 @@
     @Test
     fun testDirect() {
         singleRun { invocation ->
-            val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val store = TypeAdapterStore(Context(invocation.processingEnv))
             val primitiveType = invocation.processingEnv.typeUtils.getPrimitiveType(TypeKind.INT)
             val adapter = store.findColumnTypeAdapter(primitiveType)
             assertThat(adapter, notNullValue())
@@ -56,7 +56,7 @@
     @Test
     fun testVia1TypeAdapter() {
         singleRun { invocation ->
-            val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val store = TypeAdapterStore(Context(invocation.processingEnv))
             val booleanType = invocation.processingEnv.typeUtils
                     .getPrimitiveType(TypeKind.BOOLEAN)
             val adapter = store.findColumnTypeAdapter(booleanType)
@@ -83,7 +83,7 @@
     @Test
     fun testVia2TypeAdapters() {
         singleRun { invocation ->
-            val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv),
+            val store = TypeAdapterStore(Context(invocation.processingEnv),
                     PointTypeConverter(invocation.processingEnv))
             val pointType = invocation.processingEnv.elementUtils
                     .getTypeElement("foo.bar.Point").asType()
@@ -116,7 +116,7 @@
     @Test
     fun testIntList() {
         singleRun { invocation ->
-            val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+            val store = TypeAdapterStore(Context(invocation.processingEnv))
             val intType = invocation.processingEnv.elementUtils
                     .getTypeElement(Integer::class.java.canonicalName)
                     .asType()
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt b/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
index c642a9d..81481ca 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/testing/TestInvocation.kt
@@ -24,5 +24,5 @@
 data class TestInvocation(val processingEnv: ProcessingEnvironment,
                           val annotations: MutableSet<out TypeElement>,
                           val roundEnv: RoundEnvironment) {
-    val context = Context(roundEnv, processingEnv)
+    val context = Context(processingEnv)
 }
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
index d3ca260..c2fce0f 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
@@ -16,9 +16,8 @@
 
 package com.android.support.room.writer
 
-import com.android.support.room.processor.DatabaseProcessor
-import com.android.support.room.testing.TestProcessor
-import com.google.auto.common.MoreElements
+import COMMON
+import com.android.support.room.RoomProcessor
 import com.google.common.truth.Truth
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaSourcesSubjectFactory
@@ -35,7 +34,9 @@
     fun simpleDb() {
         singleDb(
                 loadJavaCode("databasewriter/input/ComplexDatabase.java",
-                        "foo.bar.ComplexDatabase")
+                        "foo.bar.ComplexDatabase"),
+                loadJavaCode("daoWriter/input/ComplexDao.java",
+                        "foo.bar.ComplexDao")
         ).compilesWithoutError().and().generatesSources(
                 loadJavaCode("databasewriter/output/ComplexDatabase.java",
                         "foo.bar.ComplexDatabase_Impl")
@@ -45,18 +46,6 @@
     private fun singleDb(vararg jfo : JavaFileObject): CompileTester {
         return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
                 .that(jfo.toList() + COMMON.USER)
-                .processedWith(TestProcessor.builder()
-                        .forAnnotations(com.android.support.room.Database::class)
-                        .nextRunHandler { invocation ->
-                            val dao = invocation.roundEnv
-                                    .getElementsAnnotatedWith(
-                                            com.android.support.room.Database::class.java)
-                                    .first()
-                            val processor = DatabaseProcessor(invocation.context)
-                            val db = processor.parse(MoreElements.asType(dao))
-                            DatabaseWriter(db).write(invocation.processingEnv)
-                            true
-                        }
-                        .build())
+                .processedWith(RoomProcessor())
     }
-}
\ No newline at end of file
+}
diff --git a/room/integration-tests/testapp/.gitignore b/room/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/room/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..b038b23
--- /dev/null
+++ b/room/integration-tests/testapp/build.gradle
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion compile_sdk_version
+    buildToolsVersion build_tools_version
+
+    defaultConfig {
+        minSdkVersion min_sdk_version
+        targetSdkVersion target_sdk_version
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile project(":common")
+    compile project(":db")
+    compile project(":db-impl")
+    compile "com.android.support:appcompat-v7:$support_lib_version"
+
+    androidTestAnnotationProcessor project(":compiler")
+
+    androidTestCompile("com.android.support.test.espresso:espresso-core:$espresso_version", {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    testCompile "junit:junit:$junit_version"
+    testCompile "org.mockito:mockito-core:$mockito_version"
+    compile project(path: ':runtime')
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: rootProject.ext.localMavenRepo)
+            pom.artifactId = "runtime"
+        }
+    }
+}
+
+createAndroidCheckstyle(project)
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
+
+tasks['check'].dependsOn(tasks['connectedCheck'])
diff --git a/room/integration-tests/testapp/proguard-rules.pro b/room/integration-tests/testapp/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/room/integration-tests/testapp/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/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
new file mode 100644
index 0000000..58b86ba
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.support.room.integration.testapp;
+
+import com.android.support.room.Database;
+import com.android.support.room.DatabaseConfiguration;
+import com.android.support.room.RoomDatabase;
+import com.android.support.room.integration.testapp.dao.UserDao;
+import com.android.support.room.integration.testapp.vo.User;
+
+@Database(entities = User.class)
+public abstract class TestDatabase extends RoomDatabase {
+    /**
+     * Creates a RoomDatabase with the given configuration.
+     *
+     * @param configuration The configuration to setup the database.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public TestDatabase(DatabaseConfiguration configuration) {
+        super(configuration);
+    }
+
+    public abstract UserDao getUserDao();
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java
new file mode 100644
index 0000000..60de420
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/dao/UserDao.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.support.room.integration.testapp.dao;
+
+import com.android.support.room.Dao;
+import com.android.support.room.Insert;
+import com.android.support.room.Query;
+import com.android.support.room.integration.testapp.vo.User;
+
+import java.util.List;
+
+@Dao
+public interface UserDao {
+    @Query("select * from user where mName like :name")
+    List<User> findUsersByName(String name);
+
+    @Query("select * from user where mId = ?")
+    User load(int id);
+
+    @Insert
+    void insert(User user);
+
+    @Insert(onConflict = Insert.REPLACE)
+    void insertOrReplace(User user);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
new file mode 100644
index 0000000..b69050c
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.support.room.integration.testapp.test;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.support.room.DatabaseConfiguration;
+import com.android.support.room.integration.testapp.TestDatabase;
+import com.android.support.room.integration.testapp.TestDatabase_Impl;
+import com.android.support.room.integration.testapp.vo.User;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleEntityReadWriteTest {
+    TestDatabase mDb;
+    @Before
+    public void createDb() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        DatabaseConfiguration configuration = new DatabaseConfiguration.Builder(context).build();
+        mDb = new TestDatabase_Impl(configuration);
+    }
+    @Test
+    public void writeUserAndReadInList() throws Exception {
+        User user = new User();
+        user.setId(3);
+        user.setAge(99);
+        user.setName("george");
+        user.setLastName("kloony");
+        mDb.getUserDao().insert(user);
+        List<User> byName = mDb.getUserDao().findUsersByName("george");
+        Assert.assertEquals(user, byName.get(0));
+    }
+
+    @Test
+    public void throwExceptionOnConflict() {
+        User user = new User();
+        user.setId(3);
+        user.setAge(99);
+        user.setName("george");
+        user.setLastName("kloony");
+        mDb.getUserDao().insert(user);
+
+        User user2 = new User();
+        user2.setId(3);
+        user2.setAge(22);
+        user2.setName("michael");
+        user2.setLastName("jordo");
+        try {
+            mDb.getUserDao().insert(user2);
+            throw new AssertionFailedError("didn't throw in conflicting insertion");
+        } catch (SQLiteException ignored) {
+        }
+    }
+
+    @Test
+    public void replaceOnConflict() {
+        User user = new User();
+        user.setId(3);
+        user.setAge(99);
+        user.setName("george");
+        user.setLastName("kloony");
+        mDb.getUserDao().insert(user);
+
+        User user2 = new User();
+        user2.setId(3);
+        user2.setAge(22);
+        user2.setName("michael");
+        user2.setLastName("jordo");
+        mDb.getUserDao().insertOrReplace(user2);
+        Assert.assertEquals(user2, mDb.getUserDao().load(3));
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java
new file mode 100644
index 0000000..2ccedb0
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/vo/User.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.support.room.integration.testapp.vo;
+
+import com.android.support.room.Entity;
+import com.android.support.room.PrimaryKey;
+
+@Entity
+public class User {
+    @PrimaryKey
+    private int mId;
+    private String mName;
+    private String mLastName;
+    private int mAge;
+
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        this.mId = id;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        this.mName = name;
+    }
+
+    public String getLastName() {
+        return mLastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.mLastName = lastName;
+    }
+
+    public int getAge() {
+        return mAge;
+    }
+
+    public void setAge(int age) {
+        this.mAge = age;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        User user = (User) o;
+
+        if (mId != user.mId) return false;
+        if (mAge != user.mAge) return false;
+        if (mName != null ? !mName.equals(user.mName) : user.mName != null) return false;
+        return mLastName != null ? mLastName.equals(user.mLastName) : user.mLastName == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId;
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + (mLastName != null ? mLastName.hashCode() : 0);
+        result = 31 * result + mAge;
+        return result;
+    }
+}
diff --git a/room/integration-tests/testapp/src/main/AndroidManifest.xml b/room/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..eeacd10
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.support.room.integration.testapp">
+    <application android:allowBackup="true"
+                 android:supportsRtl="true"/>
+</manifest>
diff --git a/room/runtime/src/main/java/com/android/support/room/Room.java b/room/runtime/src/main/java/com/android/support/room/Room.java
index 5971349..a7425b1 100644
--- a/room/runtime/src/main/java/com/android/support/room/Room.java
+++ b/room/runtime/src/main/java/com/android/support/room/Room.java
@@ -18,8 +18,6 @@
 
 import android.support.annotation.Nullable;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -58,25 +56,16 @@
             final Class<? extends CursorConverter<T>> aClass =
                     (Class<? extends CursorConverter<T>>) Class.forName(
                             fullPackage + "." + converterName);
-            Constructor<? extends CursorConverter<T>> constructor =
-                    aClass.getDeclaredConstructor(klass);
-            constructor.setAccessible(true);
-            return constructor.newInstance();
+            return aClass.newInstance();
         } catch (ClassNotFoundException e) {
             throw new RuntimeException("cannot find cursor converter for "
                     + klass.getCanonicalName());
-        } catch (NoSuchMethodException e) {
-            throw new RuntimeException("CursorConverters must have a no-arg constructor:"
-                    + klass.getCanonicalName());
         } catch (IllegalAccessException e) {
             throw new RuntimeException("Cannot access cursor converter constructor"
                     + klass.getCanonicalName());
         } catch (InstantiationException e) {
             throw new RuntimeException("Failed to create an instance of the cursor converter"
                     + klass.getCanonicalName());
-        } catch (InvocationTargetException e) {
-            throw new RuntimeException("Failed to create an instance of the cursor converter"
-                    + klass.getCanonicalName());
         }
     }
 
diff --git a/room/settings.gradle b/room/settings.gradle
index c1175dc..e5957e9 100644
--- a/room/settings.gradle
+++ b/room/settings.gradle
@@ -18,4 +18,5 @@
 include 'compiler'
 include 'db'
 include ':db-impl'
+include ':integration-tests:testapp'