Query result adapters

This CL introduces the mecnism to generate the code for a @Query.

I've also introduced a db package which abstracts all communication
with the SqliteDatabase. This will grow to allow people to swap
the underlying sqlite implementation.

Bug: 32342709
Bug: 30355996
Test: DaoWriterTest.kt, EntityCursorConverterWriterTest.kt
Change-Id: I4962ebd3f095839adf00e36ddd1588df7b4ac9c1
diff --git a/flatfoot-common/init.gradle b/flatfoot-common/init.gradle
index 48a220e..b73c90b 100644
--- a/flatfoot-common/init.gradle
+++ b/flatfoot-common/init.gradle
@@ -70,7 +70,7 @@
                   fileTree(dir : sourceDir, include : "**/*.kt").each{ file ->
                       file.readLines().eachWithIndex { line, index ->
                           if (line.size() > 100) {
-                              throw new Exception("line too long:\nfile: $file line:$index line: $line")
+                              throw new Exception("line too long: file: $file line:$index line: $line")
                           }
                       }
                   }
diff --git a/lifecycle/compiler/src/tests/test-data/IGNORE_CHECKSTYLE b/lifecycle/compiler/src/tests/test-data/IGNORE_CHECKSTYLE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/IGNORE_CHECKSTYLE
diff --git a/room/common/build.gradle b/room/common/build.gradle
index 77e32a5..00406d3 100644
--- a/room/common/build.gradle
+++ b/room/common/build.gradle
@@ -20,6 +20,7 @@
 sourceCompatibility = 1.7
 
 dependencies {
+    compile "com.android.support:support-annotations:$support_lib_version"
     testCompile "junit:junit:$junit_version"
     testCompile "org.mockito:mockito-core:$mockito_version"
 
@@ -34,4 +35,4 @@
     }
 }
 
-createAndroidCheckstyle(project)
\ No newline at end of file
+createAndroidCheckstyle(project)
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index 746f34f..f23951a 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -41,12 +41,15 @@
     compile "com.google.auto:auto-common:$auto_common_version"
     compile "com.squareup:javapoet:$javapoet_version"
     compile 'org.antlr:antlr4:4.5.3'
-
     testCompile "com.google.testing.compile:compile-testing:$compile_testing_version"
     testCompile "junit:junit:$junit_version"
     testCompile "com.intellij:annotations:$intellij_annotation"
     testCompile "org.mockito:mockito-core:$mockito_version"
-    testCompile fileTree(dir: "${sdkHandler.sdkFolder}/platforms/android-$target_sdk_version/", include : "android.jar")
+    testCompile fileTree(dir: "${sdkHandler.sdkFolder}/platforms/android-$target_sdk_version/",
+            include : "android.jar")
+    testCompile fileTree(dir: "${new File(project(":runtime").buildDir, "libJar")}", include : "*.jar")
+    testCompile fileTree(dir: "${new File(project(":db").buildDir, "libJar")}", include : "*.jar")
+    testCompile files(org.gradle.internal.jvm.Jvm.current().getToolsJar())
 }
 
 uploadArchives {
@@ -69,6 +72,8 @@
 }
 
 tasks.findByName("compileKotlin").dependsOn(generateAntlrTask)
+tasks.findByName("compileKotlin").dependsOn(":runtime:jarDebug")
+tasks.findByName("compileKotlin").dependsOn(":db:jarDebug")
 
 createKotlinCheckstyle(project)
 
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt b/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
index 75df8b9..9d5c2c2 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
@@ -31,6 +31,18 @@
 fun KClass<*>.arrayTypeName() = ArrayTypeName.of(typeName())
 fun TypeMirror.typeName() = TypeName.get(this)
 
+object SupportDbTypeNames {
+    val DB: ClassName = ClassName.get("com.android.support.db", "SupportDb")
+    val SQLITE_STMT : ClassName = ClassName.get("com.android.support.db", "SupportSqliteStatement")
+}
+
 object RoomTypeNames {
     val STRING_UTIL: ClassName = ClassName.get("com.android.support.room.util", "StringUtil")
+    val CURSOR_CONVERTER : ClassName = ClassName.get("com.android.support.room", "CursorConverter")
+    val ROOM : ClassName = ClassName.get("com.android.support.room", "Room")
+    val ROOM_DB : ClassName = ClassName.get("com.android.support.room", "RoomDatabase")
+}
+
+object AndroidTypeNames {
+    val CURSOR : ClassName = ClassName.get("android.database", "Cursor")
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/ext/type_mirror_ext.kt b/room/compiler/src/main/kotlin/com/android/support/room/ext/type_mirror_ext.kt
new file mode 100644
index 0000000..a647c7e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/ext/type_mirror_ext.kt
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.lang.model.type.TypeMirror
+import javax.lang.model.type.TypeKind.BOOLEAN
+import javax.lang.model.type.TypeKind.BYTE
+import javax.lang.model.type.TypeKind.SHORT
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.CHAR
+import javax.lang.model.type.TypeKind.FLOAT
+import javax.lang.model.type.TypeKind.DOUBLE
+
+fun TypeMirror.defaultValue() : String {
+    return when(this.kind) {
+        BOOLEAN -> "false"
+        BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE -> "0"
+        else -> "null"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
index 213db73..efd5f55 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
@@ -44,6 +44,6 @@
         val type = TypeName.get(declaredType)
         context.checker.notUnbound(type, element,
                 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES)
-        return Dao(element = element, type = type, queryMethods = methods)
+        return Dao(element = element, type = declaredType, queryMethods = methods)
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/EntityProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/EntityProcessor.kt
index 03cc86d..368778b 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/EntityProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/EntityProcessor.kt
@@ -27,7 +27,6 @@
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
-import com.squareup.javapoet.TypeName
 import javax.lang.model.element.ElementKind
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.Modifier
@@ -87,7 +86,7 @@
         }
         context.checker.notBlank(tableName, element,
                 ProcessorErrors.ENTITY_TABLE_NAME_CANNOT_BE_EMPTY)
-        val entity = Entity(tableName, TypeName.get(declaredType), fields)
+        val entity = Entity(tableName, declaredType, fields)
         context.checker.check(entity.primaryKeys.isNotEmpty(), element,
                 ProcessorErrors.MISSING_PRIMARY_KEY)
         return entity
@@ -98,7 +97,11 @@
 
         fields.forEach { field ->
             if (!field.element.hasAnyOf(PRIVATE)) {
-                field.getter = FieldGetter(field.name, CallType.FIELD)
+                field.getter = FieldGetter(
+                        name = field.name,
+                        type = field.type,
+                        callType = CallType.FIELD,
+                        columnAdapter = context.typeAdapterStore.findColumnTypeAdapter(field.type))
             } else {
                 val matching = getterCandidates
                         .filter {
@@ -115,11 +118,20 @@
                 val match = matching.firstOrNull()
                 if (match == null) {
                     // just assume we can set it. the error will block javac anyways.
-                    field.getter = FieldGetter(field.name, CallType.FIELD)
+                    field.getter = FieldGetter(
+                            name = field.name,
+                            type = field.type,
+                            callType = CallType.FIELD,
+                            columnAdapter = context.typeAdapterStore
+                                    .findColumnTypeAdapter(field.type))
                 } else {
-                    field.getter = FieldGetter(match.simpleName.toString(), CallType.METHOD)
+                    field.getter = FieldGetter(
+                            name = match.simpleName.toString(),
+                            type = match.returnType,
+                            callType = CallType.METHOD,
+                            columnAdapter = context.typeAdapterStore
+                                    .findColumnTypeAdapter(match.returnType))
                 }
-
             }
         }
     }
@@ -129,7 +141,11 @@
 
         fields.forEach { field ->
             if (!field.element.hasAnyOf(PRIVATE)) {
-                field.setter = FieldSetter(field.name, CallType.FIELD)
+                field.setter = FieldSetter(
+                        name = field.name,
+                        type = field.type,
+                        callType = CallType.FIELD,
+                        columnAdapter = context.typeAdapterStore.findColumnTypeAdapter(field.type))
             } else {
                 val matching = setterCandidates
                         .filter {
@@ -146,9 +162,20 @@
                 val match = matching.firstOrNull()
                 if (match == null) {
                     // default to field setter
-                    field.setter = FieldSetter(field.name, CallType.FIELD)
+                    field.setter = FieldSetter(
+                            name = field.name,
+                            type = field.type,
+                            callType = CallType.FIELD,
+                            columnAdapter = context.typeAdapterStore
+                                    .findColumnTypeAdapter(field.type))
                 } else {
-                    field.setter = FieldSetter(match.simpleName.toString(), CallType.METHOD)
+                    val paramType = match.parameters.first().asType()
+                    field.setter = FieldSetter(
+                            name = match.simpleName.toString(),
+                            type = paramType,
+                            callType = CallType.METHOD,
+                            columnAdapter = context.typeAdapterStore
+                                    .findColumnTypeAdapter(paramType))
                 }
             }
         }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/FieldProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/FieldProcessor.kt
index 87ef1ee..e37eb21 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/FieldProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/FieldProcessor.kt
@@ -45,7 +45,7 @@
         context.checker.notUnbound(type, element,
                 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS)
         return Field(name = name,
-                type = type,
+                type = member,
                 primaryKey = MoreElements.isAnnotationPresent(element, PrimaryKey::class.java),
                 element = element,
                 columnName = columnName)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
index ba27372..d8480e6 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
@@ -52,20 +52,26 @@
     val QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE = "Query method parameters cannot start" +
             " with underscore (_)."
 
+    val CANNOT_FIND_QUERY_RESULT_ADAPTER = "Not sure how to convert a Cursor to this method's " +
+            "return type"
+
     private val TOO_MANY_MATCHING_GETTERS = "Ambiguous getter for %s. All of the following " +
             "match: %s. You can @Ignore the ones that you don't want to match."
-    fun tooManyMatchingGetters(field : Field, methodNames : List<String>) : String {
+
+    fun tooManyMatchingGetters(field: Field, methodNames: List<String>): String {
         return TOO_MANY_MATCHING_GETTERS.format(field, methodNames.joinToString(", "))
     }
 
     private val TOO_MANY_MATCHING_SETTERS = "Ambiguous setter for %s. All of the following " +
             "match: %s. You can @Ignore the ones that you don't want to match."
-    fun tooManyMatchingSetter(field: Field, methodNames: List<String>) : String {
+
+    fun tooManyMatchingSetter(field: Field, methodNames: List<String>): String {
         return TOO_MANY_MATCHING_SETTERS.format(field, methodNames.joinToString(", "))
     }
 
     private val MISSING_PARAMETER_FOR_BIND = "Each bind variable in the query must have a" +
             " matching method parameter. Cannot find method parameters for %s."
+
     fun missingParameterForBindVariable(bindVarName: List<String>): String {
         return MISSING_PARAMETER_FOR_BIND.format(bindVarName.joinToString(", "))
     }
@@ -77,6 +83,4 @@
                 unusedParams.joinToString(","))
     }
 
-
-
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
index f499124..fd3849c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
@@ -29,7 +29,7 @@
 import javax.lang.model.type.TypeKind
 
 class QueryMethodProcessor(val context: Context) {
-    val parameterParser = ParameterParser(context)
+    val parameterParser = QueryParameterProcessor(context)
     fun parse(containing: DeclaredType, executableElement: ExecutableElement): QueryMethod {
         val asMember = context.processingEnv.typeUtils.asMemberOf(containing, executableElement)
         val executableType = MoreTypes.asExecutable(asMember)
@@ -56,13 +56,18 @@
         context.checker.notUnbound(returnTypeName, executableElement,
                 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS)
 
+        val resultAdapter = context.typeAdapterStore
+                .findQueryResultAdapter(executableType.returnType)
+        context.checker.check(resultAdapter != null, executableElement,
+                ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER)
         val queryMethod = QueryMethod(
                 element = executableElement,
                 query = query,
                 name = executableElement.simpleName.toString(),
                 returnType = executableType.returnType,
                 parameters = executableElement.parameters
-                        .map { parameterParser.parse(containing, it) })
+                        .map { parameterParser.parse(containing, it) },
+                resultAdapter = resultAdapter)
 
         val missing = queryMethod.sectionToParamMapping
                 .filter { it.second == null }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/ParameterParser.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryParameterProcessor.kt
similarity index 84%
rename from room/compiler/src/main/kotlin/com/android/support/room/processor/ParameterParser.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/processor/QueryParameterProcessor.kt
index ad6d44f..950d420 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/ParameterParser.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryParameterProcessor.kt
@@ -16,15 +16,15 @@
 
 package com.android.support.room.processor
 
-import com.android.support.room.vo.Parameter
+import com.android.support.room.vo.QueryParameter
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
 import com.squareup.javapoet.TypeName
 import javax.lang.model.element.VariableElement
 import javax.lang.model.type.DeclaredType
 
-class ParameterParser(val context: Context) {
-    fun parse(containing: DeclaredType, element: VariableElement): Parameter {
+class QueryParameterProcessor(val context: Context) {
+    fun parse(containing: DeclaredType, element: VariableElement): QueryParameter {
         val asMember = MoreTypes.asMemberOf(context.processingEnv.typeUtils, containing, element)
         val parameterAdapter = context.typeAdapterStore.findQueryParameterAdapter(asMember)
         context.checker.check(parameterAdapter != null, element,
@@ -33,7 +33,8 @@
         val name = element.simpleName.toString()
         context.checker.check(!name.startsWith("_"), element,
                 ProcessorErrors.QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE)
-
-        return Parameter(name, asMember, parameterAdapter)
+        return QueryParameter(name = name,
+                type = asMember,
+                queryParamAdapter = parameterAdapter)
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeAdapterStore.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeAdapterStore.kt
index 5353094..3b6fabb 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeAdapterStore.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeAdapterStore.kt
@@ -16,12 +16,22 @@
 
 package com.android.support.room.solver
 
+import com.android.support.room.Entity
+import com.android.support.room.ext.hasAnnotation
 import com.android.support.room.processor.Context
-import com.android.support.room.solver.query.ArrayQueryParameterAdapter
-import com.android.support.room.solver.query.BasicQueryParameterAdapter
-import com.android.support.room.solver.query.CollectionQueryParameterAdapter
-import com.android.support.room.solver.query.QueryParameterAdapter
+import com.android.support.room.solver.query.parameter.ArrayQueryParameterAdapter
+import com.android.support.room.solver.query.parameter.BasicQueryParameterAdapter
+import com.android.support.room.solver.query.parameter.CollectionQueryParameterAdapter
+import com.android.support.room.solver.query.parameter.QueryParameterAdapter
+import com.android.support.room.solver.query.result.ArrayQueryResultAdapter
+import com.android.support.room.solver.query.result.ListQueryResultAdapter
+import com.android.support.room.solver.query.result.EntityRowAdapter
+import com.android.support.room.solver.query.result.QueryResultAdapter
+import com.android.support.room.solver.query.result.RowAdapter
+import com.android.support.room.solver.query.result.SingleColumnRowAdapter
+import com.android.support.room.solver.query.result.SingleEntityQueryResultAdapter
 import com.android.support.room.solver.types.BoxedBooleanToBoxedIntConverter
+import com.android.support.room.solver.types.BoxedPrimitiveColumnTypeAdapter
 import com.android.support.room.solver.types.BoxedPrimitiveToStringConverter
 import com.android.support.room.solver.types.ColumnTypeAdapter
 import com.android.support.room.solver.types.CompositeAdapter
@@ -38,6 +48,7 @@
 import com.google.common.annotations.VisibleForTesting
 import java.util.*
 import javax.lang.model.type.ArrayType
+import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
 
 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
@@ -69,8 +80,11 @@
             adapters.add(adapter)
         }
 
-        PrimitiveColumnTypeAdapter
+        val primitives = PrimitiveColumnTypeAdapter
                 .createPrimitiveAdapters(context.processingEnv)
+        primitives.forEach(::addColumnAdapter)
+        BoxedPrimitiveColumnTypeAdapter
+                .createBoxedPrimitiveAdapters(context.processingEnv, primitives)
                 .forEach(::addColumnAdapter)
         addColumnAdapter(StringColumnTypeAdapter(context.processingEnv))
         addTypeConverter(IntListConverter.create(context.processingEnv))
@@ -91,7 +105,7 @@
         columnTypeAdapters.map { it.out }
     }
 
-    fun findAdapter(out: TypeMirror): ColumnTypeAdapter? {
+    fun findColumnTypeAdapter(out: TypeMirror): ColumnTypeAdapter? {
         val adapters = getAllColumnAdapters(out)
         if (adapters.isNotEmpty()) {
             return adapters.last()
@@ -107,6 +121,56 @@
         return findTypeConverter(input, listOf(output))
     }
 
+    fun findQueryResultAdapter(typeMirror: TypeMirror) : QueryResultAdapter? {
+        if (typeMirror.kind == TypeKind.DECLARED) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            if (declared.typeArguments.isEmpty()) {
+                val rowAdapter = findRowAdapter(typeMirror) ?: return null
+                return SingleEntityQueryResultAdapter(rowAdapter)
+            }
+            // TODO make this flexible so that things like LiveData, Rx can work
+            if (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)) {
+                val typeArg = declared.typeArguments.first()
+                val rowAdapter = findRowAdapter(typeArg) ?: return null
+                return ListQueryResultAdapter(rowAdapter)
+            }
+            return null
+        } else if (typeMirror.kind == TypeKind.ARRAY) {
+            val array = MoreTypes.asArray(typeMirror)
+            val rowAdapter = findRowAdapter(array.componentType) ?: return null
+            return ArrayQueryResultAdapter(rowAdapter)
+        } else {
+            val rowAdapter = findRowAdapter(typeMirror) ?: return null
+            return SingleEntityQueryResultAdapter(rowAdapter)
+        }
+    }
+
+    private fun findRowAdapter(typeMirror: TypeMirror) : RowAdapter? {
+        if (typeMirror.kind == TypeKind.DECLARED) {
+            val declared = MoreTypes.asDeclared(typeMirror)
+            if (declared.typeArguments.isNotEmpty()) {
+                // TODO one day support this
+                return null
+            }
+            val asElement = MoreTypes.asElement(typeMirror)
+            if (asElement.hasAnnotation(Entity::class)) {
+                return EntityRowAdapter(typeMirror)
+            }
+            val singleColumn = findColumnTypeAdapter(typeMirror)
+
+            if (singleColumn != null) {
+                return SingleColumnRowAdapter(singleColumn)
+            }
+
+            // TODO we can allow any class actually but need a proper API for that to avoid false
+            // positives
+            return null
+        } else {
+            val singleColumn = findColumnTypeAdapter(typeMirror) ?: return null
+            return SingleColumnRowAdapter(singleColumn)
+        }
+    }
+
     fun findQueryParameterAdapter(typeMirror : TypeMirror) : QueryParameterAdapter? {
         if (MoreTypes.isType(typeMirror)
                 && (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/ArrayQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/ArrayQueryParameterAdapter.kt
similarity index 96%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/query/ArrayQueryParameterAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/ArrayQueryParameterAdapter.kt
index 9f83a88..94a5760 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/ArrayQueryParameterAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/ArrayQueryParameterAdapter.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver.query
+package com.android.support.room.solver.query.parameter
 
 import com.android.support.room.ext.L
 import com.android.support.room.ext.T
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/BasicQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/BasicQueryParameterAdapter.kt
similarity index 95%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/query/BasicQueryParameterAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/BasicQueryParameterAdapter.kt
index edd3289..c6328af 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/BasicQueryParameterAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/BasicQueryParameterAdapter.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver.query
+package com.android.support.room.solver.query.parameter
 
 import com.android.support.room.ext.L
 import com.android.support.room.solver.CodeGenScope
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/CollectionQueryParameterAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/CollectionQueryParameterAdapter.kt
similarity index 96%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/query/CollectionQueryParameterAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/CollectionQueryParameterAdapter.kt
index 541fa68..b4c2c95 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/CollectionQueryParameterAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/CollectionQueryParameterAdapter.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver.query
+package com.android.support.room.solver.query.parameter
 
 import com.android.support.room.ext.L
 import com.android.support.room.ext.T
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryParameterAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/QueryParameterAdapter.kt
similarity index 95%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryParameterAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/QueryParameterAdapter.kt
index fcf60f0..90e2248 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryParameterAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/parameter/QueryParameterAdapter.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver.query
+package com.android.support.room.solver.query.parameter
 
 import com.android.support.room.solver.CodeGenScope
 
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ArrayQueryResultAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ArrayQueryResultAdapter.kt
new file mode 100644
index 0000000..89ce6fd
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ArrayQueryResultAdapter.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.TypeName
+
+class ArrayQueryResultAdapter(val rowAdapter: RowAdapter) : QueryResultAdapter() {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val converter = rowAdapter.init(cursorVarName, scope)
+            val arrayType = ArrayTypeName.of(type.typeName())
+            addStatement("final $T $L = new $T[$L.getCount()]",
+                    arrayType, outVarName, type.typeName(), cursorVarName)
+            val tmpVarName = scope.getTmpVar("_item")
+            val indexVar = scope.getTmpVar("_index")
+            addStatement("$T $L = 0", TypeName.INT, indexVar)
+            beginControlFlow("while($L.moveToNext())", cursorVarName).apply {
+                addStatement("final $T $L", type.typeName(), tmpVarName)
+                converter.convert(tmpVarName, cursorVarName)
+                addStatement("$L[$L] = $L", outVarName, indexVar, tmpVarName)
+                addStatement("$L ++", indexVar)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/EntityRowAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/EntityRowAdapter.kt
new file mode 100644
index 0000000..9fd05a2
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/EntityRowAdapter.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import com.squareup.javapoet.ParameterizedTypeName
+import javax.lang.model.type.TypeMirror
+
+class EntityRowAdapter(type : TypeMirror) : RowAdapter(type) {
+    override fun init(cursorVarName: String, scope : CodeGenScope) : RowConverter {
+        val converterVar = scope.getTmpVar("_converter")
+        scope.builder()
+                .addStatement("final $T $L = $T.getConverter($T.class)",
+                ParameterizedTypeName.get(RoomTypeNames.CURSOR_CONVERTER, out.typeName()),
+                converterVar, RoomTypeNames.ROOM, out.typeName())
+        return object : RowConverter {
+            override fun convert(outVarName: String, cursorVarName: String) {
+                scope.builder()
+                        .addStatement("$L = $L.convert($L)", outVarName, converterVar,
+                                cursorVarName)
+            }
+
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ListQueryResultAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ListQueryResultAdapter.kt
new file mode 100644
index 0000000..ece879d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/ListQueryResultAdapter.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import java.util.ArrayList
+
+class ListQueryResultAdapter(val rowAdapter: RowAdapter) : QueryResultAdapter() {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val converter = rowAdapter.init(cursorVarName, scope)
+            val collectionType = ParameterizedTypeName
+                    .get(ClassName.get(List::class.java), type.typeName())
+            val arrayListType = ParameterizedTypeName
+                    .get(ClassName.get(ArrayList::class.java), type.typeName())
+            addStatement("final $T $L = new $T($L.getCount())",
+                    collectionType, outVarName, arrayListType, cursorVarName)
+            val tmpVarName = scope.getTmpVar("_item")
+            beginControlFlow("while($L.moveToNext())", cursorVarName).apply {
+                addStatement("final $T $L", type.typeName(), tmpVarName)
+                converter.convert(tmpVarName, cursorVarName)
+                addStatement("$L.add($L)", outVarName, tmpVarName)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/QueryResultAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/QueryResultAdapter.kt
new file mode 100644
index 0000000..3f924b8
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/QueryResultAdapter.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.solver.CodeGenScope
+
+/**
+ * Gets a Cursor and converts it into the return type of a method annotated with @Query.
+ */
+abstract class QueryResultAdapter {
+    abstract fun convert(outVarName : String, cursorVarName : String, scope : CodeGenScope)
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/RowAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/RowAdapter.kt
new file mode 100644
index 0000000..08c1ed7
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/RowAdapter.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.solver.CodeGenScope
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Converts a row of a cursor result into an Entity or a primitive.
+ * <p>
+ * An instance of this is created for each usage so that it can keep local variables.
+ */
+abstract class RowAdapter(val out : TypeMirror) {
+    /**
+     * Receives this at the beginning of the conversion. Can declare variables etc to access later.
+     * It should return a function that handles the conversion in the given scope.
+     */
+    abstract fun init(cursorVarName: String, scope : CodeGenScope) : RowConverter
+
+    interface RowConverter {
+        fun convert(outVarName : String, cursorVarName : String)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleColumnRowAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleColumnRowAdapter.kt
new file mode 100644
index 0000000..ab3ae4d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleColumnRowAdapter.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.solver.types.ColumnTypeAdapter
+
+/**
+ * Wraps a row adapter when there is only 1 item  with 1 column in the response.
+ */
+class SingleColumnRowAdapter(val adapter : ColumnTypeAdapter) : RowAdapter(adapter.out) {
+    override fun init(cursorVarName: String, scope: CodeGenScope) : RowConverter {
+        return object : RowConverter {
+            override fun convert(outVarName: String, cursorVarName: String) {
+                adapter.readFromCursor(outVarName, cursorVarName, "0", scope)
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleEntityQueryResultAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleEntityQueryResultAdapter.kt
new file mode 100644
index 0000000..599b565
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/query/result/SingleEntityQueryResultAdapter.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.solver.query.result
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import defaultValue
+
+/**
+ * Wraps a row adapter when there is only 1 item in the result
+ */
+class SingleEntityQueryResultAdapter(val rowAdapter: RowAdapter) : QueryResultAdapter() {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val converter = rowAdapter.init(cursorVarName, scope)
+            addStatement("final $T $L", type.typeName(), outVarName)
+            beginControlFlow("if($L.moveToFirst())", cursorVarName)
+                converter.convert(outVarName, cursorVarName)
+            nextControlFlow("else").apply {
+                addStatement("$L = $L", outVarName, rowAdapter.out.defaultValue())
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
new file mode 100644
index 0000000..d941b02
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.solver.types
+
+import com.android.support.room.ext.L
+import com.android.support.room.solver.CodeGenScope
+import com.google.auto.common.MoreTypes
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Adapters for all boxed primitives that has direct cursor mappings.
+ */
+open class BoxedPrimitiveColumnTypeAdapter(boxed : TypeMirror,
+                                           val primitiveAdapter : PrimitiveColumnTypeAdapter)
+            : ColumnTypeAdapter(boxed) {
+    companion object {
+        fun createBoxedPrimitiveAdapters(processingEnvironment: ProcessingEnvironment,
+                                    primitiveAdapters : List<PrimitiveColumnTypeAdapter>)
+                : List<ColumnTypeAdapter> {
+
+            return primitiveAdapters.map {
+                BoxedPrimitiveColumnTypeAdapter(
+                        processingEnvironment.typeUtils
+                                .boxedClass(MoreTypes.asPrimitiveType(it.out)).asType(),
+                        it
+                )
+            }
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L == null)", valueVarName).apply {
+                addStatement("$L.bindNull($L)", stmtName, indexVarName)
+            }
+            nextControlFlow("else").apply {
+                primitiveAdapter.bindToStmt(stmtName, indexVarName, valueVarName, scope)
+            }
+            endControlFlow()
+        }
+    }
+
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder().apply {
+            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
+                addStatement("$L = null", outVarName)
+            }
+            nextControlFlow("else").apply {
+                primitiveAdapter.readFromCursor(outVarName, cursorVarName, indexVarName, scope)
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
index af48ebd..ff9b3d7 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
@@ -25,9 +25,9 @@
  */
 abstract class ColumnTypeAdapter(val out: TypeMirror) {
     val outTypeName by lazy { TypeName.get(out) }
-    abstract fun readFromCursor(outVarName : String, cursorVarName: String, index: Int,
+    abstract fun readFromCursor(outVarName : String, cursorVarName: String, indexVarName: String,
                                 scope: CodeGenScope)
 
-    abstract fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
+    abstract fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
                             scope: CodeGenScope)
-}
\ No newline at end of file
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeAdapter.kt
index ccdd23f..8d9d927 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeAdapter.kt
@@ -28,23 +28,23 @@
  */
 class CompositeAdapter(out: TypeMirror, val columnTypeAdapter: ColumnTypeAdapter,
                        val typeConverter : TypeConverter) : ColumnTypeAdapter(out) {
-    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
                                 scope: CodeGenScope) {
         scope.builder().apply {
             val tmpCursorValue = scope.getTmpVar()
             addStatement("final $T $L", columnTypeAdapter.outTypeName, tmpCursorValue)
-            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, index, scope)
+            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, indexVarName, scope)
             typeConverter.convertBackward(tmpCursorValue, outVarName, scope)
         }
     }
 
-    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
                             scope: CodeGenScope) {
         scope.builder().apply {
             val tmpVar = scope.getTmpVar()
             addStatement("final $T $L", columnTypeAdapter.out, tmpVar)
             typeConverter.convertForward(valueVarName, tmpVar, scope)
-            columnTypeAdapter.bindToStmt(stmtName, index, tmpVar, scope)
+            columnTypeAdapter.bindToStmt(stmtName, indexVarName, tmpVar, scope)
         }
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/NoOpConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/NoOpConverter.kt
index 069cb7c..d563f4d 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/NoOpConverter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/NoOpConverter.kt
@@ -24,7 +24,7 @@
  * Yes, we need this when user input is the same as the desired output.
  * <p>
  * Each query parameter receives an adapter that converts it into a String (or String[]). This
- * TypeAdapter basically serves as a wrapper for converting String parameter into the String of
+ * TypeAdapter basically serves as a wrapper for converting String parameter into the String[] of
  * the query. Not having this would require us to special case handle String, String[], List<String>
  * etc.
  */
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveColumnTypeAdapter.kt
index 96aa080..dda987b 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveColumnTypeAdapter.kt
@@ -43,7 +43,7 @@
 
     companion object {
         fun createPrimitiveAdapters(processingEnvironment: ProcessingEnvironment)
-                : List<ColumnTypeAdapter> {
+                : List<PrimitiveColumnTypeAdapter> {
             return listOf(
                     Triple(INT, "getInt", "bindLong"),
                     Triple(SHORT, "getShort", "bindLong"),
@@ -62,16 +62,16 @@
         }
     }
 
-    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
                             scope: CodeGenScope) {
         scope.builder()
-                .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, index, valueVarName)
+                .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, indexVarName, valueVarName)
     }
 
-    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
                                 scope: CodeGenScope) {
         scope.builder()
                 .addStatement("$L = $L$L.$L($L)", outVarName, cast, cursorVarName,
-                        cursorGetter, index)
+                        cursorGetter, indexVarName)
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
index 8ff150b..769af59 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
@@ -23,21 +23,20 @@
 class StringColumnTypeAdapter(processingEnvironment: ProcessingEnvironment)
     : ColumnTypeAdapter((processingEnvironment.elementUtils.getTypeElement(
         String::class.java.canonicalName)).asType()) {
-    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
+    override fun readFromCursor(outVarName: String, cursorVarName: String, indexVarName: String,
                                 scope: CodeGenScope) {
         scope.builder()
-                .addStatement("$L = $L.isNull($L) ? null : $L.getString($L)",
-                        outVarName, cursorVarName, index, cursorVarName, index)
+                .addStatement("$L = $L.getString($L)", outVarName, cursorVarName, indexVarName)
     }
 
-    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
+    override fun bindToStmt(stmtName: String, indexVarName: String, valueVarName: String,
                             scope: CodeGenScope) {
         scope.builder().apply {
             beginControlFlow("if ($L == null)", valueVarName)
-                    .addStatement("$L.bindNull($L)", stmtName, index)
+                    .addStatement("$L.bindNull($L)", stmtName, indexVarName)
             nextControlFlow("else")
-                    .addStatement("$L.bindString($L, $L)", stmtName, index, valueVarName)
+                    .addStatement("$L.bindString($L, $L)", stmtName, indexVarName, valueVarName)
             endControlFlow()
         }
     }
-}
\ No newline at end of file
+}
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 f78bb41..0184754 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
@@ -16,7 +16,15 @@
 
 package com.android.support.room.vo
 
-import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.ClassName
 import javax.lang.model.element.Element
+import javax.lang.model.type.DeclaredType
 
-data class Dao(val element : Element, val type : TypeName, val queryMethods: List<QueryMethod>)
\ No newline at end of file
+data class Dao(val element : Element, val type : DeclaredType,
+               val queryMethods: List<QueryMethod>) {
+    val typeName by lazy { ClassName.get(type) as ClassName }
+
+    val implClassName by lazy {
+        "${typeName.simpleName()}_Impl"
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
index 7a233ba..3de1dbe 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
@@ -16,9 +16,22 @@
 
 package com.android.support.room.vo
 
-import com.squareup.javapoet.TypeName
+import com.android.support.room.ext.typeName
+import com.squareup.javapoet.ClassName
+import javax.lang.model.type.DeclaredType
 
-data class Entity(val tableName : String, val type: TypeName, val fields : List<Field>) {
+data class Entity(val tableName : String, val type: DeclaredType, val fields : List<Field>) {
+    val converterClassName by lazy {
+        val typeName = this.typeName
+        if (typeName is ClassName) {
+            "${typeName.simpleName()}_CursorConverter"
+        } else {
+            "${typeName}_CursorConverter"
+        }
+    }
+
+    val typeName by lazy { type.typeName() }
+
     val primaryKeys by lazy {
         fields.filter { it.primaryKey }
     }
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 d5a401b..b3b82cf 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
@@ -16,14 +16,16 @@
 
 package com.android.support.room.vo
 
+import com.android.support.room.ext.typeName
 import com.squareup.javapoet.TypeName
 import javax.lang.model.element.Element
+import javax.lang.model.type.TypeMirror
 
-data class Field(val element : Element, val name : String,
-                 val type: TypeName,
-                 val primaryKey : Boolean, val columnName : String = name) {
-    lateinit var getter : FieldGetter
-    lateinit var setter : FieldSetter
+data class Field(val element: Element, val name: String, val type: TypeMirror,
+                 val primaryKey: Boolean, val columnName: String = name) {
+    lateinit var getter: FieldGetter
+    lateinit var setter: FieldSetter
+    val typeName by lazy { type.typeName() }
     /**
      * List of names that include variations.
      * e.g. if it is mUser, user is added to the list
@@ -39,7 +41,7 @@
                 result.add(name.substring(1).decapitalize())
             }
 
-            if (type == TypeName.BOOLEAN || type == TypeName.BOOLEAN.box()) {
+            if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) {
                 if (name.length > 2 && name.startsWith("is") && name[2].isUpperCase()) {
                     result.add(name.substring(2).decapitalize())
                 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldGetter.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldGetter.kt
index bc8c912..357ae24 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldGetter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldGetter.kt
@@ -16,4 +16,8 @@
 
 package com.android.support.room.vo
 
-data class FieldGetter(val name : String, val callType: CallType)
\ No newline at end of file
+import com.android.support.room.solver.types.ColumnTypeAdapter
+import javax.lang.model.type.TypeMirror
+
+data class FieldGetter(val name : String, val type : TypeMirror, val callType: CallType,
+                       val columnAdapter : ColumnTypeAdapter?)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldSetter.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldSetter.kt
index 60720d0..22fdeac 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldSetter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/FieldSetter.kt
@@ -16,4 +16,8 @@
 
 package com.android.support.room.vo
 
-data class FieldSetter(val name : String, val callType: CallType)
\ No newline at end of file
+import com.android.support.room.solver.types.ColumnTypeAdapter
+import javax.lang.model.type.TypeMirror
+
+data class FieldSetter(val name : String, val type : TypeMirror, val callType: CallType,
+                       val columnAdapter : ColumnTypeAdapter?)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryMethod.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryMethod.kt
index 9fc021b..3f00fc7 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryMethod.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryMethod.kt
@@ -17,6 +17,7 @@
 package com.android.support.room.vo
 
 import com.android.support.room.parser.ParsedQuery
+import com.android.support.room.solver.query.result.QueryResultAdapter
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.type.TypeMirror
 
@@ -25,7 +26,8 @@
  * It is self sufficient and must have all generics etc resolved once created.
  */
 data class QueryMethod(val element: ExecutableElement, val query: ParsedQuery, val name: String,
-                       val returnType: TypeMirror, val parameters: List<Parameter>) {
+                       val returnType: TypeMirror, val parameters: List<QueryParameter>,
+                       val resultAdapter : QueryResultAdapter?) {
     val sectionToParamMapping by lazy {
         query.bindSections.map {
             if (it.text.trim() == "?") {
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Parameter.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryParameter.kt
similarity index 79%
rename from room/compiler/src/main/kotlin/com/android/support/room/vo/Parameter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/vo/QueryParameter.kt
index 7468e4e..8ed8c97 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Parameter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/QueryParameter.kt
@@ -16,7 +16,7 @@
 
 package com.android.support.room.vo
 
-import com.android.support.room.solver.query.QueryParameterAdapter
+import com.android.support.room.solver.query.parameter.QueryParameterAdapter
 import com.android.support.room.solver.types.TypeConverter
 import com.squareup.javapoet.TypeName
 import javax.lang.model.type.TypeMirror
@@ -24,5 +24,5 @@
 /**
  * Holds the parameter for a {@link QueryMethod}.
  */
-data class Parameter(val name: String, val type: TypeMirror,
-                     val queryParamAdapter : QueryParameterAdapter?)
+data class QueryParameter(val name: String, val type: TypeMirror,
+                          val queryParamAdapter : QueryParameterAdapter?)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/ClassWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/ClassWriter.kt
new file mode 100644
index 0000000..98c9ea4
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/ClassWriter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.writer
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import javax.annotation.processing.ProcessingEnvironment
+
+/**
+ * Base class for all writers that can produce a class.
+ */
+abstract class ClassWriter(val className: ClassName) {
+
+    abstract fun createTypeSpec(): TypeSpec
+
+    fun write(processingEnv: ProcessingEnvironment) {
+        JavaFile.builder(className.packageName(), createTypeSpec())
+                .build()
+                .writeTo(processingEnv.filer)
+    }
+}
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
new file mode 100644
index 0000000..b083397
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DaoWriter.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.writer
+
+import com.android.support.room.ext.AndroidTypeNames
+import com.android.support.room.ext.L
+import com.android.support.room.ext.N
+import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.ext.T
+import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.Dao
+import com.android.support.room.vo.QueryMethod
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.Modifier.FINAL
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PUBLIC
+
+/**
+ * Creates the implementation for a class annotated with Dao.
+ */
+class DaoWriter(val dao: Dao) : ClassWriter(ClassName.get(dao.type) as ClassName) {
+    companion object {
+        val dbField : FieldSpec = FieldSpec
+                .builder(RoomTypeNames.ROOM_DB, "__db", PRIVATE, FINAL)
+                .build()
+    }
+
+    override fun createTypeSpec(): TypeSpec {
+        val builder = TypeSpec.classBuilder(dao.implClassName)
+        builder.apply {
+            addModifiers(PUBLIC)
+            if (dao.element.kind == ElementKind.INTERFACE) {
+                addSuperinterface(dao.typeName)
+            } else {
+                superclass(dao.typeName)
+            }
+            addField(dbField)
+            val dbParam = ParameterSpec.builder(dbField.type, dbField.name).build()
+            addMethod(
+                    MethodSpec.constructorBuilder().apply {
+                        addParameter(dbParam)
+                        addModifiers(PUBLIC)
+                        addStatement("this.$N = $N", dbField, dbParam)
+                    }.build()
+            )
+        }
+        dao.queryMethods.forEach { method ->
+            val baseSpec = MethodSpec.overriding(method.element).build()
+            val methodSpec = MethodSpec.methodBuilder(method.name).apply {
+                addAnnotation(Override::class.java)
+                addModifiers(baseSpec.modifiers)
+                addParameters(baseSpec.parameters)
+                varargs(baseSpec.varargs)
+                returns(baseSpec.returnType)
+                addCode(createQueryMethodBody(method))
+            }.build()
+            builder.addMethod(methodSpec)
+        }
+        return builder.build()
+    }
+
+    private fun createQueryMethodBody(method: QueryMethod) : CodeBlock {
+        val queryWriter = QueryWriter(method)
+        val scope = CodeGenScope()
+        val sqlVar = scope.getTmpVar("_sql")
+        val argsVar = scope.getTmpVar("_args")
+        queryWriter.prepareReadQuery(sqlVar, argsVar, scope)
+        scope.builder().apply {
+            val cursorVar = scope.getTmpVar("_cursor")
+            val outVar = scope.getTmpVar("_result")
+            addStatement("final $T $L = $N.query($L, $L)", AndroidTypeNames.CURSOR, cursorVar,
+                    dbField, sqlVar, argsVar)
+            beginControlFlow("try")
+                method.resultAdapter?.convert(outVar, cursorVar, scope)
+                addStatement("return $L", outVar)
+            nextControlFlow("finally")
+                addStatement("$L.close()", cursorVar)
+            endControlFlow()
+        }
+        return scope.builder().build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityCursorConverterWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityCursorConverterWriter.kt
new file mode 100644
index 0000000..391bec9
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/EntityCursorConverterWriter.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.writer
+
+import com.android.support.room.ext.AndroidTypeNames
+import com.android.support.room.ext.L
+import com.android.support.room.ext.N
+import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.ext.S
+import com.android.support.room.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.CallType.FIELD
+import com.android.support.room.vo.CallType.METHOD
+import com.android.support.room.vo.Entity
+import com.android.support.room.vo.Field
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PUBLIC
+
+class EntityCursorConverterWriter(val entity: Entity) : ClassWriter(entity.typeName as ClassName) {
+    override fun createTypeSpec(): TypeSpec {
+        return TypeSpec.classBuilder(entity.converterClassName)
+                .apply {
+                    addSuperinterface(ParameterizedTypeName.get(RoomTypeNames.CURSOR_CONVERTER,
+                            entity.typeName))
+                    addModifiers(PUBLIC)
+                    addMethod(
+                            MethodSpec.methodBuilder("convert").apply {
+                                val cursorParam = ParameterSpec
+                                        .builder(AndroidTypeNames.CURSOR, "cursor").build()
+                                addParameter(cursorParam)
+                                addAnnotation(Override::class.java)
+                                addModifiers(PUBLIC)
+                                returns(entity.typeName)
+                                addCode(buildConvertMethodBody(cursorParam))
+                            }.build()
+                    )
+                }
+                .build()
+    }
+
+    private fun buildConvertMethodBody(cursorParam: ParameterSpec) : CodeBlock {
+        // TODO support arg constructor
+        val scope = CodeGenScope()
+        val entityVar = scope.getTmpVar("_entity")
+        scope.builder().apply {
+            addStatement("$T $L = new $T()", entity.typeName, entityVar, entity.typeName)
+            val colNameVar = scope.getTmpVar("_columnName")
+            val colIndexVar = scope.getTmpVar("_columnIndex")
+            addStatement("$T $L = 0", TypeName.INT, colIndexVar)
+            beginControlFlow("for ($T $L : $N.getColumnNames())",
+                    TypeName.get(String::class.java), colNameVar, cursorParam).apply {
+                beginControlFlow("switch($L.hashCode())", colNameVar).apply {
+                    entity.fields.groupBy { it.columnName.hashCode() }.forEach {
+                        val hash = it.key
+                        beginControlFlow("case $L:", hash).apply {
+                            val fields = it.value
+                            fields.forEach { field ->
+                                beginControlFlow("if ($S.equals($L))", field.name, colNameVar)
+                                    readField(field, cursorParam, colIndexVar, entityVar, scope)
+                                endControlFlow()
+                            }
+                        }
+                        endControlFlow()
+                    }
+                }
+                endControlFlow()
+                addStatement("$L ++", colIndexVar)
+            }
+            endControlFlow()
+            addStatement("return $L", entityVar)
+        }
+        return scope.builder().build()
+    }
+
+    private fun readField(field : Field, cursorParam: ParameterSpec,
+                          indexVar : String, entityVar : String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val columnAdapter = field.getter.columnAdapter
+            when (field.setter.callType) {
+                FIELD -> {
+                    columnAdapter
+                            ?.readFromCursor("$entityVar.${field.getter.name}", cursorParam.name,
+                                    indexVar, scope)
+                }
+                METHOD -> {
+                    val tmpField = scope.getTmpVar("_tmp${field.name.capitalize()}")
+                    addStatement("final $T $L", field.getter.type.typeName(), tmpField)
+                    columnAdapter?.readFromCursor(tmpField, cursorParam.name,
+                            indexVar, scope)
+                    addStatement("$L.$L($L)", entityVar, field.setter.name, tmpField)
+                }
+            }
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/QueryWriter.kt
similarity index 95%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryWriter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/writer/QueryWriter.kt
index 647255a..fff4637 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/query/QueryWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/QueryWriter.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver.query
+package com.android.support.room.writer
 
 import com.android.support.room.ext.L
 import com.android.support.room.ext.RoomTypeNames.STRING_UTIL
@@ -26,8 +26,8 @@
 import com.android.support.room.parser.SectionType.NEWLINE
 import com.android.support.room.parser.SectionType.TEXT
 import com.android.support.room.solver.CodeGenScope
-import com.android.support.room.vo.Parameter
 import com.android.support.room.vo.QueryMethod
+import com.android.support.room.vo.QueryParameter
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
 
@@ -46,8 +46,8 @@
     }
 
     private fun createSqlQueryAndArgs(outSqlQueryName: String, outArgsName: String,
-                                      scope: CodeGenScope): List<Pair<Parameter, String>> {
-        val listSizeVars = arrayListOf<Pair<Parameter, String>>()
+                                      scope: CodeGenScope): List<Pair<QueryParameter, String>> {
+        val listSizeVars = arrayListOf<Pair<QueryParameter, String>>()
         val varargParams = queryMethod.parameters
                 .filter { it.queryParamAdapter?.isMultiple ?: false }
         val sectionToParamMapping = queryMethod.sectionToParamMapping
@@ -75,7 +75,7 @@
                                     pair.second
                                             ?.queryParamAdapter
                                             ?.getArgCount(pair.second!!.name, tmpCount, scope)
-                                    addStatement("$L.appendPlaceholders($L, $L)",
+                                    addStatement("$T.appendPlaceholders($L, $L)",
                                             STRING_UTIL, stringBuilderVar, tmpCount)
                                 } else {
                                     addStatement("$L.append($S)", stringBuilderVar, "?")
@@ -103,7 +103,7 @@
         return listSizeVars
     }
 
-    private fun bindArgs(outArgsName: String, listSizeVars : List<Pair<Parameter, String>>
+    private fun bindArgs(outArgsName: String, listSizeVars : List<Pair<QueryParameter, String>>
                          ,scope: CodeGenScope) {
         if (queryMethod.parameters.isEmpty()) {
             return
diff --git a/room/compiler/src/test/data/IGNORE_CHECKSTYLE b/room/compiler/src/test/data/IGNORE_CHECKSTYLE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/room/compiler/src/test/data/IGNORE_CHECKSTYLE
diff --git a/room/compiler/src/test/data/daoWriter/input/ComplexDao.java b/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
new file mode 100644
index 0000000..2ea5719
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/input/ComplexDao.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 foo.bar;
+import com.android.support.room.*;
+import java.util.List;
+@Dao
+abstract class ComplexDao {
+    @Query("SELECT * FROM user where uid = :id")
+    abstract public User getById(int id);
+
+    @Query("SELECT * FROM user where name LIKE :name AND lastName LIKE :lastName")
+    abstract public User findByName(String name, String lastName);
+
+    @Query("SELECT * FROM user where uid IN (:ids)")
+    abstract public List<User> loadAllByIds(int... ids);
+
+    @Query("SELECT age FROM user where id = :id")
+    abstract int getAge(int id);
+
+    @Query("SELECT age FROM user where id = IN(:ids)")
+    abstract public int[] getAllAges(int... ids);
+
+    @Query("SELECT age FROM user where id = IN(:ids)")
+    abstract public List<Integer> getAllAgesAsList(List<Integer> ids);
+}
diff --git a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
new file mode 100644
index 0000000..29adf7b
--- /dev/null
+++ b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
@@ -0,0 +1,196 @@
+/*
+ * 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 foo.bar;
+
+import android.database.Cursor;
+import com.android.support.room.CursorConverter;
+import com.android.support.room.Room;
+import com.android.support.room.RoomDatabase;
+import com.android.support.room.util.StringUtil;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ComplexDao_Impl extends ComplexDao {
+    private final RoomDatabase __db;
+
+    public ComplexDao_Impl(RoomDatabase __db) {
+        this.__db = __db;
+    }
+
+    @Override
+    public User getById(int id) {
+        String _sql = "SELECT * FROM user where uid = ?";
+        String[] _args = new String[1];
+        int _argIndex = 0;
+        _args[_argIndex] = Integer.toString(id);
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final CursorConverter<User> _converter = Room.getConverter(User.class);
+            final User _result;
+            if (_cursor.moveToFirst()) {
+                _result = _converter.convert(_cursor);
+            } else {
+                _result = null;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+
+    @Override
+    public User findByName(String name, String lastName) {
+        String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
+        String[] _args = new String[2];
+        int _argIndex = 0;
+        _args[_argIndex] = name;
+        _argIndex = 1;
+        _args[_argIndex] = lastName;
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final CursorConverter<User> _converter = Room.getConverter(User.class);
+            final User _result;
+            if (_cursor.moveToFirst()) {
+                _result = _converter.convert(_cursor);
+            } else {
+                _result = null;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+
+    @Override
+    public List<User> loadAllByIds(int... ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT * FROM user where uid IN (");
+        final int _inputSize = ids.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        String[] _args = new String[_argCount];
+        int _argIndex = 0;
+        for (int _item : ids) {
+            _args[_argIndex] = Integer.toString(_item);
+            _argIndex++;
+        }
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final CursorConverter<User> _converter = Room.getConverter(User.class);
+            final List<User> _result = new ArrayList<User>(_cursor.getCount());
+            while (_cursor.moveToNext()) {
+                final User _item_1;
+                _item_1 = _converter.convert(_cursor);
+                _result.add(_item_1);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+
+    @Override
+    int getAge(int id) {
+        String _sql = "SELECT age FROM user where id = ?";
+        String[] _args = new String[1];
+        int _argIndex = 0;
+        _args[_argIndex] = Integer.toString(id);
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final int _result;
+            if (_cursor.moveToFirst()) {
+                _result = _cursor.getInt(0);
+            } else {
+                _result = 0;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+
+    @Override
+    public int[] getAllAges(int... ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT age FROM user where id = IN(");
+        final int _inputSize = ids.length;
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        String[] _args = new String[_argCount];
+        int _argIndex = 0;
+        for (int _item : ids) {
+            _args[_argIndex] = Integer.toString(_item);
+            _argIndex++;
+        }
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final int[] _result = new int[_cursor.getCount()];
+            int _index = 0;
+            while (_cursor.moveToNext()) {
+                final int _item_1;
+                _item_1 = _cursor.getInt(0);
+                _result[_index] = _item_1;
+                _index ++;
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+
+    @Override
+    public List<Integer> getAllAgesAsList(List<Integer> ids) {
+        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        _stringBuilder.append("SELECT age FROM user where id = IN(");
+        final int _inputSize = ids.size();
+        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+        _stringBuilder.append(")");
+        String _sql = _stringBuilder.toString();
+        final int _argCount = 0 + _inputSize;
+        String[] _args = new String[_argCount];
+        int _argIndex = 0;
+        for (Integer _item : ids) {
+            _args[_argIndex] = _item == null ? null : Integer.toString(_item);
+            _argIndex++;
+        }
+        final Cursor _cursor = __db.query(_sql, _args);
+        try {
+            final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
+            while (_cursor.moveToNext()) {
+                final Integer _item_1;
+                if (_cursor.isNull(0)) {
+                    _item_1 = null;
+                } else {
+                    _item_1 = _cursor.getInt(0);
+                }
+                _result.add(_item_1);
+            }
+            return _result;
+        } finally {
+            _cursor.close();
+        }
+    }
+}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/BaseEntityParserTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/BaseEntityParserTest.kt
index 02a386d..f353dfb 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/BaseEntityParserTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/BaseEntityParserTest.kt
@@ -31,7 +31,7 @@
             package foo.bar;
             import com.android.support.room.*;
             @Entity%s
-            abstract class MyEntity {
+            public class MyEntity {
             """
         const val ENTITY_SUFFIX = "}"
     }
@@ -48,7 +48,7 @@
                     ")".trimIndent()
         }
         return Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
-                .that(JavaFileObjects.forSourceString("foo.bar.MyClass",
+                .that(JavaFileObjects.forSourceString("foo.bar.MyEntity",
                         ENTITY_PREFIX.format(attributesReplacement) + input + ENTITY_SUFFIX
                 ))
                 .processedWith(TestProcessor.builder()
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityNameMatchingVariationsTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityNameMatchingVariationsTest.kt
index c9de1ea..b5f104c 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityNameMatchingVariationsTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityNameMatchingVariationsTest.kt
@@ -20,12 +20,12 @@
 import com.android.support.room.vo.Field
 import com.android.support.room.vo.FieldGetter
 import com.android.support.room.vo.FieldSetter
-import com.squareup.javapoet.TypeName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import javax.lang.model.type.TypeKind.INT
 
 @RunWith(Parameterized::class)
 class EntityNameMatchingVariationsTest(triple: Triple<String, String, String>) :
@@ -61,14 +61,17 @@
             assertThat(entity.type.toString(), `is`("foo.bar.MyEntity"))
             assertThat(entity.fields.size, `is`(1))
             val field = entity.fields.first()
+            val intType = invocation.processingEnv.typeUtils.getPrimitiveType(INT)
             assertThat(field, `is`(Field(
                     element = field.element,
                     name = fieldName,
-                    type = TypeName.INT,
+                    type = intType,
                     primaryKey = true,
                     columnName = fieldName)))
-            assertThat(field.setter, `is`(FieldSetter(setterName, CallType.METHOD)))
-            assertThat(field.getter, `is`(FieldGetter(getterName, CallType.METHOD)))
+            assertThat(field.setter, `is`(FieldSetter(setterName, intType, CallType.METHOD,
+                    field.setter.columnAdapter)))
+            assertThat(field.getter, `is`(FieldGetter(getterName, intType, CallType.METHOD,
+                    field.getter.columnAdapter)))
         }.compilesWithoutError()
     }
 }
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityProcessorTest.kt
index 1b8e444..3c64077 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/EntityProcessorTest.kt
@@ -20,12 +20,12 @@
 import com.android.support.room.vo.Field
 import com.android.support.room.vo.FieldGetter
 import com.android.support.room.vo.FieldSetter
-import com.squareup.javapoet.TypeName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import javax.lang.model.type.TypeKind.INT
 
 @RunWith(JUnit4::class)
 class EntityProcessorTest : BaseEntityParserTest() {
@@ -40,14 +40,17 @@
             assertThat(entity.type.toString(), `is`("foo.bar.MyEntity"))
             assertThat(entity.fields.size, `is`(1))
             val field = entity.fields.first()
+            val intType = invocation.processingEnv.typeUtils.getPrimitiveType(INT)
             assertThat(field, `is`(Field(
                     element = field.element,
                     name = "id",
-                    type = TypeName.INT,
+                    type = intType,
                     primaryKey = true,
                     columnName = "id")))
-            assertThat(field.setter, `is`(FieldSetter("setId", CallType.METHOD)))
-            assertThat(field.getter, `is`(FieldGetter("getId", CallType.METHOD)))
+            assertThat(field.setter, `is`(FieldSetter("setId", intType, CallType.METHOD,
+                    field.setter.columnAdapter)))
+            assertThat(field.getter, `is`(FieldGetter("getId", intType, CallType.METHOD,
+                    field.getter.columnAdapter)))
             assertThat(entity.primaryKeys, `is`(listOf(field)))
         }.compilesWithoutError()
     }
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/FieldProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/FieldProcessorTest.kt
index 5416732..6746bbf 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/FieldProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/FieldProcessorTest.kt
@@ -26,16 +26,17 @@
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourceSubjectFactory
-import com.squareup.javapoet.ArrayTypeName
-import com.squareup.javapoet.TypeName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.mockito.Mockito.mock
+import simpleRun
 import javax.lang.model.element.Element
 import javax.lang.model.element.ElementKind
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
 
 @RunWith(JUnit4::class)
 class FieldProcessorTest {
@@ -48,24 +49,42 @@
                 """
         const val ENTITY_SUFFIX = "}"
         val ALL_PRIMITIVES = arrayListOf(
-                TypeName.BOOLEAN,
-                TypeName.BYTE,
-                TypeName.SHORT,
-                TypeName.INT,
-                TypeName.LONG,
-                TypeName.CHAR,
-                TypeName.FLOAT,
-                TypeName.DOUBLE)
+                TypeKind.INT,
+                TypeKind.BYTE,
+                TypeKind.SHORT,
+                TypeKind.INT,
+                TypeKind.LONG,
+                TypeKind.CHAR,
+                TypeKind.FLOAT,
+                TypeKind.DOUBLE)
+    }
+
+    // these 2 box methods are ugly but makes tests nicer and they are private
+    private fun TypeKind.typeMirror(invocation: TestInvocation): TypeMirror {
+        return invocation.processingEnv.typeUtils.getPrimitiveType(this)
+    }
+    private fun TypeKind.box(): String {
+        return "java.lang." + when (this) {
+            TypeKind.INT -> "Integer"
+            TypeKind.CHAR -> "Character"
+            else -> this.name.toLowerCase().capitalize()
+        }
+    }
+
+    private fun TypeKind.box(invocation: TestInvocation): TypeMirror {
+        return invocation.processingEnv.elementUtils.getTypeElement(box()).asType()
     }
 
     @Test
     fun primitives() {
         ALL_PRIMITIVES.forEach { primitive ->
-            singleEntity("$primitive x;") { field, invocation ->
+            singleEntity("${primitive.name.toLowerCase()} x;") { field, invocation ->
                 assertThat(field, `is`(
-                        Field(name = "x", type = primitive, primaryKey = false,
+                        Field(name = "x",
+                                type = primitive.typeMirror(invocation),
+                                primaryKey = false,
                                 element = field.element
-                                )))
+                        )))
             }.compilesWithoutError()
         }
     }
@@ -75,7 +94,9 @@
         ALL_PRIMITIVES.forEach { primitive ->
             singleEntity("${primitive.box()} y;") { field, invocation ->
                 assertThat(field, `is`(
-                        Field(name = "y", type = primitive.box(), primaryKey = false,
+                        Field(name = "y",
+                                type = primitive.box(invocation),
+                                primaryKey = false,
                                 element = field.element)))
             }.compilesWithoutError()
         }
@@ -88,7 +109,9 @@
             int x;
             """) { field, invocation ->
             assertThat(field, `is`(
-                    Field(name = "x", type = TypeName.INT, primaryKey = true,
+                    Field(name = "x",
+                            type = TypeKind.INT.typeMirror(invocation),
+                            primaryKey = true,
                             element = field.element)))
         }.compilesWithoutError()
     }
@@ -101,8 +124,11 @@
             int x;
             """) { field, invocation ->
             assertThat(field, `is`(
-                    Field(name = "x", type = TypeName.INT, primaryKey = true,
-                            element = field.element, columnName = "foo")))
+                    Field(name = "x",
+                            type = TypeKind.INT.typeMirror(invocation),
+                            primaryKey = true,
+                            element = field.element,
+                            columnName = "foo")))
         }.compilesWithoutError()
     }
 
@@ -118,9 +144,12 @@
     @Test
     fun primitiveArray() {
         ALL_PRIMITIVES.forEach { primitive ->
-            singleEntity("$primitive[] arr;") { field, invocation ->
+            singleEntity("${primitive.toString().toLowerCase()}[] arr;") { field, invocation ->
                 assertThat(field, `is`(
-                        Field(name = "arr", type = ArrayTypeName.of(primitive), primaryKey = false,
+                        Field(name = "arr",
+                                type = invocation.processingEnv.typeUtils.getArrayType(
+                                        primitive.typeMirror(invocation)),
+                                primaryKey = false,
                                 element = field.element)))
             }.compilesWithoutError()
         }
@@ -131,7 +160,9 @@
         ALL_PRIMITIVES.forEach { primitive ->
             singleEntity("${primitive.box()}[] arr;") { field, invocation ->
                 assertThat(field, `is`(
-                        Field(name = "arr", type = ArrayTypeName.of(primitive.box()),
+                        Field(name = "arr",
+                                type = invocation.processingEnv.typeUtils.getArrayType(
+                                        primitive.box(invocation)),
                                 primaryKey = false,
                                 element = field.element)))
             }.compilesWithoutError()
@@ -149,7 +180,7 @@
                 }
                 """) { field, invocation ->
             assertThat(field, `is`(Field(name = "item",
-                    type = TypeName.INT.box(),
+                    type = TypeKind.INT.box(invocation),
                     primaryKey = false,
                     element = field.element)))
         }.compilesWithoutError()
@@ -162,71 +193,86 @@
                 static class BaseClass<T> {
                     T item;
                 }
-                """) {field, invocation -> }.failsToCompile()
+                """) { field, invocation -> }.failsToCompile()
                 .withErrorContaining(ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS)
     }
 
     @Test
     fun nameVariations() {
-        assertThat(Field(mock(Element::class.java), "x", TypeName.INT, false)
-                .nameWithVariations, `is`(arrayListOf("x")))
-        assertThat(Field(mock(Element::class.java), "x", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("x")))
-        assertThat(Field(mock(Element::class.java), "xAll", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("xAll")))
+        simpleRun {
+            assertThat(Field(mock(Element::class.java), "x", TypeKind.INT.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("x")))
+            assertThat(Field(mock(Element::class.java), "x", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("x")))
+            assertThat(Field(mock(Element::class.java), "xAll",
+                            TypeKind.BOOLEAN.typeMirror(it), false).nameWithVariations,
+                    `is`(arrayListOf("xAll")))
+        }
     }
 
     @Test
     fun nameVariations_is() {
-        assertThat(Field(mock(Element::class.java), "isX", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("isX", "x")))
-        assertThat(Field(mock(Element::class.java), "isX", TypeName.INT, false)
-                .nameWithVariations, `is`(arrayListOf("isX")))
-        assertThat(Field(mock(Element::class.java), "is", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("is")))
-        assertThat(Field(mock(Element::class.java), "isAllItems", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("isAllItems", "allItems")))
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "isX", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("isX", "x")))
+            assertThat(Field(elm, "isX", TypeKind.INT.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("isX")))
+            assertThat(Field(elm, "is", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("is")))
+            assertThat(Field(elm, "isAllItems", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("isAllItems", "allItems")))
+        }
     }
 
     @Test
     fun nameVariations_has() {
-        assertThat(Field(mock(Element::class.java), "hasX", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("hasX", "x")))
-        assertThat(Field(mock(Element::class.java), "hasX", TypeName.INT, false)
-                .nameWithVariations, `is`(arrayListOf("hasX")))
-        assertThat(Field(mock(Element::class.java), "has", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("has")))
-        assertThat(Field(mock(Element::class.java), "hasAllItems", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("hasAllItems", "allItems")))
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "hasX", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("hasX", "x")))
+            assertThat(Field(elm, "hasX", TypeKind.INT.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("hasX")))
+            assertThat(Field(elm, "has", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("has")))
+            assertThat(Field(elm, "hasAllItems", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("hasAllItems", "allItems")))
+        }
     }
 
     @Test
     fun nameVariations_m() {
-        assertThat(Field(mock(Element::class.java), "mall", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("mall")))
-        assertThat(Field(mock(Element::class.java), "mallVars", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("mallVars")))
-        assertThat(Field(mock(Element::class.java), "mAll", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("mAll", "all")))
-        assertThat(Field(mock(Element::class.java), "m", TypeName.INT, false)
-                .nameWithVariations, `is`(arrayListOf("m")))
-        assertThat(Field(mock(Element::class.java), "mallItems", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("mallItems")))
-        assertThat(Field(mock(Element::class.java), "mAllItems", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("mAllItems", "allItems")))
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "mall", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("mall")))
+            assertThat(Field(elm, "mallVars", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("mallVars")))
+            assertThat(Field(elm, "mAll", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("mAll", "all")))
+            assertThat(Field(elm, "m", TypeKind.INT.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("m")))
+            assertThat(Field(elm, "mallItems", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("mallItems")))
+            assertThat(Field(elm, "mAllItems", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("mAllItems", "allItems")))
+        }
     }
 
     @Test
     fun nameVariations_underscore() {
-        assertThat(Field(mock(Element::class.java), "_all", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("_all", "all")))
-        assertThat(Field(mock(Element::class.java), "_", TypeName.INT, false)
-                .nameWithVariations, `is`(arrayListOf("_")))
-        assertThat(Field(mock(Element::class.java), "_allItems", TypeName.BOOLEAN, false)
-                .nameWithVariations, `is`(arrayListOf("_allItems", "allItems")))
+        val elm = mock(Element::class.java)
+        simpleRun {
+            assertThat(Field(elm, "_all", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("_all", "all")))
+            assertThat(Field(elm, "_", TypeKind.INT.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("_")))
+            assertThat(Field(elm, "_allItems", TypeKind.BOOLEAN.typeMirror(it), false)
+                    .nameWithVariations, `is`(arrayListOf("_allItems", "allItems")))
+        }
     }
 
-    fun singleEntity(vararg input: String, handler: (Field, invocation : TestInvocation) -> Unit):
+    fun singleEntity(vararg input: String, handler: (Field, invocation: TestInvocation) -> Unit):
             CompileTester {
         return Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
                 .that(JavaFileObjects.forSourceString("foo.bar.MyEntity",
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
index e11aafb..2b6094c 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
@@ -28,6 +28,7 @@
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourceSubjectFactory
+import com.squareup.javapoet.ArrayTypeName
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
@@ -59,11 +60,12 @@
         singleQueryMethod(
                 """
                 @Query("SELECT * from users")
-                abstract public void foo();
+                abstract public int[] foo();
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
             assertThat(parsedQuery.parameters.size, `is`(0))
-            assertThat(parsedQuery.returnType.typeName(), `is`(TypeName.VOID))
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ArrayTypeName.of(TypeName.INT) as TypeName))
         }.compilesWithoutError()
     }
 
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 21082eb..48f880b 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
@@ -26,6 +26,7 @@
 import org.junit.runners.Parameterized
 import simpleRun
 import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.PrimitiveType
 import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
 
@@ -71,7 +72,7 @@
                               st.bindString(6, inp);
                             }
                             """.trimIndent(),
-                            "out = crs.isNull(9) ? null : crs.getString(9);")
+                            "out = crs.getString(9);")
             )
         }
     }
@@ -80,15 +81,38 @@
     fun bind() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
-                    .findAdapter(input.getTypeMirror(invocation.processingEnv))!!
-            adapter.bindToStmt("st", 6, "inp", scope)
+                    .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
+            adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(scope.generate().trim(), `is`(bindCode))
-            generateCode(invocation)
+            generateCode(invocation, false)
         }.compilesWithoutError()
     }
 
-    private fun generateCode(invocation: TestInvocation) {
-        val typeMirror = input.getTypeMirror(invocation.processingEnv)
+    @Test
+    fun boxedBind() {
+        if (!input.typeKind.isPrimitive) {
+            return // no-op for those
+        }
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+                    .findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
+            adapter.bindToStmt("st", "6", "inp", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    if (inp == null) {
+                      st.bindNull(6);
+                    } else {
+                      $bindCode
+                    }
+                    """.trimIndent()
+            ))
+            generateCode(invocation, true)
+        }.compilesWithoutError()
+    }
+
+    private fun generateCode(invocation: TestInvocation, boxed : Boolean) {
+        val typeMirror = if (boxed) input.getBoxedTypeMirror(invocation.processingEnv)
+                         else input.getTypeMirror(invocation.processingEnv)
         val spec = TypeSpec.classBuilder("OutClass")
                 .addField(FieldSpec.builder(SQLITE_STMT, "st").build())
                 .addField(FieldSpec.builder(CURSOR, "crs").build())
@@ -107,10 +131,32 @@
     fun read() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
-                    .findAdapter(input.getTypeMirror(invocation.processingEnv))!!
-            adapter.readFromCursor("out", "crs", 9, scope)
+                    .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv))!!
+            adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(scope.generate().trim(), `is`(cursorCode))
-            generateCode(invocation)
+            generateCode(invocation, false)
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun readBoxed() {
+        if (!input.typeKind.isPrimitive) {
+            return // no-op for those
+        }
+        simpleRun { invocation ->
+            val adapter = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
+                    .findColumnTypeAdapter(input.getBoxedTypeMirror(invocation.processingEnv))!!
+            adapter.readFromCursor("out", "crs", "9", scope)
+            assertThat(scope.generate().trim(), `is`(
+                    """
+                    if (crs.isNull(9)) {
+                      out = null;
+                    } else {
+                      $cursorCode
+                    }
+                    """.trimIndent()
+            ))
+            generateCode(invocation, true)
         }.compilesWithoutError()
     }
 
@@ -122,5 +168,15 @@
                 processingEnv.elementUtils.getTypeElement(qName).asType()
             }
         }
+
+        fun getBoxedTypeMirror(processingEnv: ProcessingEnvironment) : TypeMirror {
+            return if (typeKind.isPrimitive) {
+                processingEnv.typeUtils
+                        .boxedClass(getTypeMirror(processingEnv) as PrimitiveType)
+                        .asType()
+            } else {
+                getTypeMirror(processingEnv)
+            }
+        }
     }
 }
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 d5f85d8..b62025d 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
@@ -48,7 +48,7 @@
         singleRun { invocation ->
             val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
             val primitiveType = invocation.processingEnv.typeUtils.getPrimitiveType(TypeKind.INT)
-            val adapter = store.findAdapter(primitiveType)
+            val adapter = store.findColumnTypeAdapter(primitiveType)
             assertThat(adapter, notNullValue())
         }.compilesWithoutError()
     }
@@ -59,11 +59,11 @@
             val store = TypeAdapterStore(Context(invocation.roundEnv, invocation.processingEnv))
             val booleanType = invocation.processingEnv.typeUtils
                     .getPrimitiveType(TypeKind.BOOLEAN)
-            val adapter = store.findAdapter(booleanType)
+            val adapter = store.findColumnTypeAdapter(booleanType)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
             val bindScope = CodeGenScope()
-            adapter!!.bindToStmt("stmt", 41, "fooVar", bindScope)
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
             assertThat(bindScope.generate().trim(), `is`("""
                     final int ${tmp(0)};
                     ${tmp(0)} = fooVar ? 1 : 0;
@@ -71,7 +71,7 @@
                     """.trimIndent()))
 
             val cursorScope = CodeGenScope()
-            adapter.readFromCursor("res", "curs", 7, cursorScope)
+            adapter.readFromCursor("res", "curs", "7", cursorScope)
             assertThat(cursorScope.generate().trim(), `is`("""
                     final int ${tmp(0)};
                     ${tmp(0)} = curs.getInt(7);
@@ -87,12 +87,12 @@
                     PointTypeConverter(invocation.processingEnv))
             val pointType = invocation.processingEnv.elementUtils
                     .getTypeElement("foo.bar.Point").asType()
-            val adapter = store.findAdapter(pointType)
+            val adapter = store.findColumnTypeAdapter(pointType)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
 
             val bindScope = CodeGenScope()
-            adapter!!.bindToStmt("stmt", 41, "fooVar", bindScope)
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
             assertThat(bindScope.generate().trim(), `is`("""
                     final int ${tmp(0)};
                     final boolean ${tmp(1)};
@@ -102,7 +102,7 @@
                     """.trimIndent()))
 
             val cursorScope = CodeGenScope()
-            adapter.readFromCursor("res", "curs", 11, cursorScope).toString()
+            adapter.readFromCursor("res", "curs", "11", cursorScope).toString()
             assertThat(cursorScope.generate().trim(), `is`("""
                     final int ${tmp(0)};
                     ${tmp(0)} = curs.getInt(11);
@@ -123,11 +123,11 @@
             val listType = invocation.processingEnv.elementUtils
                     .getTypeElement(java.util.List::class.java.canonicalName)
             val listOfInts = invocation.processingEnv.typeUtils.getDeclaredType(listType, intType)
-            val adapter = store.findAdapter(listOfInts)
+            val adapter = store.findColumnTypeAdapter(listOfInts)
             assertThat(adapter, notNullValue())
 
             val bindScope = CodeGenScope()
-            adapter!!.bindToStmt("stmt", 41, "fooVar", bindScope)
+            adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
             assertThat(bindScope.generate().trim(), `is`("""
                     final java.lang.String ${tmp(0)};
                     ${tmp(0)} = com.android.support.room.util.StringUtil.joinIntoString(fooVar);
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/query/QueryWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/query/QueryWriterTest.kt
index 9231e87..fe6072c 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/solver/query/QueryWriterTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/query/QueryWriterTest.kt
@@ -22,6 +22,7 @@
 import com.android.support.room.processor.QueryMethodProcessor
 import com.android.support.room.solver.CodeGenScope
 import com.android.support.room.testing.TestProcessor
+import com.android.support.room.writer.QueryWriter
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
 import com.google.common.truth.Truth
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt b/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
index fe71691..181d338 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
@@ -1,13 +1,3 @@
-import com.android.support.room.Query
-import com.android.support.room.testing.TestInvocation
-import com.android.support.room.testing.TestProcessor
-import com.google.common.truth.Truth
-import com.google.testing.compile.CompileTester
-import com.google.testing.compile.JavaFileObjects
-import com.google.testing.compile.JavaSourceSubjectFactory
-import org.hamcrest.CoreMatchers
-import org.hamcrest.MatcherAssert
-
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
@@ -23,6 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+import com.android.support.room.Query
+import com.android.support.room.testing.TestInvocation
+import com.android.support.room.testing.TestProcessor
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourceSubjectFactory
+import java.io.File
+import javax.tools.JavaFileObject
+
 fun simpleRun(f: (TestInvocation) -> Unit): CompileTester {
     return Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
             .that(JavaFileObjects.forSourceString("foo.bar.MyClass",
@@ -41,3 +42,8 @@
                     .forAnnotations(Query::class)
                     .build())
 }
+
+fun loadJavaCode(fileName : String, qName : String) : JavaFileObject {
+    val contents = File("src/test/data/$fileName").readText(Charsets.UTF_8)
+    return JavaFileObjects.forSourceString(qName, contents)
+}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/DaoWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/DaoWriterTest.kt
new file mode 100644
index 0000000..e6d60d7
--- /dev/null
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/DaoWriterTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.writer
+
+import com.android.support.room.processor.DaoProcessor
+import com.android.support.room.testing.TestProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import loadJavaCode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class DaoWriterTest {
+    companion object {
+        val USER: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.User",
+                """
+                package foo.bar;
+                import com.android.support.room.*;
+                @Entity
+                public class User {
+                    @PrimaryKey
+                    int uid;
+                }
+                """)
+    }
+    @Test
+    fun complexDao() {
+        singleDao(
+                loadJavaCode("daoWriter/input/ComplexDao.java", "foo.bar.ComplexDao")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("daoWriter/output/ComplexDao.java", "foo.bar.ComplexDao_Impl")
+        )
+    }
+
+    fun singleDao(vararg jfo : JavaFileObject): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfo.toList() + USER)
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(com.android.support.room.Dao::class)
+                        .nextRunHandler { invocation ->
+                            val dao = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            com.android.support.room.Dao::class.java)
+                                    .first()
+                            val parser = DaoProcessor(invocation.context)
+                            val parsedDao = parser.parse(MoreElements.asType(dao))
+                            DaoWriter(parsedDao).write(invocation.processingEnv)
+                            true
+                        }
+                        .build())
+    }
+}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/EntityCursorConverterWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/EntityCursorConverterWriterTest.kt
new file mode 100644
index 0000000..3ce0488
--- /dev/null
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/EntityCursorConverterWriterTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.writer
+
+import com.android.support.room.processor.BaseEntityParserTest
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.squareup.javapoet.JavaFile
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class EntityCursorConverterWriterTest : BaseEntityParserTest() {
+    companion object {
+        val OUT_PREFIX = """
+            package foo.bar;
+            import android.database.Cursor;
+            import com.android.support.room.CursorConverter;
+            import java.lang.Override;
+            import java.lang.String;
+            public class MyEntity_CursorConverter implements CursorConverter<MyEntity> {
+            """.trimIndent()
+        const val OUT_SUFFIX = "}"
+    }
+
+    @Test
+    fun generateSimple() {
+        generateAndMatch(
+                """
+                @PrimaryKey
+                private int id;
+                String name;
+                String lastName;
+                int age;
+                public int getId() { return id; }
+                public void setId(int id) { this.id = id; }
+                """,
+                """
+                @Override
+                public MyEntity convert(Cursor cursor) {
+                  MyEntity _entity = new MyEntity();
+                  int _columnIndex = 0;
+                  for (String _columnName : cursor.getColumnNames()) {
+                    switch(_columnName.hashCode()) {
+                     case ${"id".hashCode()}: {
+                        if ("id".equals(_columnName)) {
+                          final int _tmpId;
+                          _tmpId = cursor.getInt(_columnIndex);
+                          _entity.setId(_tmpId);
+                        }
+                      }
+                      case ${"name".hashCode()}: {
+                        if ("name".equals(_columnName)) {
+                          _entity.name = cursor.getString(_columnIndex);
+                        }
+                      }
+                      case ${"lastName".hashCode()}: {
+                        if ("lastName".equals(_columnName)) {
+                          _entity.lastName = cursor.getString(_columnIndex);
+                        }
+                      }
+                      case ${"age".hashCode()}: {
+                        if ("age".equals(_columnName)) {
+                          _entity.age = cursor.getInt(_columnIndex);
+                        }
+                      }
+                    }
+                    _columnIndex ++;
+                  }
+                  return _entity;
+                }
+                """.trimIndent())
+    }
+
+    fun generateAndMatch(input: String, output : String,
+                         attributes: Map<String, String> = mapOf()) {
+
+        generate(input, attributes)
+                .compilesWithoutError()
+                .and()
+                .generatesSources(JavaFileObjects.forSourceString(
+                        "foo.bar.MyEntity_CursorConverter",
+                        listOf(OUT_PREFIX,output,OUT_SUFFIX).joinToString("\n")))
+    }
+
+    fun generate(input: String, attributes: Map<String, String> = mapOf()) : CompileTester {
+        return singleEntity(input, attributes) { entity, invocation ->
+            EntityCursorConverterWriter(entity).write(invocation.processingEnv)
+        }
+    }
+}
diff --git a/room/db/build.gradle b/room/db/build.gradle
new file mode 100644
index 0000000..ff8d428
--- /dev/null
+++ b/room/db/build.gradle
@@ -0,0 +1,68 @@
+/*
+ * 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"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: rootProject.ext.localMavenRepo)
+            pom.artifactId = "support-db"
+        }
+    }
+}
+
+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")
+    }
+}
diff --git a/room/db/src/main/AndroidManifest.xml b/room/db/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c5738d5
--- /dev/null
+++ b/room/db/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.db">
+</manifest>
diff --git a/room/db/src/main/java/com/android/support/db/SupportDb.java b/room/db/src/main/java/com/android/support/db/SupportDb.java
new file mode 100644
index 0000000..3377504
--- /dev/null
+++ b/room/db/src/main/java/com/android/support/db/SupportDb.java
@@ -0,0 +1,44 @@
+/*
+ * 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.db;
+
+import android.database.Cursor;
+
+/**
+ * A database abstraction which removes the framework dependency and allows swapping underlying
+ * sql versions.
+ */
+public interface SupportDb {
+    /**
+     * Runs the query with the given arguments and returns the result Cursor.
+     *
+     * @param sql The SQL query.
+     * @param args The query arguments.
+     *
+     * @return A Cursor that contains the result.
+     */
+    Cursor query(String sql, String[] args);
+
+    /**
+     * Compiles the given SQL statement.
+     *
+     * @param sql The sql query.
+     *
+     * @return Compiled statement.
+     */
+    SupportSqliteStatement compileStatement(String sql);
+}
diff --git a/room/db/src/main/java/com/android/support/db/SupportSqliteStatement.java b/room/db/src/main/java/com/android/support/db/SupportSqliteStatement.java
new file mode 100644
index 0000000..69c8e5a
--- /dev/null
+++ b/room/db/src/main/java/com/android/support/db/SupportSqliteStatement.java
@@ -0,0 +1,72 @@
+/*
+ * 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.db;
+
+/**
+ * An interface to map the behavior of {@link android.database.sqlite.SQLiteStatement}.
+ */
+@SuppressWarnings("unused")
+public interface SupportSqliteStatement {
+    /**
+     * Bind a NULL value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind null to
+     */
+    void bindNull(int index);
+
+    /**
+     * Bind a long value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *addToBindArgs
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind
+     */
+    void bindLong(int index, long value);
+
+    /**
+     * Bind a double value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind
+     */
+    void bindDouble(int index, double value);
+
+    /**
+     * Bind a String value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind, must not be null
+     */
+    void bindString(int index, String value);
+
+    /**
+     * Bind a byte array value to this statement. The value remains bound until
+     * {@link #clearBindings} is called.
+     *
+     * @param index The 1-based index to the parameter to bind
+     * @param value The value to bind, must not be null
+     */
+    void bindBlob(int index, byte[] value);
+
+    /**
+     * Clears all existing bindings. Unset bindings are treated as NULL.
+     */
+    void clearBindings();
+}
diff --git a/room/gradle/wrapper/gradle-wrapper.jar b/room/gradle/wrapper/gradle-wrapper.jar
index ca78035..d6e2637 100644
--- a/room/gradle/wrapper/gradle-wrapper.jar
+++ b/room/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/room/gradle/wrapper/gradle-wrapper.properties b/room/gradle/wrapper/gradle-wrapper.properties
index 54a595d..1aa4621 100644
--- a/room/gradle/wrapper/gradle-wrapper.properties
+++ b/room/gradle/wrapper/gradle-wrapper.properties
@@ -1,21 +1,6 @@
-#
-# 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.
-#
-#Sat Nov 19 13:16:30 PST 2016
+#Mon Dec 05 17:54:45 PST 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-bin.zip
diff --git a/room/gradlew b/room/gradlew
index 27309d9..4ef3a87 100755
--- a/room/gradlew
+++ b/room/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
 
 ##############################################################################
 ##
@@ -154,11 +154,18 @@
     esac
 fi
 
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+# Escape application args
+for s in "${@}" ; do
+    s=\"$s\"
+    APP_ARGS=$APP_ARGS" "$s
+done
 
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- "$DEFAULT_JVM_OPTS" "$JAVA_OPTS" "$GRADLE_OPTS" "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/room/gradlew.bat b/room/gradlew.bat
index f6d5974..e95643d 100644
--- a/room/gradlew.bat
+++ b/room/gradlew.bat
@@ -49,7 +49,6 @@
 @rem Get command-line arguments, handling Windows variants

 

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

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

 

 :win9xME_args

 @rem Slurp the command line arguments.

@@ -60,11 +59,6 @@
 if "x%~1" == "x" goto execute

 

 set CMD_LINE_ARGS=%*

-goto execute

-

-:4NT_args

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

-set CMD_LINE_ARGS=%$

 

 :execute

 @rem Setup the command line

diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index b5c72f8..76992f1 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -13,8 +13,9 @@
  * 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
@@ -47,10 +48,30 @@
 
 dependencies {
     compile project(":common")
+    compile project(":db")
     compile "com.android.support:support-core-utils:$support_lib_version"
 
     testCompile "junit:junit:$junit_version"
     testCompile "org.mockito:mockito-core:$mockito_version"
 }
 
+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")
+    }
+}
diff --git a/room/runtime/src/main/java/com/android/support/room/CursorConverter.java b/room/runtime/src/main/java/com/android/support/room/CursorConverter.java
new file mode 100644
index 0000000..73e649e
--- /dev/null
+++ b/room/runtime/src/main/java/com/android/support/room/CursorConverter.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;
+
+import android.database.Cursor;
+
+/**
+ * Converts a Cursor into an instance of type T.
+ * @param <T> The type of the output class.
+ *
+ * @hide
+ */
+@SuppressWarnings("unused")
+public interface CursorConverter<T> {
+    /**
+     * Converts the cursor into an instance of type T.
+     * <p>
+     * This method should NOT advance / move the cursor.
+     *
+     * @param cursor The cursor
+     * @return An instance of type T.
+     */
+    T convert(Cursor cursor);
+}
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
new file mode 100644
index 0000000..5971349
--- /dev/null
+++ b/room/runtime/src/main/java/com/android/support/room/Room.java
@@ -0,0 +1,86 @@
+/*
+ * 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 android.support.annotation.Nullable;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class for Room runtime.
+ */
+@SuppressWarnings("unused")
+public class Room {
+    private static Map<Class, CursorConverter> sCache = new HashMap<>();
+
+    /**
+     * Returns the CursorConverter for the given type.
+     *
+     * @param klass The class to convert from Cursor
+     * @param <T> The type parameter of the class
+     * @return A CursorConverter that can create an instance of the given klass from a Cursor.
+     */
+    public static <T> CursorConverter<T> getConverter(Class<T> klass) {
+        CursorConverter existing = sCache.get(klass);
+        if (existing != null) {
+            //noinspection unchecked
+            return existing;
+        }
+        CursorConverter<T> generated = getGeneratedCursorConverter(klass);
+        sCache.put(klass, generated);
+        return generated;
+    }
+
+    @Nullable
+    private static <T> CursorConverter<T> getGeneratedCursorConverter(Class<T> klass) {
+        final String fullPackage = klass.getPackage().getName();
+        final String converterName = getConverterName(klass.getSimpleName());
+        //noinspection TryWithIdenticalCatches
+        try {
+            @SuppressWarnings("unchecked")
+            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();
+        } 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());
+        }
+    }
+
+    private static String getConverterName(String className) {
+        return className.replace(".", "_") + "_CursorConverter";
+    }
+}
diff --git a/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
new file mode 100644
index 0000000..3d43bc5
--- /dev/null
+++ b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.database.Cursor;
+
+import com.android.support.db.SupportDb;
+import com.android.support.db.SupportSqliteStatement;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for all Room databases.
+ */
+@SuppressWarnings("unused")
+public abstract class RoomDatabase implements SupportDb {
+    private ThreadLocal<Map<String, SupportSqliteStatement>> mStatementCache;
+    private final SupportDb mDb;
+
+    public RoomDatabase(SupportDb supportDb) {
+        mDb = supportDb;
+        mStatementCache = new ThreadLocal<>();
+    }
+
+    @Override
+    public Cursor query(String sql, String[] args) {
+        return mDb.query(sql, args);
+    }
+
+    @Override
+    public SupportSqliteStatement compileStatement(String sql) {
+        Map<String, SupportSqliteStatement> cache = mStatementCache.get();
+        if (cache == null) {
+            cache = new HashMap<>();
+            mStatementCache.set(cache);
+        }
+        SupportSqliteStatement cached = cache.get(sql);
+        if (cached == null) {
+            cached = mDb.compileStatement(sql);
+            cache.put(sql, cached);
+        }
+        return cached;
+    }
+}
diff --git a/room/settings.gradle b/room/settings.gradle
index 19185a1..f6dfcf8 100644
--- a/room/settings.gradle
+++ b/room/settings.gradle
@@ -16,3 +16,5 @@
 include 'common'
 include 'runtime'
 include 'compiler'
+include 'db'
+