Preparations for query adapter

This CL makes some changes in preparation for query adapters.

* re-packaged type adapters to be separated from query adapteres
* Changed TypeAdapter store to create chained TypeConverters that
can be merged with 1 ColumnAdapter to create a CompositeColumnAdapter.
In other words, now both CompositeColumnAdapter and
CompositeTypeConverter have just 2 items and they get chained as needed.
This simplifies the readability of our code a lot and has no effect on
the generated code.
* Implemented type converteres from pritivies to String. It is necessary
to create bind arguments for SQL.
* Cleaned up TypeAdapterStore

Test: BasicTypeConvertersTest.kt + existing tests
Bug: 32342709
Change-Id: Ibcd2e05796be2576847d6627b98d17339dfbea65
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 b173508..4c98a59 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
@@ -16,6 +16,17 @@
 
 package com.android.support.room.ext
 
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+import kotlin.reflect.KClass
+
 val L = "\$L"
 val T = "\$T"
-val N = "\$N"
\ No newline at end of file
+val N = "\$N"
+val S = "\$S"
+
+fun KClass<*>.typeName() = ClassName.get(this.java)
+fun KClass<*>.arrayTypeName() = ArrayTypeName.of(typeName())
+fun TypeMirror.typeName() = TypeName.get(this)
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt b/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
index c0fcd70..c8d5a81 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
@@ -16,6 +16,9 @@
 
 package com.android.support.room.parser
 
+import com.android.support.room.parser.SectionType.BIND_VAR
+import com.android.support.room.parser.SectionType.NEWLINE
+import com.android.support.room.parser.SectionType.TEXT
 import org.antlr.v4.runtime.tree.TerminalNode
 
 enum class SectionType {
@@ -62,6 +65,7 @@
         }
         sections
     }
+
     val errors by lazy {
         val hasUnnamed = inputs.any { it.text == "?" }
         inputs.filter {
@@ -71,4 +75,15 @@
         } + (if (hasUnnamed && inputs.size > 1) arrayListOf(ParserErrors.TOO_MANY_UNNAMED_VARIABLES)
         else emptyList<String>()) + syntaxErrors
     }
+
+    val queryWithReplacedBindParams by lazy {
+        sections.joinToString("") {
+            when(it.type) {
+                TEXT -> ""
+                BIND_VAR -> "?"
+                NEWLINE -> "\n"
+                else -> throw IllegalArgumentException("??")
+            }
+        }
+    }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
index 6757309..f684c3d 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
@@ -18,6 +18,7 @@
 
 import com.android.support.room.log.RLog
 import com.android.support.room.preconditions.Checks
+import com.android.support.room.solver.TypeAdapterStore
 import javax.annotation.processing.ProcessingEnvironment
 import javax.annotation.processing.RoundEnvironment
 
@@ -25,4 +26,5 @@
                    val processingEnv: ProcessingEnvironment) {
     val logger = RLog(processingEnv)
     val checker = Checks(logger)
+    val typeAdapterStore = TypeAdapterStore(roundEnv, processingEnv)
 }
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/ParameterParser.kt
index 3ee1abe..641dea2 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/ParameterParser.kt
@@ -17,6 +17,7 @@
 package com.android.support.room.processor
 
 import com.android.support.room.vo.Parameter
+import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
 import com.squareup.javapoet.TypeName
 import javax.lang.model.element.VariableElement
@@ -25,6 +26,12 @@
 class ParameterParser(val context: Context) {
     fun parse(containing: DeclaredType, element: VariableElement): Parameter {
         val asMember = MoreTypes.asMemberOf(context.processingEnv.typeUtils, containing, element)
-        return Parameter(element.simpleName.toString(), TypeName.get(asMember))
+        val typeConverter = context.typeAdapterStore.findTypeConverter(asMember,
+                context.processingEnv.elementUtils.getTypeElement("java.lang.String").asType()
+        )
+        context.checker.check(typeConverter != null, element,
+                ProcessorErrors.CANNOT_CONVERT_QUERY_PARAMETER_TO_STRING)
+
+        return Parameter(element.simpleName.toString(), TypeName.get(asMember), typeConverter)
     }
 }
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 3f1c511..6214463 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
@@ -48,6 +48,9 @@
     val ENTITY_TABLE_NAME_CANNOT_BE_EMPTY = "Entity table name cannot be blank. If you don't want" +
             " to set it, just remove the tableName property."
 
+    val CANNOT_CONVERT_QUERY_PARAMETER_TO_STRING = "QueryMethod parameters should be suitable to" +
+            " be converted into String but I don't know how to convert this."
+
     fun tooManyMatchingGetters(field : Field, methodNames : List<String>) : String {
         return TOO_MANY_MATCHING_GETTERS.format(field, methodNames.joinToString(", "))
     }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
index 9ad3b49..e9f943f 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
@@ -23,12 +23,13 @@
  * Defines a code generation scope where we can provide temporary variables, global variables etc
  */
 class CodeGenScope {
-    private var tmpVarIndex = 0
+    private var tmpVarIndices = mutableMapOf<String, Int>()
     private var builder : CodeBlock.Builder? = null
     companion object {
-        const val TMP_VAR_PREFIX = "_tmp"
+        const val TMP_VAR_DEFAULT_PREFIX = "_tmp"
         @VisibleForTesting
-        fun _tmpVar(index:Int) = "${TMP_VAR_PREFIX}_$index"
+        fun _tmpVar(index:Int) = _tmpVar(TMP_VAR_DEFAULT_PREFIX, index)
+        fun _tmpVar(prefix : String, index:Int) = "${prefix}_$index"
     }
 
     fun builder() : CodeBlock.Builder {
@@ -39,7 +40,17 @@
     }
 
     fun getTmpVar() : String {
-        return _tmpVar(tmpVarIndex ++)
+        return getTmpVar(TMP_VAR_DEFAULT_PREFIX)
+    }
+
+    fun getTmpVar(prefix : String) : String {
+        if (!prefix.startsWith("_")) {
+            throw RuntimeException("tmp variable prefixes should start with _")
+        }
+        val index = tmpVarIndices.getOrElse(prefix) { 0 }
+        val result = _tmpVar(prefix, index)
+        tmpVarIndices.put(prefix, index + 1)
+        return result
     }
 
     fun generate() = builder().build().toString()
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/CompositeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/CompositeAdapter.kt
deleted file mode 100644
index 5d52668..0000000
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/CompositeAdapter.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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
-
-import com.android.support.room.ext.L
-import com.android.support.room.ext.T
-import com.squareup.javapoet.TypeName
-import javax.lang.model.type.TypeMirror
-
-/**
- * A column adapter that uses multiple type adapters to do the conversion.
- */
-class CompositeAdapter(out: TypeMirror, val columnTypeAdapter: ColumnTypeAdapter,
-                       val typeConverters: List<TypeConverter>) : ColumnTypeAdapter(out) {
-    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
-                                scope: CodeGenScope) {
-        val reversed = typeConverters.reversed()
-
-        scope.builder().apply {
-            val tmpCursorValue = scope.getTmpVar()
-            addStatement("final $T $L", columnTypeAdapter.outTypeName, tmpCursorValue)
-            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, index, scope)
-            var tmpInVar = tmpCursorValue
-            var tmpOutVar = scope.getTmpVar()
-            reversed.take(reversed.size - 1).forEach {
-                addStatement("final $T $L", it.fromTypeName, tmpOutVar)
-                it.convertBackward(tmpInVar, tmpOutVar, scope)
-                tmpInVar = tmpOutVar
-                tmpOutVar = scope.getTmpVar()
-            }
-            reversed.last().convertBackward(tmpInVar, outVarName, scope)
-        }
-    }
-
-    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
-                            scope: CodeGenScope) {
-        var tmpInVar = valueVarName
-        var tmpOutVar = scope.getTmpVar()
-        scope.builder().apply {
-            typeConverters.forEach {
-                addStatement("final $T $L", it.toTypeName, tmpOutVar)
-                it.convertForward(tmpInVar, tmpOutVar, scope)
-                tmpInVar = tmpOutVar
-                tmpOutVar = scope.getTmpVar()
-            }
-            columnTypeAdapter.bindToStmt(stmtName, index, tmpInVar, scope)
-        }
-    }
-}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveColumnTypeAdapter.kt
deleted file mode 100644
index 13178ce..0000000
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveColumnTypeAdapter.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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
-
-import com.android.support.room.ext.L
-import javax.annotation.processing.ProcessingEnvironment
-import javax.lang.model.type.PrimitiveType
-import javax.lang.model.type.TypeKind
-
-/**
- * Adapters for all primitives that has direct cursor mappings.
- */
-open class PrimitiveColumnTypeAdapter(out: PrimitiveType,
-                                      val cursorGetter: String,
-                                      val stmtSetter: String,
-                                      cast: Boolean = false) : ColumnTypeAdapter(out) {
-    val cast = if (cast) "(${out.kind.name.toLowerCase()}) " else ""
-
-    companion object {
-        fun createPrimitiveAdapters(processingEnvironment: ProcessingEnvironment)
-                : List<ColumnTypeAdapter> {
-            return listOf(PrimitiveColumnTypeAdapter(
-                    out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.INT),
-                    cursorGetter = "getInt",
-                    stmtSetter = "bindLong"),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.SHORT),
-                            cursorGetter = "getShort",
-                            stmtSetter = "bindLong"),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.BYTE),
-                            cursorGetter = "getShort",
-                            stmtSetter = "bindLong",
-                            cast = true),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.LONG),
-                            cursorGetter = "getLong",
-                            stmtSetter = "bindLong"),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.CHAR),
-                            cursorGetter = "getInt",
-                            stmtSetter = "bindLong",
-                            cast = true),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.FLOAT),
-                            cursorGetter = "getFloat",
-                            stmtSetter = "bindDouble"),
-                    PrimitiveColumnTypeAdapter(
-                            out = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.DOUBLE),
-                            cursorGetter = "getDouble",
-                            stmtSetter = "bindDouble"))
-        }
-    }
-
-    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
-                            scope: CodeGenScope) {
-        scope.builder()
-                .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, index, valueVarName)
-    }
-
-    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
-                                scope: CodeGenScope) {
-        scope.builder()
-                .addStatement("$L = $L$L.$L($L)", outVarName, cast, cursorVarName,
-                        cursorGetter, index)
-    }
-}
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 2e1d5de..6932cc5 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,6 +16,20 @@
 
 package com.android.support.room.solver
 
+import com.android.support.room.solver.types.BoxedBooleanToBoxedIntConverter
+import com.android.support.room.solver.types.BoxedPrimitiveToStringConverter
+import com.android.support.room.solver.types.ColumnTypeAdapter
+import com.android.support.room.solver.types.CompositeAdapter
+import com.android.support.room.solver.types.CompositeTypeConverter
+import com.android.support.room.solver.types.IntListConverter
+import com.android.support.room.solver.types.PrimitiveBooleanToIntConverter
+import com.android.support.room.solver.types.PrimitiveColumnTypeAdapter
+import com.android.support.room.solver.types.PrimitiveToStringConverter
+import com.android.support.room.solver.types.ReverseTypeConverter
+import com.android.support.room.solver.types.StringColumnTypeAdapter
+import com.android.support.room.solver.types.TypeConverter
+import com.google.common.annotations.VisibleForTesting
+import java.util.*
 import javax.annotation.processing.ProcessingEnvironment
 import javax.annotation.processing.RoundEnvironment
 import javax.lang.model.type.TypeMirror
@@ -25,72 +39,104 @@
  * database column.
  */
 class TypeAdapterStore(val roundEnv: RoundEnvironment,
-                       val processingEnvironment: ProcessingEnvironment) {
-    private val columnTypeAdapters = arrayListOf<ColumnTypeAdapter>()
-    private val typeAdapters = arrayListOf<TypeConverter>()
+                       val processingEnvironment: ProcessingEnvironment,
+                       @VisibleForTesting vararg extras : Any) {
+    private val columnTypeAdapters : List<ColumnTypeAdapter>
+    private val typeConverters : List<TypeConverter>
 
     init {
-        PrimitiveColumnTypeAdapter.createPrimitiveAdapters(processingEnvironment).forEach {
-            addColumnAdapter(it)
-        }
-        addColumnAdapter(StringColumnTypeAdapter(processingEnvironment))
-        addTypeAdapter(IntListConverter.create(processingEnvironment))
-        addTypeAdapter(PrimitiveBooleanToIntConverter(processingEnvironment))
-    }
-
-    fun getTypeAdapters(input : TypeMirror, excludes : Set<TypeMirror>) : List<TypeConverter>? {
-        // TODO optimize
-        return if (findColumnAdapters(input).isNotEmpty()) {
-            emptyList()
-        } else {
-            val candidate = findTypeAdapters(input)
-                    .filterNot { excludes.contains(it.to) }
-                    .map {
-                        Pair(it, getTypeAdapters(it.to, excludes + it.from))
-                    }
-                    .filterNot { it.second == null }
-                    .sortedBy { it.second!!.size }
-                    .firstOrNull()
-            return if (candidate == null) {
-                null
-            } else {
-                listOf(candidate.first) + candidate.second!!
+        val adapters = arrayListOf<ColumnTypeAdapter>()
+        val converters = arrayListOf<TypeConverter>()
+        extras.forEach {
+            when(it) {
+                is TypeConverter -> converters.add(it)
+                is ColumnTypeAdapter -> adapters.add(it)
+                else -> throw IllegalArgumentException("unknown extra")
             }
         }
+        fun addTypeConverter(converter: TypeConverter) {
+            converters.add(converter)
+            converters.add(ReverseTypeConverter(converter))
+        }
+
+        fun addColumnAdapter(adapter: ColumnTypeAdapter) {
+            adapters.add(adapter)
+        }
+
+        PrimitiveColumnTypeAdapter
+                .createPrimitiveAdapters(processingEnvironment)
+                .forEach(::addColumnAdapter)
+        addColumnAdapter(StringColumnTypeAdapter(processingEnvironment))
+        addTypeConverter(IntListConverter.create(processingEnvironment))
+        addTypeConverter(PrimitiveBooleanToIntConverter(processingEnvironment))
+        PrimitiveToStringConverter
+                .createPrimitives(processingEnvironment)
+                .forEach(::addTypeConverter)
+        BoxedPrimitiveToStringConverter
+                .createBoxedPrimitives(processingEnvironment)
+                .forEach(::addTypeConverter)
+        addTypeConverter(BoxedBooleanToBoxedIntConverter(processingEnvironment))
+        columnTypeAdapters = adapters
+        typeConverters = converters
     }
 
-    fun getAdapterFor(out : TypeMirror) : ColumnTypeAdapter? {
-        val adapters = findColumnAdapters(out)
+    // type mirrors that be converted into columns w/o an extra converter
+    private val knownColumnTypeMirrors by lazy {
+        columnTypeAdapters.map { it.out }
+    }
+
+    fun findAdapter(out: TypeMirror): ColumnTypeAdapter? {
+        val adapters = getAllColumnAdapters(out)
         if (adapters.isNotEmpty()) {
             return adapters.last()
         }
-        val typeAdapters = getTypeAdapters(out, setOf(out))
-        return if (typeAdapters == null) {
-            null
-        } else {
-            return CompositeAdapter(out, findColumnAdapters(typeAdapters.last().to).last(),
-                    typeAdapters)
+        val converter = findTypeConverter(out, knownColumnTypeMirrors)
+        if (converter != null) {
+            return CompositeAdapter(out, getAllColumnAdapters(converter.to).first(), converter)
         }
+        return null
     }
 
-    fun addTypeAdapter(converter: TypeConverter) {
-        typeAdapters.add(converter)
-        typeAdapters.add(ReverseTypeConverter(converter))
+    fun findTypeConverter(input: TypeMirror, output: TypeMirror): TypeConverter? {
+        return findTypeConverter(input, listOf(output))
     }
 
-    fun addColumnAdapter(adapter: ColumnTypeAdapter) {
-        columnTypeAdapters.add(adapter)
+    private fun findTypeConverter(input: TypeMirror, outputs: List<TypeMirror>): TypeConverter? {
+        val types = processingEnvironment.typeUtils
+        val excludes = arrayListOf<TypeMirror>()
+        excludes.add(input)
+        val queue = LinkedList<TypeConverter>()
+        do {
+            val prev = if (queue.isEmpty()) null else queue.pop()
+            val from = prev?.to ?: input
+            val candidates = getAllTypeConverters(from, excludes)
+            val match = candidates.firstOrNull {
+                outputs.any { output -> types.isSameType(output, it.to) } }
+            if (match != null) {
+                return if (prev == null) match else CompositeTypeConverter(prev, match)
+            }
+            candidates.forEach {
+                excludes.add(it.to)
+                queue.add(
+                        if (prev == null) it else CompositeTypeConverter(prev, it)
+                )
+            }
+        } while (queue.isNotEmpty())
+        return null
     }
 
-    fun findColumnAdapters(input : TypeMirror) : List<ColumnTypeAdapter> {
+    private fun getAllColumnAdapters(input: TypeMirror): List<ColumnTypeAdapter> {
         return columnTypeAdapters.filter {
             processingEnvironment.typeUtils.isSameType(input, it.out)
         }
     }
 
-    fun findTypeAdapters(input : TypeMirror) : List<TypeConverter> {
-        return typeAdapters.filter {
-            processingEnvironment.typeUtils.isSameType(input, it.from)
+    private fun getAllTypeConverters(input: TypeMirror, excludes : List<TypeMirror>):
+            List<TypeConverter> {
+        val types = processingEnvironment.typeUtils
+        return typeConverters.filter { converter ->
+            types.isSameType(input, converter.from) &&
+                    !excludes.any { types.isSameType(it, converter.to) }
         }
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedBooleanToBoxedIntConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
new file mode 100644
index 0000000..ec9702d
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedBooleanToBoxedIntConverter.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.types
+
+import com.android.support.room.ext.L
+import com.android.support.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeKind.BOOLEAN
+import javax.lang.model.type.TypeKind.INT
+
+/**
+ * int to boolean adapter.
+ */
+class BoxedBooleanToBoxedIntConverter(processingEnvironment: ProcessingEnvironment) : TypeConverter(
+        from = processingEnvironment.elementUtils.getTypeElement("java.lang.Boolean").asType(),
+        to = processingEnvironment.elementUtils.getTypeElement("java.lang.Integer").asType()) {
+    override fun convertForward(inputVarName: String, outputVarName: String,
+                                scope: CodeGenScope) {
+        scope.builder().addStatement("$L = $L == null ? null : ($L ? 1 : 0)",
+                outputVarName, inputVarName, inputVarName)
+    }
+
+    override fun convertBackward(inputVarName: String, outputVarName: String,
+                                 scope: CodeGenScope) {
+        scope.builder().addStatement("$L = $L == null ? null : $L != 0", outputVarName,
+                inputVarName, inputVarName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveToStringConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveToStringConverter.kt
new file mode 100644
index 0000000..016565f
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/BoxedPrimitiveToStringConverter.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.ext.T
+import com.android.support.room.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeMirror
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+open class BoxedPrimitiveToStringConverter(val boxed: TypeMirror,
+                                           val parseMethod: String,
+                                           stringType: TypeMirror)
+        : TypeConverter(boxed, stringType) {
+    companion object {
+        fun createBoxedPrimitives(processingEnv: ProcessingEnvironment): List<TypeConverter> {
+            val elmUtils = processingEnv.elementUtils
+            val stringType = processingEnv.elementUtils.getTypeElement("java.lang.String").asType()
+            return listOf(
+                    Pair(java.lang.Integer::class, "parseInt"),
+                    Pair(java.lang.Long::class, "parseLong"),
+                    Pair(java.lang.Short::class, "parseShort"),
+                    Pair(java.lang.Byte::class, "parseByte"),
+                    Pair(java.lang.Float::class, "parseFloat"),
+                    Pair(java.lang.Double::class, "parseDouble")
+            ).map {
+                BoxedPrimitiveToStringConverter(
+                        boxed = elmUtils.getTypeElement(it.first.java.canonicalName).asType(),
+                        parseMethod = it.second,
+                        stringType = stringType
+                )
+            } + object : BoxedPrimitiveToStringConverter(
+                    boxed = elmUtils.getTypeElement("java.lang.Character").asType(),
+                    parseMethod = "",
+                    stringType = stringType
+            ) {
+                override fun convertBackward(inputVarName: String, outputVarName: String,
+                                             scope: CodeGenScope) {
+                    scope.builder().addStatement("$L = $L == null ? null : $L.charAt(0)",
+                            outputVarName, inputVarName, inputVarName)
+                }
+            }
+        }
+    }
+
+    override fun convertForward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L == null ? null : $T.toString($L)", outputVarName,
+                        inputVarName, boxed.typeName(), inputVarName)
+    }
+
+    override fun convertBackward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L == null ? null : $T.$L($L)", outputVarName, inputVarName,
+                        boxed.typeName(), parseMethod, inputVarName)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/ColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
similarity index 92%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/ColumnTypeAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
index 31ab2c9..af48ebd 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/ColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ColumnTypeAdapter.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
 
+import com.android.support.room.solver.CodeGenScope
 import com.squareup.javapoet.TypeName
 import javax.lang.model.type.TypeMirror
 
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
new file mode 100644
index 0000000..ccdd23f
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeAdapter.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.ext.T
+import com.android.support.room.solver.CodeGenScope
+import com.squareup.javapoet.TypeName
+import javax.lang.model.type.TypeMirror
+
+/**
+ * A column adapter that uses a type converter to do the conversion. The type converter may be
+ * a composite one.
+ */
+class CompositeAdapter(out: TypeMirror, val columnTypeAdapter: ColumnTypeAdapter,
+                       val typeConverter : TypeConverter) : ColumnTypeAdapter(out) {
+    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
+                                scope: CodeGenScope) {
+        scope.builder().apply {
+            val tmpCursorValue = scope.getTmpVar()
+            addStatement("final $T $L", columnTypeAdapter.outTypeName, tmpCursorValue)
+            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, index, scope)
+            typeConverter.convertBackward(tmpCursorValue, outVarName, scope)
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, index: Int, 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)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeTypeConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeTypeConverter.kt
new file mode 100644
index 0000000..c2658dc
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/CompositeTypeConverter.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.types
+
+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
+
+/**
+ * combines 2 type converters
+ */
+class CompositeTypeConverter(val conv1 : TypeConverter, val conv2 : TypeConverter) : TypeConverter(
+        conv1.from, conv2.to) {
+    override fun convertForward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val tmp = scope.getTmpVar()
+            addStatement("final $T $L", conv1.to.typeName(), tmp)
+            conv1.convertForward(inputVarName, tmp, scope)
+            conv2.convertForward(tmp, outputVarName, scope)
+        }
+
+    }
+
+    override fun convertBackward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder().apply {
+            val tmp = scope.getTmpVar()
+            addStatement("final $T $L", conv2.from.typeName(), tmp)
+            conv2.convertBackward(inputVarName, tmp, scope)
+            conv1.convertBackward(tmp, outputVarName, scope)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/IntListConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/IntListConverter.kt
similarity index 95%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/IntListConverter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/IntListConverter.kt
index 093661e..f48d041 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/IntListConverter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/IntListConverter.kt
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
 
 import com.android.support.room.ext.L
 import com.android.support.room.ext.N
 import com.android.support.room.ext.T
+import com.android.support.room.solver.CodeGenScope
 import com.squareup.javapoet.ClassName
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.type.TypeMirror
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveBooleanToIntConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveBooleanToIntConverter.kt
similarity index 83%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveBooleanToIntConverter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveBooleanToIntConverter.kt
index f4f312b..ea8ace8 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/PrimitiveBooleanToIntConverter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveBooleanToIntConverter.kt
@@ -14,18 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
 
 import com.android.support.room.ext.L
+import com.android.support.room.solver.CodeGenScope
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeKind.BOOLEAN
+import javax.lang.model.type.TypeKind.INT
 
 /**
  * int to boolean adapter.
  */
 class PrimitiveBooleanToIntConverter(processingEnvironment: ProcessingEnvironment) : TypeConverter(
-        from = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.BOOLEAN),
-        to = processingEnvironment.typeUtils.getPrimitiveType(TypeKind.INT)) {
+        from = processingEnvironment.typeUtils.getPrimitiveType(BOOLEAN),
+        to = processingEnvironment.typeUtils.getPrimitiveType(INT)) {
     override fun convertForward(inputVarName: String, outputVarName: String,
                                 scope: CodeGenScope) {
         scope.builder().addStatement("$L = $L ? 1 : 0", outputVarName, inputVarName)
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
new file mode 100644
index 0000000..96aa080
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveColumnTypeAdapter.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.ext.typeName
+import com.android.support.room.solver.CodeGenScope
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeKind.BYTE
+import javax.lang.model.type.TypeKind.CHAR
+import javax.lang.model.type.TypeKind.DOUBLE
+import javax.lang.model.type.TypeKind.FLOAT
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.SHORT
+
+/**
+ * Adapters for all primitives that has direct cursor mappings.
+ */
+open class PrimitiveColumnTypeAdapter(out: PrimitiveType,
+                                      val cursorGetter: String,
+                                      val stmtSetter: String) : ColumnTypeAdapter(out) {
+    val cast =  if (cursorGetter == "get${out.typeName().toString().capitalize()}")
+                    ""
+                else
+                    "(${out.typeName()}) "
+
+    companion object {
+        fun createPrimitiveAdapters(processingEnvironment: ProcessingEnvironment)
+                : List<ColumnTypeAdapter> {
+            return listOf(
+                    Triple(INT, "getInt", "bindLong"),
+                    Triple(SHORT, "getShort", "bindLong"),
+                    Triple(BYTE, "getShort", "bindLong"),
+                    Triple(LONG, "getLong", "bindLong"),
+                    Triple(CHAR, "getInt", "bindLong"),
+                    Triple(FLOAT, "getFloat", "bindDouble"),
+                    Triple(DOUBLE, "getDouble", "bindDouble")
+            ).map {
+                PrimitiveColumnTypeAdapter(
+                        out = processingEnvironment.typeUtils.getPrimitiveType(it.first),
+                        cursorGetter = it.second,
+                        stmtSetter = it.third
+                )
+            }
+        }
+    }
+
+    override fun bindToStmt(stmtName: String, index: Int, valueVarName: String,
+                            scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, index, valueVarName)
+    }
+
+    override fun readFromCursor(outVarName: String, cursorVarName: String, index: Int,
+                                scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $L$L.$L($L)", outVarName, cast, cursorVarName,
+                        cursorGetter, index)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveToStringConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveToStringConverter.kt
new file mode 100644
index 0000000..b849a0e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/PrimitiveToStringConverter.kt
@@ -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.solver.types
+
+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 javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind.BYTE
+import javax.lang.model.type.TypeKind.CHAR
+import javax.lang.model.type.TypeKind.DOUBLE
+import javax.lang.model.type.TypeKind.FLOAT
+import javax.lang.model.type.TypeKind.INT
+import javax.lang.model.type.TypeKind.LONG
+import javax.lang.model.type.TypeKind.SHORT
+import javax.lang.model.type.TypeMirror
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+open class PrimitiveToStringConverter(val boxed : TypeMirror,
+                                      val parseMethod : String?,
+                                      val primitiveType : PrimitiveType,
+                                      val stringType : TypeMirror) :
+        TypeConverter(primitiveType, stringType) {
+    companion object {
+        fun createPrimitives(processingEnv : ProcessingEnvironment) : List<TypeConverter> {
+            val elmUtils = processingEnv.elementUtils
+            val typeUtils = processingEnv.typeUtils
+            val stringType = processingEnv.elementUtils.getTypeElement("java.lang.String").asType()
+
+            return listOf(
+                Triple(java.lang.Integer::class, "parseInt", INT),
+                Triple(java.lang.Long::class, "parseLong", LONG),
+                Triple(java.lang.Short::class, "parseShort", SHORT),
+                Triple(java.lang.Byte::class, "parseByte", BYTE),
+                Triple(java.lang.Float::class, "parseFloat", FLOAT),
+                Triple(java.lang.Double::class, "parseDouble", DOUBLE)
+            ).map {
+                PrimitiveToStringConverter(
+                        boxed = elmUtils.getTypeElement(it.first.java.canonicalName).asType(),
+                        parseMethod = it.second,
+                        primitiveType = typeUtils.getPrimitiveType(it.third),
+                        stringType = stringType
+                )
+            } + object : PrimitiveToStringConverter(
+                            boxed = elmUtils.getTypeElement("java.lang.Character").asType(),
+                            parseMethod = null,
+                            primitiveType = typeUtils.getPrimitiveType(CHAR),
+                            stringType = stringType
+                    ) {
+                        override fun convertBackward(inputVarName: String, outputVarName: String,
+                                                     scope: CodeGenScope) {
+                            scope.builder().addStatement("$L = $L.charAt(0)", outputVarName,
+                                    inputVarName)
+                        }
+                    }
+
+        }
+    }
+
+    override fun convertForward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $T.toString($L)", outputVarName, boxed.typeName(), inputVarName)
+    }
+
+    override fun convertBackward(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
+        scope.builder()
+                .addStatement("$L = $T.$L($L)", outputVarName, boxed.typeName(), parseMethod,
+                        inputVarName)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/ReverseTypeConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ReverseTypeConverter.kt
similarity index 92%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/ReverseTypeConverter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/ReverseTypeConverter.kt
index 919a374..c96c7cb 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/ReverseTypeConverter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/ReverseTypeConverter.kt
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
+
+import com.android.support.room.solver.CodeGenScope
 
 /**
  * Takes a type adapter and reverses it.
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/StringColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
similarity index 94%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/StringColumnTypeAdapter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
index a6ee779..8ff150b 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/StringColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/StringColumnTypeAdapter.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
 
 import com.android.support.room.ext.L
+import com.android.support.room.solver.CodeGenScope
 import javax.annotation.processing.ProcessingEnvironment
 
 class StringColumnTypeAdapter(processingEnvironment: ProcessingEnvironment)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeConverter.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/TypeConverter.kt
similarity index 92%
rename from room/compiler/src/main/kotlin/com/android/support/room/solver/TypeConverter.kt
rename to room/compiler/src/main/kotlin/com/android/support/room/solver/types/TypeConverter.kt
index 68d5f79..e4300d1 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/TypeConverter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/types/TypeConverter.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.support.room.solver
+package com.android.support.room.solver.types
 
+import com.android.support.room.solver.CodeGenScope
 import com.squareup.javapoet.TypeName
 import javax.lang.model.type.TypeMirror
 
@@ -30,4 +31,4 @@
 
     abstract fun convertBackward(inputVarName: String, outputVarName: String,
                                  scope: CodeGenScope)
-}
\ No newline at end of file
+}
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/Parameter.kt
index f9c1a70..cf63425 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/Parameter.kt
@@ -16,9 +16,15 @@
 
 package com.android.support.room.vo
 
+import com.android.support.room.solver.types.TypeConverter
 import com.squareup.javapoet.TypeName
 
 /**
  * Holds the parameter for a {@link QueryMethod}.
  */
-data class Parameter(val name: String, val type: TypeName)
+data class Parameter(val name: String, val type: TypeName,
+                     /**
+                      * Used when converting this parameter into the String[] of the query
+                      * parameters
+                      */
+                     val stringTypeConverter : TypeConverter?)
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 9ecaabe..13d2e1d 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
@@ -18,11 +18,11 @@
 
 import com.android.support.room.parser.ParsedQuery
 import com.squareup.javapoet.TypeName
-import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
 
 /**
  * A class that holds information about a QueryMethod.
  * It is self sufficient and must have all generics etc resolved once created.
  */
-data class QueryMethod(val element : Element, val query: ParsedQuery, val name: String,
+data class QueryMethod(val element : ExecutableElement, val query: ParsedQuery, val name: String,
                        val returnType: TypeName, val parameters: List<Parameter>)
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 3c9a937..2ea3649 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
@@ -79,7 +79,7 @@
     fun bind() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
-                    .getAdapterFor(input.getTypeMirror(invocation.processingEnv))!!
+                    .findAdapter(input.getTypeMirror(invocation.processingEnv))!!
             adapter.bindToStmt("st", 6, "inp", scope)
             assertThat(scope.generate().trim(), `is`(bindCode))
             generateCode(invocation)
@@ -106,7 +106,7 @@
     fun read() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
-                    .getAdapterFor(input.getTypeMirror(invocation.processingEnv))!!
+                    .findAdapter(input.getTypeMirror(invocation.processingEnv))!!
             adapter.readFromCursor("out", "crs", 9, scope)
             assertThat(scope.generate().trim(), `is`(cursorCode))
             generateCode(invocation)
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
new file mode 100644
index 0000000..662fe4b
--- /dev/null
+++ b/room/compiler/src/test/kotlin/com/android/support/room/solver/BasicTypeConvertersTest.kt
@@ -0,0 +1,165 @@
+/*
+ * 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
+
+import com.android.support.room.testing.TestInvocation
+import com.squareup.javapoet.*
+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 simpleRun
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeKind.DECLARED
+import javax.lang.model.type.TypeMirror
+
+@RunWith(Parameterized::class)
+class BasicTypeConvertersTest(val input: Input, val forwardCode: String,
+                                  val backwardCode: String) {
+    val scope = CodeGenScope()
+
+    companion object {
+        @Parameterized.Parameters(name = "kind:{0},bind:_{1},cursor:_{2}")
+        @JvmStatic
+        fun params(): List<Array<Any>> {
+            return listOf(
+                    arrayOf(Input(TypeKind.BOOLEAN),
+                            """
+                            final int _tmp_0;
+                            _tmp_0 = inp ? 1 : 0;
+                            out = java.lang.Integer.toString(_tmp_0);
+                            """.trimIndent(),
+                            """
+                            final int _tmp_0;
+                            _tmp_0 = java.lang.Integer.parseInt(inp);
+                            out = _tmp_0 != 0;
+                            """.trimIndent()),
+                    arrayOf(Input(TypeKind.INT),
+                            "out = java.lang.Integer.toString(inp);",
+                            "out = java.lang.Integer.parseInt(inp);"),
+                    arrayOf(Input(TypeKind.BYTE),
+                            "out = java.lang.Byte.toString(inp);",
+                            "out = java.lang.Byte.parseByte(inp);"),
+                    arrayOf(Input(TypeKind.SHORT),
+                            "out = java.lang.Short.toString(inp);",
+                            "out = java.lang.Short.parseShort(inp);"),
+                    arrayOf(Input(TypeKind.LONG),
+                            "out = java.lang.Long.toString(inp);",
+                            "out = java.lang.Long.parseLong(inp);"),
+                    arrayOf(Input(TypeKind.CHAR),
+                            "out = java.lang.Character.toString(inp);",
+                            "out = inp.charAt(0);"),
+                    arrayOf(Input(TypeKind.FLOAT),
+                            "out = java.lang.Float.toString(inp);",
+                            "out = java.lang.Float.parseFloat(inp);"),
+                    arrayOf(Input(TypeKind.DOUBLE),
+                            "out = java.lang.Double.toString(inp);",
+                            "out = java.lang.Double.parseDouble(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Integer"),
+                            "out = inp == null ? null : java.lang.Integer.toString(inp);",
+                            "out = inp == null ? null : java.lang.Integer.parseInt(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Byte"),
+                            "out = inp == null ? null : java.lang.Byte.toString(inp);",
+                            "out = inp == null ? null : java.lang.Byte.parseByte(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Short"),
+                            "out = inp == null ? null : java.lang.Short.toString(inp);",
+                            "out = inp == null ? null : java.lang.Short.parseShort(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Long"),
+                            "out = inp == null ? null : java.lang.Long.toString(inp);",
+                            "out = inp == null ? null : java.lang.Long.parseLong(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Float"),
+                            "out = inp == null ? null : java.lang.Float.toString(inp);",
+                            "out = inp == null ? null : java.lang.Float.parseFloat(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Double"),
+                            "out = inp == null ? null : java.lang.Double.toString(inp);",
+                            "out = inp == null ? null : java.lang.Double.parseDouble(inp);"),
+                    arrayOf(Input(DECLARED, "java.lang.Character"),
+                            "out = inp == null ? null : java.lang.Character.toString(inp);",
+                            "out = inp == null ? null : inp.charAt(0);"),
+                    arrayOf(Input(DECLARED, "java.lang.Boolean"),
+                            """
+                            final java.lang.Integer _tmp_0;
+                            _tmp_0 = inp == null ? null : (inp ? 1 : 0);
+                            out = _tmp_0 == null ? null : java.lang.Integer.toString(_tmp_0);
+                            """.trimIndent(),
+                            """
+                            final java.lang.Integer _tmp_0;
+                            _tmp_0 = inp == null ? null : java.lang.Integer.parseInt(inp);
+                            out = _tmp_0 == null ? null : _tmp_0 != 0;
+                            """.trimIndent()))
+        }
+    }
+
+    @Test
+    fun forward() {
+        simpleRun { invocation ->
+            val stringTypeMirror = invocation
+                    .processingEnv.elementUtils
+                    .getTypeElement("java.lang.String").asType()
+            val converter = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
+                    .findTypeConverter(input.getTypeMirror(invocation.processingEnv),
+                            stringTypeMirror)!!
+            converter.convertForward("inp", "out", scope)
+            assertThat(scope.generate().trim(), `is`(forwardCode))
+            generateCode(invocation, input.getTypeMirror(invocation.processingEnv),
+                    stringTypeMirror)
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun backward() {
+        simpleRun { invocation ->
+            val stringTypeMirror = invocation
+                    .processingEnv.elementUtils
+                    .getTypeElement("java.lang.String").asType()
+            val converter = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
+                    .findTypeConverter(input.getTypeMirror(invocation.processingEnv),
+                            stringTypeMirror)!!
+            converter.convertBackward("inp", "out", scope)
+            assertThat(scope.generate().trim(), `is`(backwardCode))
+            generateCode(invocation, stringTypeMirror,
+                    input.getTypeMirror(invocation.processingEnv))
+        }.compilesWithoutError()
+    }
+
+    private fun generateCode(invocation: TestInvocation, inpType : TypeMirror,
+                             outType : TypeMirror) {
+        input.getTypeMirror(invocation.processingEnv)
+        val spec = TypeSpec.classBuilder("OutClass")
+                .addField(FieldSpec.builder(TypeName.get(outType), "out").build())
+                .addField(FieldSpec.builder(TypeName.get(inpType), "inp").build())
+                .addMethod(
+                        MethodSpec.methodBuilder("foo")
+                                .addCode(scope.builder().build())
+                                .build()
+                )
+                .build()
+        JavaFile.builder("foo.bar", spec).build().writeTo(invocation.processingEnv.filer)
+    }
+
+    data class Input(val typeKind: TypeKind, val qName : String? = null) {
+        fun getTypeMirror(processingEnv: ProcessingEnvironment) : TypeMirror {
+            return if (typeKind.isPrimitive) {
+                processingEnv.typeUtils.getPrimitiveType(typeKind)
+            } else {
+                processingEnv.elementUtils.getTypeElement(qName).asType()
+            }
+        }
+    }
+}
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 25fce2f..3e94524 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
@@ -19,6 +19,8 @@
 import com.android.support.room.Entity
 import com.android.support.room.ext.L
 import com.android.support.room.ext.T
+import com.android.support.room.solver.types.CompositeAdapter
+import com.android.support.room.solver.types.TypeConverter
 import com.android.support.room.testing.TestInvocation
 import com.android.support.room.testing.TestProcessor
 import com.google.common.truth.Truth
@@ -45,7 +47,7 @@
         singleRun { invocation ->
             val store = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
             val primitiveType = invocation.processingEnv.typeUtils.getPrimitiveType(TypeKind.INT)
-            val adapter = store.getAdapterFor(primitiveType)
+            val adapter = store.findAdapter(primitiveType)
             assertThat(adapter, notNullValue())
         }.compilesWithoutError()
     }
@@ -56,7 +58,7 @@
             val store = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
             val booleanType = invocation.processingEnv.typeUtils
                     .getPrimitiveType(TypeKind.BOOLEAN)
-            val adapter = store.getAdapterFor(booleanType)
+            val adapter = store.findAdapter(booleanType)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
             val bindScope = CodeGenScope()
@@ -80,22 +82,22 @@
     @Test
     fun testVia2TypeAdapters() {
         singleRun { invocation ->
-            val store = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv)
-            store.addTypeAdapter(PointTypeConverter(invocation.processingEnv))
+            val store = TypeAdapterStore(invocation.roundEnv, invocation.processingEnv,
+                    PointTypeConverter(invocation.processingEnv))
             val pointType = invocation.processingEnv.elementUtils
                     .getTypeElement("foo.bar.Point").asType()
-            val adapter = store.getAdapterFor(pointType)
+            val adapter = store.findAdapter(pointType)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
 
             val bindScope = CodeGenScope()
             adapter!!.bindToStmt("stmt", 41, "fooVar", bindScope)
             assertThat(bindScope.generate().trim(), `is`("""
-                    final boolean ${tmp(0)};
-                    ${tmp(0)} = foo.bar.Point.toBoolean(fooVar);
-                    final int ${tmp(1)};
-                    ${tmp(1)} = ${tmp(0)} ? 1 : 0;
-                    stmt.bindLong(41, ${tmp(1)});
+                    final int ${tmp(0)};
+                    final boolean ${tmp(1)};
+                    ${tmp(1)} = foo.bar.Point.toBoolean(fooVar);
+                    ${tmp(0)} = ${tmp(1)} ? 1 : 0;
+                    stmt.bindLong(41, ${tmp(0)});
                     """.trimIndent()))
 
             val cursorScope = CodeGenScope()
@@ -120,7 +122,7 @@
             val listType = invocation.processingEnv.elementUtils
                     .getTypeElement(java.util.List::class.java.canonicalName)
             val listOfInts = invocation.processingEnv.typeUtils.getDeclaredType(listType, intType)
-            val adapter = store.getAdapterFor(listOfInts)
+            val adapter = store.findAdapter(listOfInts)
             assertThat(adapter, notNullValue())
 
             val bindScope = CodeGenScope()