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'
+