From 9a2e76312eb132b2b5720425611dda7fcf520144 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Fri, 14 Apr 2017 10:52:06 +1000 Subject: [PATCH 01/37] [processor] simplify a lot of calls via extension methods and KPoet. --- .../android/dbflow/processor/Handlers.kt | 36 +-- .../dbflow/processor/ProcessorManager.kt | 33 +-- .../dbflow/processor/ProcessorUtils.kt | 159 +++++++------ .../processor/definition/BaseDefinition.kt | 32 ++- .../processor/definition/ContentProvider.kt | 214 +++++++++--------- .../definition/DatabaseDefinition.kt | 20 +- .../definition/DatabaseHolderDefinition.kt | 10 +- .../definition/IndexGroupsDefinition.kt | 37 ++- .../definition/InternalAdapterHelper.kt | 46 ++-- .../definition/ManyToManyDefinition.kt | 24 +- .../definition/MigrationDefinition.kt | 16 +- .../definition/ModelViewDefinition.kt | 30 ++- .../processor/definition/NotifyDefinition.kt | 14 +- .../definition/OneToManyDefinition.kt | 19 +- .../definition/QueryModelDefinition.kt | 17 +- .../processor/definition/TableDefinition.kt | 133 ++++++----- .../definition/TableEndpointDefinition.kt | 12 +- .../definition/TypeConverterDefinition.kt | 4 +- .../definition/column/ColumnDefinition.kt | 99 ++++---- .../column/ForeignKeyColumnDefinition.kt | 91 ++++---- .../processor/utils/ElementExtensions.kt | 4 +- .../dbflow/processor/utils/ElementUtility.kt | 2 +- 22 files changed, 501 insertions(+), 551 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt index 54965487c..158205827 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt @@ -1,31 +1,13 @@ package com.raizlabs.android.dbflow.processor import com.google.common.collect.Sets -import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.ManyToMany -import com.raizlabs.android.dbflow.annotation.Migration -import com.raizlabs.android.dbflow.annotation.ModelView -import com.raizlabs.android.dbflow.annotation.MultipleManyToMany -import com.raizlabs.android.dbflow.annotation.QueryModel -import com.raizlabs.android.dbflow.annotation.Table +import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.annotation.TypeConverter import com.raizlabs.android.dbflow.annotation.provider.ContentProvider import com.raizlabs.android.dbflow.annotation.provider.TableEndpoint -import com.raizlabs.android.dbflow.converter.BigDecimalConverter -import com.raizlabs.android.dbflow.converter.BooleanConverter -import com.raizlabs.android.dbflow.converter.CalendarConverter -import com.raizlabs.android.dbflow.converter.DateConverter -import com.raizlabs.android.dbflow.converter.SqlDateConverter -import com.raizlabs.android.dbflow.converter.UUIDConverter -import com.raizlabs.android.dbflow.processor.definition.ContentProviderDefinition -import com.raizlabs.android.dbflow.processor.definition.DatabaseDefinition -import com.raizlabs.android.dbflow.processor.definition.ManyToManyDefinition -import com.raizlabs.android.dbflow.processor.definition.MigrationDefinition -import com.raizlabs.android.dbflow.processor.definition.ModelViewDefinition -import com.raizlabs.android.dbflow.processor.definition.QueryModelDefinition -import com.raizlabs.android.dbflow.processor.definition.TableDefinition -import com.raizlabs.android.dbflow.processor.definition.TableEndpointDefinition -import com.raizlabs.android.dbflow.processor.definition.TypeConverterDefinition +import com.raizlabs.android.dbflow.converter.* +import com.raizlabs.android.dbflow.processor.definition.* +import com.raizlabs.android.dbflow.processor.utils.annotation import javax.annotation.processing.RoundEnvironment import javax.lang.model.element.Element import javax.lang.model.element.Modifier @@ -125,14 +107,14 @@ class TableHandler : BaseContainerHandler() { val tableDefinition = TableDefinition(processorManager, element) processorManager.addTableDefinition(tableDefinition) - if (element.getAnnotation(ManyToMany::class.java) != null) { + if (element.annotation() != null) { val manyToManyDefinition = ManyToManyDefinition(element, processorManager) processorManager.addManyToManyDefinition(manyToManyDefinition) } - if (element.getAnnotation(MultipleManyToMany::class.java) != null) { - val multipleManyToMany = element.getAnnotation(MultipleManyToMany::class.java) - multipleManyToMany.value.forEach { + if (element.annotation() != null) { + val multipleManyToMany = element.annotation() + multipleManyToMany?.value?.forEach { processorManager.addManyToManyDefinition(ManyToManyDefinition(element, processorManager, it)) } } @@ -154,7 +136,7 @@ class TypeConverterHandler : BaseContainerHandler() { override fun onProcessElement(processorManager: ProcessorManager, element: Element) { if (element is TypeElement) { - val className = ProcessorUtils.fromTypeMirror(element.asType(), processorManager) + val className = fromTypeMirror(element.asType(), processorManager) val converterDefinition = className?.let { TypeConverterDefinition(it, element.asType(), processorManager, element) } converterDefinition?.let { if (VALIDATOR.validate(processorManager, converterDefinition)) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorManager.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorManager.kt index 9606f30f0..cfe84d855 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorManager.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorManager.kt @@ -29,23 +29,21 @@ class ProcessorManager internal constructor(val processingEnvironment: Processin lateinit var manager: ProcessorManager } - private val uniqueDatabases = Lists.newArrayList() - private val modelToDatabaseMap = Maps.newHashMap() - val typeConverters = Maps.newLinkedHashMap() - private val migrations = Maps.newHashMap>>() + private val uniqueDatabases = arrayListOf() + private val modelToDatabaseMap = hashMapOf() + val typeConverters = linkedMapOf() + private val migrations = hashMapOf>>() - private val databaseDefinitionMap = Maps.newHashMap() - private val handlers = ArrayList>() - private val providerMap = Maps.newHashMap() + private val databaseDefinitionMap = hashMapOf() + private val handlers = mutableSetOf>() + private val providerMap = hashMapOf() init { manager = this } fun addHandlers(vararg containerHandlers: BaseContainerHandler<*>) { - containerHandlers - .filterNot { handlers.contains(it) } - .forEach { handlers.add(it) } + containerHandlers.forEach { handlers.add(it) } } val messager: Messager = processingEnvironment.messager @@ -179,14 +177,7 @@ class ProcessorManager internal constructor(val processingEnvironment: Processin } } - fun getMigrationsForDatabase(databaseName: TypeName): Map> { - val migrationDefinitions = migrations[databaseName] - if (migrationDefinitions != null) { - return migrationDefinitions - } else { - return Maps.newHashMap>() - } - } + fun getMigrationsForDatabase(databaseName: TypeName) = migrations[databaseName] ?: hashMapOf>() fun addContentProviderDefinition(contentProviderDefinition: ContentProviderDefinition) { contentProviderDefinition.elementTypeName?.let { @@ -210,11 +201,9 @@ class ProcessorManager internal constructor(val processingEnvironment: Processin messager.printMessage(Diagnostic.Kind.ERROR, String.format("*==========*${callingClass ?: ""} :" + error?.trim() + "*==========*", *args)) var stackTraceElements = Thread.currentThread().stackTrace if (stackTraceElements.size > 8) { - stackTraceElements = Arrays.copyOf(stackTraceElements, 8) - } - for (stackTrace in stackTraceElements) { - messager.printMessage(Diagnostic.Kind.ERROR, stackTrace.toString()) + stackTraceElements = stackTraceElements.copyOf(8) } + stackTraceElements.forEach { messager.printMessage(Diagnostic.Kind.ERROR, it.toString()) } } fun logError(error: String?, vararg args: Any?) = logError(callingClass = null, error = error, args = args) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt index 44a2fe433..f47beada2 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt @@ -13,102 +13,101 @@ import javax.lang.model.type.TypeMirror import javax.tools.Diagnostic /** - * Description: Provides handy methods for processing + * Whether the specified element implements the [ClassName] */ -object ProcessorUtils { +fun TypeElement?.implementsClass(processingEnvironment: ProcessingEnvironment + = ProcessorManager.manager.processingEnvironment, className: ClassName) + = implementsClass(processingEnvironment, className.toString()) - /** - * Whether the specified element is assignable to the fqTn parameter +/** + * Whether the specified element is assignable to the fqTn parameter - * @param processingEnvironment The environment this runs in - * * - * @param fqTn THe fully qualified type name of the element we want to check - * * - * @param element The element to check that implements - * * - * @return true if element implements the fqTn - */ - fun implementsClass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { - val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) - if (typeElement == null) { - processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, - "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") + * @param processingEnvironment The environment this runs in + * * + * @param fqTn THe fully qualified type name of the element we want to check + * * + * @param element The element to check that implements + * * + * @return true if element implements the fqTn + */ +fun TypeElement?.implementsClass(processingEnvironment: ProcessingEnvironment, fqTn: String): Boolean { + val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) + if (typeElement == null) { + processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, + "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") + return false + } else { + val classMirror: TypeMirror? = typeElement.asType().erasure() + if (classMirror == null || this?.asType() == null) { return false - } else { - val classMirror: TypeMirror? = typeElement.asType().erasure() - if (classMirror == null || element?.asType() == null) { - return false - } - val elementType = element.asType() - return elementType != null && (processingEnvironment.typeUtils.isAssignable(elementType, classMirror) || elementType == classMirror) } + val elementType = this.asType() + return elementType != null && (processingEnvironment.typeUtils.isAssignable(elementType, classMirror) || elementType == classMirror) } +} - /** - * Whether the specified element is assignable to the fqTn parameter +/** + * Whether the specified element is assignable to the fqTn parameter - * @param processingEnvironment The environment this runs in - * * - * @param fqTn THe fully qualified type name of the element we want to check - * * - * @param element The element to check that implements - * * - * @return true if element implements the fqTn - */ - fun isSubclass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { - val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) - if (typeElement == null) { - processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") - return false - } else { - val classMirror = typeElement.asType() - return classMirror != null && element != null && element.asType() != null && processingEnvironment.typeUtils.isSubtype(element.asType(), classMirror) - } + * @param processingEnvironment The environment this runs in + * * + * @param fqTn THe fully qualified type name of the element we want to check + * * + * @param element The element to check that implements + * * + * @return true if element implements the fqTn + */ +fun isSubclass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { + val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) + if (typeElement == null) { + processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") + return false + } else { + val classMirror = typeElement.asType() + return classMirror != null && element != null && element.asType() != null && processingEnvironment.typeUtils.isSubtype(element.asType(), classMirror) } +} - fun fromTypeMirror(typeMirror: TypeMirror, processorManager: ProcessorManager): ClassName? { - var className: ClassName? = null - val element = getTypeElement(typeMirror) - if (element != null) { - className = ClassName.get(element) - } else { - className = ElementUtility.getClassName(typeMirror.toString(), processorManager) - } - return className +fun fromTypeMirror(typeMirror: TypeMirror, processorManager: ProcessorManager): ClassName? { + val element = getTypeElement(typeMirror) + return if (element != null) { + ClassName.get(element) + } else { + ElementUtility.getClassName(typeMirror.toString(), processorManager) } +} - fun getTypeElement(element: Element): TypeElement? { - val typeElement: TypeElement? - if (element is TypeElement) { - typeElement = element - } else { - typeElement = getTypeElement(element.asType()) - } - return typeElement +fun getTypeElement(element: Element): TypeElement? { + val typeElement: TypeElement? + if (element is TypeElement) { + typeElement = element + } else { + typeElement = getTypeElement(element.asType()) } + return typeElement +} - fun getTypeElement(typeMirror: TypeMirror): TypeElement? { - val manager = ProcessorManager.manager - var typeElement: TypeElement? = typeMirror.toTypeElement(manager) - if (typeElement == null) { - val el = manager.typeUtils.asElement(typeMirror) - typeElement = if (el != null) (el as TypeElement) else null - } - return typeElement +fun getTypeElement(typeMirror: TypeMirror): TypeElement? { + val manager = ProcessorManager.manager + var typeElement: TypeElement? = typeMirror.toTypeElement(manager) + if (typeElement == null) { + val el = manager.typeUtils.asElement(typeMirror) + typeElement = if (el != null) (el as TypeElement) else null } + return typeElement +} - fun ensureVisibleStatic(element: Element, typeElement: TypeElement, - name: String) { - if (element.modifiers.contains(Modifier.PRIVATE) - || element.modifiers.contains(Modifier.PROTECTED)) { - manager.logError("$name must be visible from: " + typeElement) - } - if (!element.modifiers.contains(Modifier.STATIC)) { - manager.logError("$name must be static from: " + typeElement) - } +fun ensureVisibleStatic(element: Element, typeElement: TypeElement, + name: String) { + if (element.modifiers.contains(Modifier.PRIVATE) + || element.modifiers.contains(Modifier.PROTECTED)) { + manager.logError("$name must be visible from: " + typeElement) + } + if (!element.modifiers.contains(Modifier.STATIC)) { + manager.logError("$name must be static from: " + typeElement) + } - if (!element.modifiers.contains(Modifier.FINAL)) { - manager.logError("The $name must be final") - } + if (!element.modifiers.contains(Modifier.FINAL)) { + manager.logError("The $name must be final") } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseDefinition.kt index a54f10224..944637507 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseDefinition.kt @@ -1,15 +1,14 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.utils.ElementUtility import com.squareup.javapoet.ClassName import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec -import java.util.* import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement -import javax.lang.model.element.Modifier import javax.lang.model.element.TypeElement import javax.lang.model.type.TypeMirror @@ -39,14 +38,14 @@ abstract class BaseDefinition : TypeDefinition { try { val typeMirror = element.asType() - elementTypeName = TypeName.get(typeMirror) + elementTypeName = typeMirror.typeName elementTypeName?.let { if (!it.isPrimitive) { elementClassName = getElementClassName(element) } } val erasedType = processorManager.typeUtils.erasure(typeMirror) - erasedTypeName = TypeName.get(erasedType) + erasedTypeName = erasedType.typeName } catch (e: Exception) { } @@ -60,16 +59,16 @@ abstract class BaseDefinition : TypeDefinition { val typeMirror: TypeMirror if (element is ExecutableElement) { typeMirror = element.returnType - elementTypeName = TypeName.get(typeMirror) + elementTypeName = typeMirror.typeName } else { typeMirror = element.asType() - elementTypeName = TypeName.get(typeMirror) + elementTypeName = typeMirror.typeName } val erasedType = processorManager.typeUtils.erasure(typeMirror) erasedTypeName = TypeName.get(erasedType) } catch (i: IllegalArgumentException) { - manager.logError("Found illegal type:" + element.asType() + " for " + element.simpleName.toString()) - manager.logError("Exception here:" + i.toString()) + manager.logError("Found illegal type: ${element.asType()} for ${element.simpleName}") + manager.logError("Exception here: $i") } elementName = element.simpleName.toString() @@ -87,7 +86,7 @@ abstract class BaseDefinition : TypeDefinition { this.typeElement = element this.element = element elementClassName = ClassName.get(typeElement) - elementTypeName = TypeName.get(element.asType()) + elementTypeName = element.asType().typeName elementName = element.simpleName.toString() packageName = manager.elements.getPackageOf(element)?.qualifiedName?.toString() ?: "" } @@ -124,16 +123,13 @@ abstract class BaseDefinition : TypeDefinition { override val typeSpec: TypeSpec get() { - val typeBuilder = TypeSpec.classBuilder(outputClassName?.simpleName()) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addSuperinterfaces(Arrays.asList(*implementsClasses)) - val extendsClass = extendsClass - if (extendsClass != null) { - typeBuilder.superclass(extendsClass) + return `public final class`(outputClassName?.simpleName() ?: "") { + extendsClass?.let { extends(it) } + implementsClasses.forEach { implements(it) } + javadoc("This is generated code. Please do not modify") + onWriteDefinition(this) + this } - typeBuilder.addJavadoc("This is generated code. Please do not modify") - onWriteDefinition(typeBuilder) - return typeBuilder.build() } protected open val extendsClass: TypeName? diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ContentProvider.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ContentProvider.kt index 4190603cd..3e543ddb5 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ContentProvider.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ContentProvider.kt @@ -10,6 +10,7 @@ import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.TableEndpointValidator import com.raizlabs.android.dbflow.processor.utils.`override fun` +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.controlFlow import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.squareup.javapoet.* @@ -18,9 +19,9 @@ import javax.lang.model.type.MirroredTypeException internal fun appendDefault(code: CodeBlock.Builder) { code.beginControlFlow("default:") - .addStatement("throw new \$T(\$S + \$L)", - ClassName.get(IllegalArgumentException::class.java), "Unknown URI", Constants.PARAM_URI) - .endControlFlow() + .addStatement("throw new \$T(\$S + \$L)", + ClassName.get(IllegalArgumentException::class.java), "Unknown URI", Constants.PARAM_URI) + .endControlFlow() } object Constants { @@ -33,25 +34,24 @@ object Constants { * Get any code needed to use path segments. This should be called before creating the statement that uses * [.getSelectionAndSelectionArgs]. */ -internal fun ContentUriDefinition.getSegmentsPreparation(): CodeBlock { - if (segments.size == 0) { - return CodeBlock.builder().build() - } else { - return CodeBlock.builder().addStatement("\$T<\$T> segments = uri.getPathSegments()", - List::class.java, String::class.java).build() +internal fun ContentUriDefinition.getSegmentsPreparation() = code { + if (segments.isNotEmpty()) { + statement("\$T segments = uri.getPathSegments()", + parameterized(List::class)) } + this } /** * Get code which creates the `selection` and `selectionArgs` parameters separated by a comma. */ internal fun ContentUriDefinition.getSelectionAndSelectionArgs(): CodeBlock { - if (segments.size == 0) { + if (segments.isEmpty()) { return CodeBlock.builder().add("selection, selectionArgs").build() } else { val selectionBuilder = CodeBlock.builder().add("\$T.concatenateWhere(selection, \"", ClassNames.DATABASE_UTILS) val selectionArgsBuilder = CodeBlock.builder().add("\$T.appendSelectionArgs(selectionArgs, new \$T[] {", - ClassNames.DATABASE_UTILS, String::class.java) + ClassNames.DATABASE_UTILS, String::class.java) var isFirst = true for (segment in segments) { if (!isFirst) { @@ -84,21 +84,21 @@ class DeleteMethod(private val contentProviderDefinition: ContentProviderDefinit contentProviderDefinition.endpointDefinitions.forEach { it.contentUriDefinitions.forEach { uriDefinition -> if (uriDefinition.deleteEnabled) { - - code.beginControlFlow("case \$L:", uriDefinition.name) - - code.add(uriDefinition.getSegmentsPreparation()) - code.add("long count = \$T.getDatabase(\$S).getWritableDatabase().delete(\$S, ", - ClassNames.FLOW_MANAGER, - manager.getDatabaseName(contentProviderDefinition.databaseName), - it.tableName) - code.add(uriDefinition.getSelectionAndSelectionArgs()) - code.add(");\n") - - NotifyMethod(it, uriDefinition, Notify.Method.DELETE).addCode(code) - - code.addStatement("return (int) count") - code.endControlFlow() + code.apply { + case(uriDefinition.name.L) { + add(uriDefinition.getSegmentsPreparation()) + add("long count = \$T.getDatabase(\$S).getWritableDatabase().delete(\$S, ", + ClassNames.FLOW_MANAGER, + manager.getDatabaseName(contentProviderDefinition.databaseName), + it.tableName) + add(uriDefinition.getSelectionAndSelectionArgs()) + add(");\n") + + NotifyMethod(it, uriDefinition, Notify.Method.DELETE).addCode(this) + + `return`("(int) count") + } + } } } } @@ -107,12 +107,12 @@ class DeleteMethod(private val contentProviderDefinition: ContentProviderDefinit code.endControlFlow() return MethodSpec.methodBuilder("delete") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.URI, PARAM_URI) - .addParameter(ClassName.get(String::class.java), PARAM_SELECTION) - .addParameter(ArrayTypeName.of(String::class.java), PARAM_SELECTION_ARGS) - .addCode(code.build()).returns(TypeName.INT).build() + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.URI, PARAM_URI) + .addParameter(ClassName.get(String::class.java), PARAM_SELECTION) + .addParameter(ArrayTypeName.of(String::class.java), PARAM_SELECTION_ARGS) + .addCode(code.build()).returns(TypeName.INT).build() } companion object { @@ -141,12 +141,12 @@ class InsertMethod(private val contentProviderDefinition: ContentProviderDefinit code.apply { beginControlFlow("case \$L:", uriDefinition.name) addStatement("\$T adapter = \$T.getModelAdapter(\$T.getTableClassForName(\$S, \$S))", - ClassNames.MODEL_ADAPTER, ClassNames.FLOW_MANAGER, ClassNames.FLOW_MANAGER, - contentProviderDefinition.databaseNameString, it.tableName) + ClassNames.MODEL_ADAPTER, ClassNames.FLOW_MANAGER, ClassNames.FLOW_MANAGER, + contentProviderDefinition.databaseNameString, it.tableName) add("final long id = FlowManager.getDatabase(\$S).getWritableDatabase()", - contentProviderDefinition.databaseNameString).add(".insertWithOnConflict(\$S, null, values, " + "\$T.getSQLiteDatabaseAlgorithmInt(adapter.getInsertOnConflictAction()));\n", it.tableName, - ClassNames.CONFLICT_ACTION) + contentProviderDefinition.databaseNameString).add(".insertWithOnConflict(\$S, null, values, " + "\$T.getSQLiteDatabaseAlgorithmInt(adapter.getInsertOnConflictAction()));\n", it.tableName, + ClassNames.CONFLICT_ACTION) NotifyMethod(it, uriDefinition, Notify.Method.INSERT).addCode(this) @@ -164,10 +164,10 @@ class InsertMethod(private val contentProviderDefinition: ContentProviderDefinit appendDefault(code) code.endControlFlow() return MethodSpec.methodBuilder(if (isBulk) "bulkInsert" else "insert") - .addAnnotation(Override::class.java).addParameter(ClassNames.URI, Constants.PARAM_URI) - .addParameter(ClassNames.CONTENT_VALUES, Constants.PARAM_CONTENT_VALUES) - .addModifiers(if (isBulk) Modifier.PROTECTED else Modifier.PUBLIC, Modifier.FINAL) - .addCode(code.build()).returns(if (isBulk) TypeName.INT else ClassNames.URI).build() + .addAnnotation(Override::class.java).addParameter(ClassNames.URI, Constants.PARAM_URI) + .addParameter(ClassNames.CONTENT_VALUES, Constants.PARAM_CONTENT_VALUES) + .addModifiers(if (isBulk) Modifier.PROTECTED else Modifier.PUBLIC, Modifier.FINAL) + .addCode(code.build()).returns(if (isBulk) TypeName.INT else ClassNames.URI).build() } } @@ -188,16 +188,16 @@ class NotifyMethod(private val tableEndpointDefinition: TableEndpointDefinition, val notifyDefinition = notifyDefinitionList[i] if (notifyDefinition.returnsArray) { code.addStatement("\$T[] notifyUris\$L = \$L.\$L(\$L)", ClassNames.URI, - notifyDefinition.methodName, notifyDefinition.parent, - notifyDefinition.methodName, notifyDefinition.params) + notifyDefinition.methodName, notifyDefinition.parent, + notifyDefinition.methodName, notifyDefinition.params) code.beginControlFlow("for (\$T notifyUri: notifyUris\$L)", ClassNames.URI, notifyDefinition.methodName) } else { code.addStatement("\$T notifyUri\$L = \$L.\$L(\$L)", ClassNames.URI, - notifyDefinition.methodName, notifyDefinition.parent, - notifyDefinition.methodName, notifyDefinition.params) + notifyDefinition.methodName, notifyDefinition.parent, + notifyDefinition.methodName, notifyDefinition.params) } code.addStatement("getContext().getContentResolver().notifyChange(notifyUri\$L, null)", - if (notifyDefinition.returnsArray) "" else notifyDefinition.methodName) + if (notifyDefinition.returnsArray) "" else notifyDefinition.methodName) if (notifyDefinition.returnsArray) { code.endControlFlow() } @@ -233,14 +233,14 @@ class QueryMethod(private val contentProviderDefinition: ContentProviderDefiniti override val methodSpec: MethodSpec? get() { val method = MethodSpec.methodBuilder("query") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.URI, "uri") - .addParameter(ArrayTypeName.of(String::class.java), "projection") - .addParameter(ClassName.get(String::class.java), "selection") - .addParameter(ArrayTypeName.of(String::class.java), "selectionArgs") - .addParameter(ClassName.get(String::class.java), "sortOrder") - .returns(ClassNames.CURSOR) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.URI, "uri") + .addParameter(ArrayTypeName.of(String::class.java), "projection") + .addParameter(ClassName.get(String::class.java), "selection") + .addParameter(ArrayTypeName.of(String::class.java), "selectionArgs") + .addParameter(ClassName.get(String::class.java), "sortOrder") + .returns(ClassNames.CURSOR) method.addStatement("\$L cursor = null", ClassNames.CURSOR) method.beginControlFlow("switch(\$L.match(uri))", ContentProviderDefinition.URI_MATCHER) @@ -251,9 +251,9 @@ class QueryMethod(private val contentProviderDefinition: ContentProviderDefiniti beginControlFlow("case \$L:", uriDefinition.name) addCode(uriDefinition.getSegmentsPreparation()) addCode("cursor = \$T.getDatabase(\$S).getWritableDatabase().query(\$S, projection, ", - ClassNames.FLOW_MANAGER, - manager.getDatabaseName(contentProviderDefinition.databaseName), - tableEndpointDefinition.tableName) + ClassNames.FLOW_MANAGER, + manager.getDatabaseName(contentProviderDefinition.databaseName), + tableEndpointDefinition.tableName) addCode(uriDefinition.getSelectionAndSelectionArgs()) addCode(", null, null, sortOrder);\n") addStatement("break") @@ -282,13 +282,13 @@ class UpdateMethod(private val contentProviderDefinition: ContentProviderDefinit override val methodSpec: MethodSpec? get() { val method = MethodSpec.methodBuilder("update") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC) - .addParameter(ClassNames.URI, Constants.PARAM_URI) - .addParameter(ClassNames.CONTENT_VALUES, Constants.PARAM_CONTENT_VALUES) - .addParameter(ClassName.get(String::class.java), "selection") - .addParameter(ArrayTypeName.of(String::class.java), "selectionArgs") - .returns(TypeName.INT) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC) + .addParameter(ClassNames.URI, Constants.PARAM_URI) + .addParameter(ClassNames.CONTENT_VALUES, Constants.PARAM_CONTENT_VALUES) + .addParameter(ClassName.get(String::class.java), "selection") + .addParameter(ArrayTypeName.of(String::class.java), "selectionArgs") + .returns(TypeName.INT) method.beginControlFlow("switch(MATCHER.match(\$L))", Constants.PARAM_URI) for (tableEndpointDefinition in contentProviderDefinition.endpointDefinitions) { @@ -297,24 +297,24 @@ class UpdateMethod(private val contentProviderDefinition: ContentProviderDefinit method.apply { beginControlFlow("case \$L:", uriDefinition.name) addStatement("\$T adapter = \$T.getModelAdapter(\$T.getTableClassForName(\$S, \$S))", - ClassNames.MODEL_ADAPTER, ClassNames.FLOW_MANAGER, ClassNames.FLOW_MANAGER, - contentProviderDefinition.databaseNameString, - tableEndpointDefinition.tableName) + ClassNames.MODEL_ADAPTER, ClassNames.FLOW_MANAGER, ClassNames.FLOW_MANAGER, + contentProviderDefinition.databaseNameString, + tableEndpointDefinition.tableName) addCode(uriDefinition.getSegmentsPreparation()) addCode( - "long count = \$T.getDatabase(\$S).getWritableDatabase().updateWithOnConflict(\$S, \$L, ", - ClassNames.FLOW_MANAGER, - manager.getDatabaseName(contentProviderDefinition.databaseName), - tableEndpointDefinition.tableName, - Constants.PARAM_CONTENT_VALUES) + "long count = \$T.getDatabase(\$S).getWritableDatabase().updateWithOnConflict(\$S, \$L, ", + ClassNames.FLOW_MANAGER, + manager.getDatabaseName(contentProviderDefinition.databaseName), + tableEndpointDefinition.tableName, + Constants.PARAM_CONTENT_VALUES) addCode(uriDefinition.getSelectionAndSelectionArgs()) addCode( - ", \$T.getSQLiteDatabaseAlgorithmInt(adapter.getUpdateOnConflictAction()));\n", - ClassNames.CONFLICT_ACTION) + ", \$T.getSQLiteDatabaseAlgorithmInt(adapter.getUpdateOnConflictAction()));\n", + ClassNames.CONFLICT_ACTION) val code = CodeBlock.builder() NotifyMethod(tableEndpointDefinition, uriDefinition, - Notify.Method.UPDATE).addCode(code) + Notify.Method.UPDATE).addCode(code) addCode(code.build()) addStatement("return (int) count") @@ -349,14 +349,14 @@ class ContentProviderDefinition(typeElement: Element, processorManager: Processo var endpointDefinitions: MutableList = Lists.newArrayList() private val methods: Array = arrayOf(QueryMethod(this, manager), - InsertMethod(this, false), - InsertMethod(this, true), - DeleteMethod(this, manager), - UpdateMethod(this, manager)) + InsertMethod(this, false), + InsertMethod(this, true), + DeleteMethod(this, manager), + UpdateMethod(this, manager)) init { - val provider = element.getAnnotation(ContentProvider::class.java) + val provider = element.annotation() if (provider != null) { try { provider.database @@ -406,10 +406,10 @@ class ContentProviderDefinition(typeElement: Element, processorManager: Processo `override fun`(TypeName.BOOLEAN, "onCreate") { modifiers(public, final) addStatement("final \$T $AUTHORITY = \$L", String::class.java, - if (authority.contains("R.string.")) - "getContext().getString($authority)" - else - "\"$authority\"") + if (authority.contains("R.string.")) + "getContext().getString($authority)" + else + "\"$authority\"") for (endpointDefinition in endpointDefinitions) { endpointDefinition.contentUriDefinitions.forEach { @@ -418,7 +418,7 @@ class ContentProviderDefinition(typeElement: Element, processorManager: Processo path = "\"" + it.path + "\"" } else { path = CodeBlock.builder().add("\$L.\$L.getPath()", it.elementClassName, - it.name).build().toString() + it.name).build().toString() } addStatement("\$L.addURI(\$L, \$L, \$L)", URI_MATCHER, AUTHORITY, path, it.name) } @@ -438,12 +438,12 @@ class ContentProviderDefinition(typeElement: Element, processorManager: Processo statement("\$T type = null", ClassName.get(String::class.java)) controlFlow("switch(\$L.match(uri))", URI_MATCHER) { endpointDefinitions.flatMap { it.contentUriDefinitions } - .forEach { uri -> - controlFlow("case \$L:", uri.name) { - statement("type = \$S", uri.type) - `break`() + .forEach { uri -> + controlFlow("case \$L:", uri.name) { + statement("type = \$S", uri.type) + `break`() + } } - } appendDefault(this) } `return`("type") @@ -452,7 +452,7 @@ class ContentProviderDefinition(typeElement: Element, processorManager: Processo } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } } companion object { @@ -470,30 +470,32 @@ class ContentUriDefinition(typeElement: Element, processorManager: ProcessorMana var name = typeElement.enclosingElement.simpleName.toString() + "_" + typeElement.simpleName.toString() - var path: String + var path = "" + + var type = "" - var type: String + var queryEnabled = false - var queryEnabled: Boolean = false + var insertEnabled = false - var insertEnabled: Boolean = false + var deleteEnabled = false - var deleteEnabled: Boolean = false - var updateEnabled: Boolean = false + var updateEnabled = false - var segments: Array + var segments = arrayOf() init { - val contentUri = typeElement.getAnnotation(ContentUri::class.java) - path = contentUri.path - type = contentUri.type - queryEnabled = contentUri.queryEnabled - insertEnabled = contentUri.insertEnabled - deleteEnabled = contentUri.deleteEnabled - updateEnabled = contentUri.updateEnabled - - segments = contentUri.segments + typeElement.annotation()?.let { contentUri -> + path = contentUri.path + type = contentUri.type + queryEnabled = contentUri.queryEnabled + insertEnabled = contentUri.insertEnabled + deleteEnabled = contentUri.deleteEnabled + updateEnabled = contentUri.updateEnabled + + segments = contentUri.segments + } if (typeElement is VariableElement) { if (ClassNames.URI != elementTypeName) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index 1c35410cb..ccc3ee3a1 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -5,6 +5,7 @@ import com.raizlabs.android.dbflow.annotation.ConflictAction import com.raizlabs.android.dbflow.annotation.Database import com.raizlabs.android.dbflow.processor.* import com.raizlabs.android.dbflow.processor.utils.`override fun` +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.squareup.javapoet.* import java.util.* @@ -43,8 +44,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi init { packageName = ClassNames.FLOW_MANAGER_PACKAGE - val database = element.getAnnotation(Database::class.java) - if (database != null) { + element.annotation()?.let { database -> databaseName = database.name databaseExtensionName = database.databaseExtension if (databaseName.isNullOrEmpty()) { @@ -52,7 +52,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi } if (!isValidDatabaseName(databaseName)) { throw Error("Database name [ " + databaseName + " ] is not valid. It must pass [A-Za-z_$]+[a-zA-Z0-9_$]* " + - "regex so it can't start with a number or contain any special character except '$'. Especially a dot character is not allowed!") + "regex so it can't start with a number or contain any special character except '$'. Especially a dot character is not allowed!") } consistencyChecksEnabled = database.consistencyCheckEnabled @@ -134,19 +134,19 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi statement("holder.putDatabaseForTable(\$T.class, this)", tableDefinition.elementClassName) statement("\$L.put(\$S, \$T.class)", DatabaseHandler.MODEL_NAME_MAP, tableDefinition.tableName, tableDefinition.elementClassName) statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, - tableDefinition.elementClassName, tableDefinition.outputClassName) + tableDefinition.elementClassName, tableDefinition.outputClassName) } for (modelViewDefinition in manager.getModelViewDefinitions(elementClassName)) { statement("holder.putDatabaseForTable(\$T.class, this)", modelViewDefinition.elementClassName) statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_VIEW_ADAPTER_MAP_FIELD_NAME, - modelViewDefinition.elementClassName, modelViewDefinition.outputClassName) + modelViewDefinition.elementClassName, modelViewDefinition.outputClassName) } for (queryModelDefinition in manager.getQueryModelDefinitions(elementClassName)) { statement("holder.putDatabaseForTable(\$T.class, this)", queryModelDefinition.elementClassName) statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.QUERY_MODEL_ADAPTER_MAP_FIELD_NAME, - queryModelDefinition.elementClassName, queryModelDefinition.outputClassName) + queryModelDefinition.elementClassName, queryModelDefinition.outputClassName) } val migrationDefinitionMap = manager.getMigrationsForDatabase(elementClassName) @@ -158,12 +158,12 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi migrationDefinitions?.let { Collections.sort(migrationDefinitions, { o1, o2 -> Integer.valueOf(o2.priority)!!.compareTo(o1.priority) }) statement("\$T migrations\$L = new \$T()", ParameterizedTypeName.get(ClassName.get(List::class.java), ClassNames.MIGRATION), - version, ParameterizedTypeName.get(ClassName.get(ArrayList::class.java), ClassNames.MIGRATION)) + version, ParameterizedTypeName.get(ClassName.get(ArrayList::class.java), ClassNames.MIGRATION)) statement("\$L.put(\$L, migrations\$L)", DatabaseHandler.MIGRATION_FIELD_NAME, - version, version) + version, version) for (migrationDefinition in migrationDefinitions) { statement("migrations\$L.add(new \$T\$L)", version, migrationDefinition.elementClassName, - migrationDefinition.constructorName) + migrationDefinition.constructorName) } } } @@ -177,7 +177,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi private fun writeGetters(typeBuilder: TypeSpec.Builder) { typeBuilder.apply { `override fun`(ParameterizedTypeName.get(ClassName.get(Class::class.java), WildcardTypeName.subtypeOf(Any::class.java)), - "getAssociatedDatabaseClassFile") { + "getAssociatedDatabaseClassFile") { modifiers(public, final) `return`("\$T.class", elementTypeName) } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt index 232a721ab..048dd8804 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt @@ -46,9 +46,13 @@ class DatabaseHolderDefinition(private val processorManager: ProcessorManager) : } } - processorManager.getDatabaseHolderDefinitionList().sortedBy { it.databaseDefinition?.outputClassName?.simpleName() }.forEach { databaseDefinition -> - databaseDefinition.databaseDefinition?.let { constructor.addStatement("new \$T(this)", it.outputClassName) } - } + processorManager.getDatabaseHolderDefinitionList() + .sortedBy { it.databaseDefinition?.outputClassName?.simpleName() } + .forEach { databaseDefinition -> + databaseDefinition.databaseDefinition?.let { + constructor.addStatement("new \$T(this)", it.outputClassName) + } + } typeBuilder.addMethod(constructor.build()) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt index d486181aa..f8af6907d 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt @@ -1,49 +1,38 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.IndexGroup import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.squareup.javapoet.CodeBlock -import com.squareup.javapoet.FieldSpec import com.squareup.javapoet.ParameterizedTypeName import java.util.* import java.util.concurrent.atomic.AtomicInteger -import javax.lang.model.element.Modifier /** * Description: */ class IndexGroupsDefinition(private val tableDefinition: TableDefinition, indexGroup: IndexGroup) { - val indexName: String - val indexNumber: Int - val isUnique: Boolean + val indexName = indexGroup.name + val indexNumber = indexGroup.number + val isUnique = indexGroup.unique val columnDefinitionList: MutableList = ArrayList() - init { - this.indexName = indexGroup.name - this.indexNumber = indexGroup.number - this.isUnique = indexGroup.unique - } - - val fieldSpec: FieldSpec - get() { - val fieldBuilder = FieldSpec.builder(ParameterizedTypeName.get(ClassNames.INDEX_PROPERTY, tableDefinition.elementClassName), - "index_" + indexName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - val initializer = CodeBlock.builder().add("new \$T<>(\$S, \$L, \$T.class", ClassNames.INDEX_PROPERTY, + val fieldSpec = field(ParameterizedTypeName.get(ClassNames.INDEX_PROPERTY, tableDefinition.elementClassName), + "index_$indexName") { + addModifiers(public, static, final) + `=` { + add("new \$T<>(\$S, \$L, \$T.class", ClassNames.INDEX_PROPERTY, indexName, isUnique, tableDefinition.elementTypeName) if (columnDefinitionList.isNotEmpty()) { - initializer.add(",") + add(",") } val index = AtomicInteger(0) - columnDefinitionList.forEach { it.appendIndexInitializer(initializer, index) } - initializer.add(")") - - fieldBuilder.initializer(initializer.build()) - - return fieldBuilder.build() + columnDefinitionList.forEach { it.appendIndexInitializer(this, index) } + add(")") } + }.build()!! } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt index 5df837639..7eebb341e 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt @@ -1,16 +1,13 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.DefinitionUtils import com.raizlabs.android.dbflow.processor.utils.ModelUtils +import com.raizlabs.android.dbflow.processor.utils.`override fun` import com.raizlabs.android.dbflow.sql.QueryBuilder -import com.squareup.javapoet.ArrayTypeName -import com.squareup.javapoet.ClassName -import com.squareup.javapoet.MethodSpec -import com.squareup.javapoet.ParameterizedTypeName -import com.squareup.javapoet.TypeName -import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.* import javax.lang.model.element.Modifier /** @@ -18,32 +15,27 @@ import javax.lang.model.element.Modifier */ object InternalAdapterHelper { - fun writeGetModelClass(typeBuilder: TypeSpec.Builder, modelClassName: ClassName?) { - typeBuilder.addMethod(MethodSpec.methodBuilder("getModelClass") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addStatement("return \$T.class", modelClassName) - .returns(ParameterizedTypeName.get(ClassName.get(Class::class.java), modelClassName)) - .build()) + fun writeGetModelClass(typeBuilder: TypeSpec.Builder, modelClassName: ClassName?) = typeBuilder.apply { + `override fun`(ParameterizedTypeName.get(ClassName.get(Class::class.java), modelClassName), "getModelClass") { + modifiers(public, final) + `return`("\$T.class", modelClassName) + } } - fun writeGetTableName(typeBuilder: TypeSpec.Builder, tableName: String?) { - typeBuilder.addMethod(MethodSpec.methodBuilder("getTableName") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addStatement("return \$S", QueryBuilder.quote(tableName)) - .returns(ClassName.get(String::class.java)) - .build()) + fun writeGetTableName(typeBuilder: TypeSpec.Builder, tableName: String?) = typeBuilder.apply { + `override fun`(String::class, "getTableName") { + modifiers(public, final) + `return`(QueryBuilder.quote(tableName).S) + } } fun writeUpdateAutoIncrement(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, - autoIncrementDefinition: ColumnDefinition) { - typeBuilder.addMethod(MethodSpec.methodBuilder("updateAutoIncrement") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - .addParameter(ClassName.get(Number::class.java), "id") - .addCode(autoIncrementDefinition.updateAutoIncrementMethod) - .build()) + autoIncrementDefinition: ColumnDefinition) = typeBuilder.apply { + `override fun`(TypeName.VOID, "updateAutoIncrement", param(modelClassName!!, ModelUtils.variable), + param(Number::class, "id")) { + modifiers(public, final) + addCode(autoIncrementDefinition.updateAutoIncrementMethod) + } } fun writeGetCachingId(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt index be541a3c5..dd7a6ddc6 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt @@ -7,10 +7,7 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.utils.capitalizeFirstLetter -import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty -import com.raizlabs.android.dbflow.processor.utils.lower -import com.raizlabs.android.dbflow.processor.utils.toTypeElement +import com.raizlabs.android.dbflow.processor.utils.* import com.squareup.javapoet.AnnotationSpec import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec @@ -22,7 +19,7 @@ import javax.lang.model.type.TypeMirror * Description: Generates the Model class that is used in a many to many. */ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, processorManager: ProcessorManager, - manyToMany: ManyToMany = element.getAnnotation(ManyToMany::class.java)) + manyToMany: ManyToMany = element.annotation()!!) : BaseDefinition(element, processorManager) { internal var referencedTable: TypeName @@ -48,15 +45,16 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce sameTableReferenced = referencedTable == elementTypeName - val table = element.getAnnotation(Table::class.java) - try { - table.database - } catch (mte: MirroredTypeException) { - databaseTypeName = TypeName.get(mte.typeMirror) + element.annotation
()?.let { table -> + try { + table.database + } catch (mte: MirroredTypeException) { + databaseTypeName = TypeName.get(mte.typeMirror) + } } if (!thisColumnName.isNullOrEmpty() && !referencedColumnName.isNullOrEmpty() - && thisColumnName == referencedColumnName) { + && thisColumnName == referencedColumnName) { manager.logError(ManyToManyDefinition::class, "The thisTableColumnName and referenceTableColumnName" + "cannot be the same") } } @@ -77,7 +75,7 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { typeBuilder.addAnnotation(AnnotationSpec.builder(Table::class.java) - .addMember("database", "\$T.class", databaseTypeName).build()) + .addMember("database", "\$T.class", databaseTypeName).build()) val referencedDefinition = manager.getTableDefinition(databaseTypeName, referencedTable) val selfDefinition = manager.getTableDefinition(databaseTypeName, elementTypeName) @@ -123,7 +121,7 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce `return`(fieldName.L) } `fun`(TypeName.VOID, "set${fieldName.capitalizeFirstLetter()}", - param(referencedDefinition.elementClassName!!, "param")) { + param(referencedDefinition.elementClassName!!, "param")) { modifiers(public, final) statement("$fieldName = param") } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt index 99adf9f0d..3a8fcc75a 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt @@ -1,7 +1,9 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.typeName import com.raizlabs.android.dbflow.annotation.Migration import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock @@ -15,7 +17,7 @@ import javax.lang.model.type.MirroredTypeException * Description: Used in holding data about migration files. */ class MigrationDefinition(processorManager: ProcessorManager, typeElement: TypeElement) -: BaseDefinition(typeElement, processorManager) { + : BaseDefinition(typeElement, processorManager) { var databaseName: TypeName? = null @@ -29,14 +31,14 @@ class MigrationDefinition(processorManager: ProcessorManager, typeElement: TypeE init { setOutputClassName("") - val migration = typeElement.getAnnotation(Migration::class.java) + val migration = typeElement.annotation() if (migration == null) { processorManager.logError("Migration was null for:" + typeElement) } else { try { migration.database } catch (mte: MirroredTypeException) { - databaseName = TypeName.get(mte.typeMirror) + databaseName = mte.typeMirror.typeName } version = migration.version @@ -44,7 +46,7 @@ class MigrationDefinition(processorManager: ProcessorManager, typeElement: TypeE val elements = typeElement.enclosedElements for (element in elements) { - if (element is ExecutableElement && element.getSimpleName().toString() == "") { + if (element is ExecutableElement && element.simpleName.toString() == "") { if (!constructorName.isNullOrEmpty()) { manager.logError(MigrationDefinition::class, "Migrations cannot have more than one constructor. " + "They can only have an Empty() or single-parameter constructor Empty(Empty.class) that specifies " + @@ -57,12 +59,12 @@ class MigrationDefinition(processorManager: ProcessorManager, typeElement: TypeE val params = element.parameters val param = params[0] - val type = TypeName.get(param.asType()) + val type = param.asType().typeName if (type is ParameterizedTypeName && type.rawType == ClassName.get(Class::class.java)) { val containedType = type.typeArguments[0] - constructorName = CodeBlock.builder().add("(\$T.class)", containedType).build().toString() + constructorName = CodeBlock.of("(\$T.class)", containedType).toString() } else { - manager.logError(MigrationDefinition::class, "Wrong parameter type found for %1s. Found %1s" + "but required ModelClass.class", typeElement, type) + manager.logError(MigrationDefinition::class, "Wrong parameter type found for $typeElement. Found $type but required ModelClass.class") } } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index 0287b63b2..70d64b78e 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -4,10 +4,7 @@ import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.Column import com.raizlabs.android.dbflow.annotation.ModelView import com.raizlabs.android.dbflow.annotation.ModelViewQuery -import com.raizlabs.android.dbflow.processor.ClassNames -import com.raizlabs.android.dbflow.processor.ColumnValidator -import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.ProcessorUtils +import com.raizlabs.android.dbflow.processor.* import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.utils.* @@ -31,7 +28,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab private var name: String? = null private val methods: Array = - arrayOf(LoadFromCursorMethod(this), ExistenceMethod(this), PrimaryConditionMethod(this)) + arrayOf(LoadFromCursorMethod(this), ExistenceMethod(this), PrimaryConditionMethod(this)) var allFields: Boolean = false @@ -39,8 +36,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab init { - val modelView = element.getAnnotation(ModelView::class.java) - if (modelView != null) { + element.annotation()?.let { modelView -> try { modelView.database } catch (mte: MirroredTypeException) { @@ -57,8 +53,8 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } if (element is TypeElement) { - implementsLoadFromCursorListener = ProcessorUtils.implementsClass(manager.processingEnvironment, - ClassNames.LOAD_FROM_CURSOR_LISTENER.toString(), element) + implementsLoadFromCursorListener = element.implementsClass(manager.processingEnvironment, + ClassNames.LOAD_FROM_CURSOR_LISTENER) } else { implementsLoadFromCursorListener = false } @@ -93,7 +89,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab val isValidAllFields = ElementUtility.isValidAllFields(allFields, element) - if (variableElement.getAnnotation(Column::class.java) != null || isValidAllFields) { + if (variableElement.annotation() != null || isValidAllFields) { // package private, will generate helper val isPackagePrivate = ElementUtility.isPackagePrivate(element) @@ -109,17 +105,17 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } if (columnDefinition.isPrimaryKey || columnDefinition is ForeignKeyColumnDefinition - || columnDefinition.isPrimaryKeyAutoIncrement || columnDefinition.isRowId) { + || columnDefinition.isPrimaryKeyAutoIncrement || columnDefinition.isRowId) { manager.logError("ModelViews cannot have primary or foreign keys") } - } else if (variableElement.getAnnotation(ModelViewQuery::class.java) != null) { + } else if (variableElement.annotation() != null) { if (!queryFieldName.isNullOrEmpty()) { manager.logError("Found duplicate ") } - ProcessorUtils.ensureVisibleStatic(variableElement, typeElement, "ModelViewQuery") + ensureVisibleStatic(variableElement, typeElement, "ModelViewQuery") val element = variableElement.toTypeErasedElement() - if (!ProcessorUtils.implementsClass(manager.processingEnvironment, ClassNames.QUERY.toString(), element)) { + if (!element.implementsClass(manager.processingEnvironment, ClassNames.QUERY)) { manager.logError("The field ${variableElement.simpleName} must implement ${ClassNames.QUERY}") } @@ -141,7 +137,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { typeBuilder.addField(FieldSpec.builder(ClassName.get(String::class.java), - "VIEW_NAME", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("\$S", name!!).build()) + "VIEW_NAME", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("\$S", name!!).build()) elementClassName?.let { columnDefinitions.forEach { columnDefinition -> @@ -153,7 +149,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab customTypeConverterPropertyMethod.addToType(typeBuilder) typeBuilder.constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { modifiers(public) statement("super(databaseDefinition)") code { @@ -162,7 +158,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/NotifyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/NotifyDefinition.kt index 972bc9123..dff447d93 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/NotifyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/NotifyDefinition.kt @@ -3,6 +3,7 @@ package com.raizlabs.android.dbflow.processor.definition import com.raizlabs.android.dbflow.annotation.provider.Notify import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.raizlabs.android.dbflow.processor.utils.annotation import com.squareup.javapoet.ClassName import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement @@ -14,8 +15,8 @@ import javax.lang.model.element.TypeElement class NotifyDefinition(typeElement: Element, processorManager: ProcessorManager) : BaseDefinition(typeElement, processorManager) { - var paths: Array - var method: Notify.Method + var paths = arrayOf() + var method = Notify.Method.DELETE val parent = (typeElement.enclosingElement as TypeElement).qualifiedName.toString() val methodName = typeElement.simpleName.toString() var params: String @@ -24,11 +25,10 @@ class NotifyDefinition(typeElement: Element, processorManager: ProcessorManager) init { - val notify = typeElement.getAnnotation(Notify::class.java) - - paths = notify.paths - - method = notify.method + typeElement.annotation()?.let { notify -> + paths = notify.paths + method = notify.method + } val executableElement = typeElement as ExecutableElement diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 78198fd99..2ca294d79 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -4,10 +4,11 @@ import com.google.common.collect.Lists import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.ProcessorUtils import com.raizlabs.android.dbflow.processor.definition.column.* +import com.raizlabs.android.dbflow.processor.isSubclass import com.raizlabs.android.dbflow.processor.utils.ModelUtils import com.raizlabs.android.dbflow.processor.utils.addStatement +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.controlFlow import com.squareup.javapoet.* import java.util.* @@ -46,7 +47,7 @@ class OneToManyDefinition(typeElement: ExecutableElement, init { - val oneToMany = typeElement.getAnnotation(OneToMany::class.java) + val oneToMany = typeElement.annotation()!! _methodName = typeElement.simpleName.toString() _variableName = oneToMany.variableName @@ -78,10 +79,10 @@ class OneToManyDefinition(typeElement: ExecutableElement, referencedTableType = refTableType referencedType = manager.elements.getTypeElement(referencedTableType?.toString()) - extendsBaseModel = ProcessorUtils.isSubclass(manager.processingEnvironment, - ClassNames.BASE_MODEL.toString(), referencedType) - extendsModel = ProcessorUtils.isSubclass(manager.processingEnvironment, - ClassNames.MODEL.toString(), referencedType) + extendsBaseModel = isSubclass(manager.processingEnvironment, + ClassNames.BASE_MODEL.toString(), referencedType) + extendsModel = isSubclass(manager.processingEnvironment, + ClassNames.MODEL.toString(), referencedType) } } } @@ -132,11 +133,11 @@ class OneToManyDefinition(typeElement: ExecutableElement, if (!extendsModel) { addStatement("\$T adapter = \$T.getModelAdapter(\$T.class)", - ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), - ClassNames.FLOW_MANAGER, referencedTableType) + ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), + ClassNames.FLOW_MANAGER, referencedTableType) addStatement("adapter.\$LAll(\$L\$L)", methodName, oneToManyMethodName, - if (useWrapper) ", " + ModelUtils.wrapper else "") + if (useWrapper) ", " + ModelUtils.wrapper else "") } else { controlFlow("for (\$T value: \$L) ", loopClass, oneToManyMethodName) { codeBuilder.addStatement("value.\$L(\$L)", methodName, if (useWrapper) ModelUtils.wrapper else "") diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index 21591c6e1..79c93c322 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -6,10 +6,11 @@ import com.raizlabs.android.dbflow.annotation.QueryModel import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ColumnValidator import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.ProcessorUtils import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition +import com.raizlabs.android.dbflow.processor.implementsClass import com.raizlabs.android.dbflow.processor.utils.ElementUtility import com.raizlabs.android.dbflow.processor.utils.`override fun` +import com.raizlabs.android.dbflow.processor.utils.annotation import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec @@ -34,21 +35,19 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana init { - val queryModel = typeElement.getAnnotation(QueryModel::class.java) - if (queryModel != null) { + typeElement.annotation()?.let { queryModel -> try { queryModel.database } catch (mte: MirroredTypeException) { databaseTypeName = TypeName.get(mte.typeMirror) } - } elementClassName?.let { databaseTypeName?.let { it1 -> processorManager.addModelToDatabase(it, it1) } } if (element is TypeElement) { - implementsLoadFromCursorListener = ProcessorUtils.implementsClass(manager.processingEnvironment, ClassNames.LOAD_FROM_CURSOR_LISTENER.toString(), - element as TypeElement) + implementsLoadFromCursorListener = + (element as TypeElement).implementsClass(manager.processingEnvironment, ClassNames.LOAD_FROM_CURSOR_LISTENER) } @@ -85,7 +84,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) typeBuilder.constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { modifiers(public) statement("super(databaseDefinition)") code { @@ -94,7 +93,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } typeBuilder.apply { `override fun`(elementClassName!!, "newInstance") { @@ -120,7 +119,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana val isPackagePrivate = ElementUtility.isPackagePrivate(element) val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, element, this.element) - if (variableElement.getAnnotation(Column::class.java) != null || isAllFields) { + if (variableElement.annotation() != null || isAllFields) { val columnDefinition = ColumnDefinition(manager, variableElement, this, isPackagePrivateNotInSamePackage) if (columnValidator.validate(manager, columnDefinition)) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index 06759ebcf..569ab8d25 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -56,7 +56,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val columnMap: MutableMap = Maps.newHashMap() var columnUniqueMap: MutableMap> - = Maps.newHashMap>() + = Maps.newHashMap>() var oneToManyDefinitions: MutableList = ArrayList() @@ -73,8 +73,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab uniqueGroupsDefinitions = ArrayList() indexGroupsDefinitions = ArrayList() - val table = element.getAnnotation(Table::class.java) - if (table != null) { + element.annotation
()?.let { table -> this.tableName = table.name if (tableName == null || tableName!!.isEmpty()) { @@ -103,7 +102,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab inheritedColumns.forEach { if (inheritedFieldNameList.contains(it.fieldName)) { manager.logError("A duplicate inherited column with name %1s was found for %1s", - it.fieldName, tableName) + it.fieldName, tableName) } inheritedFieldNameList.add(it.fieldName) inheritedColumnMap.put(it.fieldName, it) @@ -113,35 +112,35 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab inheritedPrimaryKeys.forEach { if (inheritedFieldNameList.contains(it.fieldName)) { manager.logError("A duplicate inherited column with name %1s was found for %1s", - it.fieldName, tableName) + it.fieldName, tableName) } inheritedFieldNameList.add(it.fieldName) inheritedPrimaryKeyMap.put(it.fieldName, it) } - implementsLoadFromCursorListener = ProcessorUtils.implementsClass(manager.processingEnvironment, - ClassNames.LOAD_FROM_CURSOR_LISTENER.toString(), element) + implementsLoadFromCursorListener = element.implementsClass(manager.processingEnvironment, + ClassNames.LOAD_FROM_CURSOR_LISTENER) - implementsContentValuesListener = ProcessorUtils.implementsClass(manager.processingEnvironment, - ClassNames.CONTENT_VALUES_LISTENER.toString(), element) + implementsContentValuesListener = element.implementsClass(manager.processingEnvironment, + ClassNames.CONTENT_VALUES_LISTENER) - implementsSqlStatementListener = ProcessorUtils.implementsClass(manager.processingEnvironment, - ClassNames.SQLITE_STATEMENT_LISTENER.toString(), element) + implementsSqlStatementListener = element.implementsClass(manager.processingEnvironment, + ClassNames.SQLITE_STATEMENT_LISTENER) } methods = arrayOf(BindToContentValuesMethod(this, true, implementsContentValuesListener), - BindToContentValuesMethod(this, false, implementsContentValuesListener), - BindToStatementMethod(this, true), BindToStatementMethod(this, false), - InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), - CreationQueryMethod(this), LoadFromCursorMethod(this), ExistenceMethod(this), - PrimaryConditionMethod(this), OneToManyDeleteMethod(this, false), - OneToManyDeleteMethod(this, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, true)) + BindToContentValuesMethod(this, false, implementsContentValuesListener), + BindToStatementMethod(this, true), BindToStatementMethod(this, false), + InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), + CreationQueryMethod(this), LoadFromCursorMethod(this), ExistenceMethod(this), + PrimaryConditionMethod(this), OneToManyDeleteMethod(this, false), + OneToManyDeleteMethod(this, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, true)) } override fun prepareForWrite() { @@ -175,7 +174,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab var updateConflict: ConflictAction? = table.updateConflict if (updateConflict == ConflictAction.NONE - && it.updateConflict != ConflictAction.NONE) { + && it.updateConflict != ConflictAction.NONE) { updateConflict = it.updateConflict } @@ -229,9 +228,9 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab for (element in elements) { classElementLookUpMap.put(element.simpleName.toString(), element) if (element is ExecutableElement && element.parameters.isEmpty() - && element.simpleName.toString() == "" - && element.enclosingElement == typeElement - && !element.modifiers.contains(Modifier.PRIVATE)) { + && element.simpleName.toString() == "" + && element.enclosingElement == typeElement + && !element.modifiers.contains(Modifier.PRIVATE)) { hasPrimaryConstructor = true } } @@ -246,28 +245,28 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val isPackagePrivate = ElementUtility.isPackagePrivate(element) val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, element, this.element) - val isForeign = element.getAnnotation(ForeignKey::class.java) != null - val isPrimary = element.getAnnotation(PrimaryKey::class.java) != null + val isForeign = element.annotation() != null + val isPrimary = element.annotation() != null val isInherited = inheritedColumnMap.containsKey(element.simpleName.toString()) val isInheritedPrimaryKey = inheritedPrimaryKeyMap.containsKey(element.simpleName.toString()) - if (element.getAnnotation(Column::class.java) != null || isForeign || isPrimary - || isAllFields || isInherited || isInheritedPrimaryKey) { + if (element.annotation() != null || isForeign || isPrimary + || isAllFields || isInherited || isInheritedPrimaryKey) { val columnDefinition: ColumnDefinition if (isInheritedPrimaryKey) { val inherited = inheritedPrimaryKeyMap[element.simpleName.toString()] columnDefinition = ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, inherited?.primaryKey) + inherited?.column, inherited?.primaryKey) } else if (isInherited) { val inherited = inheritedColumnMap[element.simpleName.toString()] columnDefinition = ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, null) + inherited?.column, null) } else if (isForeign) { columnDefinition = ForeignKeyColumnDefinition(manager, this, - element, isPackagePrivateNotInSamePackage) + element, isPackagePrivateNotInSamePackage) } else { columnDefinition = ColumnDefinition(manager, element, - this, isPackagePrivateNotInSamePackage) + this, isPackagePrivateNotInSamePackage) } if (columnValidator.validate(manager, columnDefinition)) { @@ -305,20 +304,20 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab packagePrivateList.add(columnDefinition) } } - } else if (element.getAnnotation(OneToMany::class.java) != null) { + } else if (element.annotation() != null) { val oneToManyDefinition = OneToManyDefinition(element as ExecutableElement, manager) if (oneToManyValidator.validate(manager, oneToManyDefinition)) { oneToManyDefinitions.add(oneToManyDefinition) } - } else if (element.getAnnotation(ModelCacheField::class.java) != null) { - ProcessorUtils.ensureVisibleStatic(element, typeElement, "ModelCacheField") + } else if (element.annotation() != null) { + ensureVisibleStatic(element, typeElement, "ModelCacheField") if (!customCacheFieldName.isNullOrEmpty()) { manager.logError("ModelCacheField can only be declared once from: " + typeElement) } else { customCacheFieldName = element.simpleName.toString() } - } else if (element.getAnnotation(MultiCacheField::class.java) != null) { - ProcessorUtils.ensureVisibleStatic(element, typeElement, "MultiCacheField") + } else if (element.annotation() != null) { + ensureVisibleStatic(element, typeElement, "MultiCacheField") if (!customMultiCacheFieldName.isNullOrEmpty()) { manager.logError("MultiCacheField can only be declared once from: " + typeElement) } else { @@ -345,19 +344,19 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab InternalAdapterHelper.writeGetTableName(typeBuilder, tableName) val getAllColumnPropertiesMethod = FieldSpec.builder( - ArrayTypeName.of(ClassNames.IPROPERTY), "ALL_COLUMN_PROPERTIES", - Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + ArrayTypeName.of(ClassNames.IPROPERTY), "ALL_COLUMN_PROPERTIES", + Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) val getPropertiesBuilder = CodeBlock.builder() val paramColumnName = "columnName" val getPropertyForNameMethod = MethodSpec.methodBuilder("getProperty") - .addAnnotation(Override::class.java) - .addParameter(ClassName.get(String::class.java), paramColumnName) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassNames.PROPERTY) + .addAnnotation(Override::class.java) + .addParameter(ClassName.get(String::class.java), paramColumnName) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .returns(ClassNames.PROPERTY) getPropertyForNameMethod.addStatement("\$L = \$T.quoteIfNeeded(\$L)", paramColumnName, - ClassName.get(QueryBuilder::class.java), paramColumnName) + ClassName.get(QueryBuilder::class.java), paramColumnName) getPropertyForNameMethod.beginControlFlow("switch (\$L) ", paramColumnName) columnDefinitions.indices.forEach { i -> @@ -371,7 +370,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } getPropertyForNameMethod.controlFlow("default:") { addStatement("throw new \$T(\$S)", IllegalArgumentException::class.java, - "Invalid column name passed. Ensure you are calling the correct table's column") + "Invalid column name passed. Ensure you are calling the correct table's column") } getPropertyForNameMethod.endControlFlow() @@ -404,7 +403,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val saveForeignKeyFields = columnDefinitions.filter { (it is ForeignKeyColumnDefinition) && it.saveForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } + .map { it as ForeignKeyColumnDefinition } if (saveForeignKeyFields.isNotEmpty()) { val code = CodeBlock.builder() saveForeignKeyFields.forEach { @@ -413,7 +412,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab typeBuilder.apply { `override fun`(TypeName.VOID, "saveForeignKeys", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { modifiers(public, final) addCode(code.build()) } @@ -421,14 +420,14 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val deleteForeignKeyFields = columnDefinitions.filter { (it is ForeignKeyColumnDefinition) && it.deleteForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } + .map { it as ForeignKeyColumnDefinition } if (deleteForeignKeyFields.isNotEmpty()) { val code = CodeBlock.builder() deleteForeignKeyFields.forEach { it.appendDeleteMethod(code) } typeBuilder.`override fun`(TypeName.VOID, "deleteForeignKeys", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { modifiers(public, final) addCode(code.build()) } @@ -449,21 +448,21 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `override fun`(ClassNames.SINGLE_MODEL_LOADER, "createSingleModelLoader") { modifiers(public, final) addStatement("return new \$T<>(getModelClass())", - if (singlePrimaryKey) - ClassNames.SINGLE_KEY_CACHEABLE_MODEL_LOADER - else - ClassNames.CACHEABLE_MODEL_LOADER) + if (singlePrimaryKey) + ClassNames.SINGLE_KEY_CACHEABLE_MODEL_LOADER + else + ClassNames.CACHEABLE_MODEL_LOADER) } `override fun`(ClassNames.LIST_MODEL_LOADER, "createListModelLoader") { modifiers(public, final) `return`("new \$T<>(getModelClass())", - if (singlePrimaryKey) - ClassNames.SINGLE_KEY_CACHEABLE_LIST_MODEL_LOADER - else - ClassNames.CACHEABLE_LIST_MODEL_LOADER) + if (singlePrimaryKey) + ClassNames.SINGLE_KEY_CACHEABLE_LIST_MODEL_LOADER + else + ClassNames.CACHEABLE_LIST_MODEL_LOADER) } `override fun`(ParameterizedTypeName.get(ClassNames.CACHEABLE_LIST_MODEL_SAVER, elementClassName), - "createListModelSaver") { + "createListModelSaver") { modifiers(protected) `return`("new \$T<>(getModelSaver())", ClassNames.CACHEABLE_LIST_MODEL_SAVER) } @@ -498,7 +497,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (!customCacheFieldName.isNullOrEmpty()) { `override fun`(ParameterizedTypeName.get(ClassNames.MODEL_CACHE, elementClassName, - WildcardTypeName.subtypeOf(Any::class.java)), "createModelCache") { + WildcardTypeName.subtypeOf(Any::class.java)), "createModelCache") { modifiers(public, final) `return`("\$T.\$L", elementClassName, customCacheFieldName) } @@ -506,15 +505,15 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (!customMultiCacheFieldName.isNullOrEmpty()) { `override fun`(ParameterizedTypeName.get(ClassNames.MULTI_KEY_CACHE_CONVERTER, - WildcardTypeName.subtypeOf(Any::class.java)), "getCacheConverter") { + WildcardTypeName.subtypeOf(Any::class.java)), "getCacheConverter") { modifiers(public, final) `return`("\$T.\$L", elementClassName, customMultiCacheFieldName) } } `override fun`(TypeName.VOID, "reloadRelationships", - param(elementClassName!!, ModelUtils.variable), - param(ClassNames.CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { + param(elementClassName!!, ModelUtils.variable), + param(ClassNames.CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { modifiers(public, final) code { val noIndex = AtomicInteger(-1) @@ -533,7 +532,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab typeBuilder.apply { constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { modifiers(public) statement("super(databaseDefinition)") code { @@ -562,7 +561,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } } companion object { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableEndpointDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableEndpointDefinition.kt index ca8f28d2f..3ade6f0cb 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableEndpointDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableEndpointDefinition.kt @@ -4,6 +4,7 @@ import com.raizlabs.android.dbflow.annotation.provider.ContentUri import com.raizlabs.android.dbflow.annotation.provider.Notify import com.raizlabs.android.dbflow.annotation.provider.TableEndpoint import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.raizlabs.android.dbflow.processor.utils.annotation import com.squareup.javapoet.TypeName import javax.lang.model.element.Element import javax.lang.model.element.PackageElement @@ -14,7 +15,7 @@ import javax.lang.model.type.MirroredTypeException * Description: */ class TableEndpointDefinition(typeElement: Element, processorManager: ProcessorManager) -: BaseDefinition(typeElement, processorManager) { + : BaseDefinition(typeElement, processorManager) { var contentUriDefinitions: MutableList = mutableListOf() @@ -34,9 +35,7 @@ class TableEndpointDefinition(typeElement: Element, processorManager: ProcessorM init { - val endpoint = typeElement.getAnnotation(TableEndpoint::class.java) - if (endpoint != null) { - + typeElement.annotation()?.let { endpoint -> tableName = endpoint.name try { @@ -44,21 +43,20 @@ class TableEndpointDefinition(typeElement: Element, processorManager: ProcessorM } catch (mte: MirroredTypeException) { contentProviderName = TypeName.get(mte.typeMirror) } - } isTopLevel = typeElement.enclosingElement is PackageElement val elements = processorManager.elements.getAllMembers(typeElement as TypeElement) for (innerElement in elements) { - if (innerElement.getAnnotation(ContentUri::class.java) != null) { + if (innerElement.annotation() != null) { val contentUriDefinition = ContentUriDefinition(innerElement, processorManager) if (!pathValidationMap.containsKey(contentUriDefinition.path)) { contentUriDefinitions.add(contentUriDefinition) } else { processorManager.logError("There must be unique paths for the specified @ContentUri" + " %1s from %1s", contentUriDefinition.name, contentProviderName) } - } else if (innerElement.getAnnotation(Notify::class.java) != null) { + } else if (innerElement.annotation() != null) { val notifyDefinition = NotifyDefinition(innerElement, processorManager) for (path in notifyDefinition.paths) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TypeConverterDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TypeConverterDefinition.kt index 0b1955df5..0f4e698c0 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TypeConverterDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TypeConverterDefinition.kt @@ -3,6 +3,7 @@ package com.raizlabs.android.dbflow.processor.definition import com.raizlabs.android.dbflow.annotation.TypeConverter import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.raizlabs.android.dbflow.processor.utils.annotation import com.squareup.javapoet.ClassName import com.squareup.javapoet.TypeName import javax.lang.model.element.TypeElement @@ -27,8 +28,7 @@ class TypeConverterDefinition(val className: ClassName, init { - val annotation = typeElement?.getAnnotation(TypeConverter::class.java) - if (annotation != null) { + typeElement.annotation()?.let { annotation -> val allowedSubTypes: MutableList = mutableListOf() try { annotation.allowedSubtypes; diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index 742f54e87..7044e783d 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt @@ -4,11 +4,13 @@ import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.data.Blob import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.ProcessorUtils import com.raizlabs.android.dbflow.processor.definition.BaseDefinition import com.raizlabs.android.dbflow.processor.definition.BaseTableDefinition import com.raizlabs.android.dbflow.processor.definition.TableDefinition import com.raizlabs.android.dbflow.processor.definition.TypeConverterDefinition +import com.raizlabs.android.dbflow.processor.fromTypeMirror +import com.raizlabs.android.dbflow.processor.getTypeElement +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.* @@ -26,8 +28,8 @@ import javax.tools.Diagnostic open class ColumnDefinition @JvmOverloads constructor(processorManager: ProcessorManager, element: Element, var baseTableDefinition: BaseTableDefinition, isPackagePrivate: Boolean, - var column: Column? = element.getAnnotation(Column::class.java), - primaryKey: PrimaryKey? = element.getAnnotation(PrimaryKey::class.java)) + var column: Column? = element.annotation(), + primaryKey: PrimaryKey? = element.annotation()) : BaseDefinition(element, processorManager) { private val QUOTE_PATTERN = Pattern.compile("\".*\"") @@ -78,8 +80,7 @@ constructor(processorManager: ProcessorManager, element: Element, get() = QueryBuilder.quote(columnName) init { - val notNullAnno = element.getAnnotation(NotNull::class.java) - if (notNullAnno != null) { + element.annotation()?.let { notNullAnno -> notNull = true onNullConflict = notNullAnno.onNullConflict } @@ -98,8 +99,8 @@ constructor(processorManager: ProcessorManager, element: Element, } if (defaultValue != null - && elementClassName == ClassName.get(String::class.java) - && !QUOTE_PATTERN.matcher(defaultValue).find()) { + && elementClassName == ClassName.get(String::class.java) + && !QUOTE_PATTERN.matcher(defaultValue).find()) { defaultValue = "\"" + defaultValue + "\"" } } @@ -109,19 +110,19 @@ constructor(processorManager: ProcessorManager, element: Element, if (isPackagePrivate) { columnAccessor = PackagePrivateScopeColumnAccessor(elementName, packageName, - baseTableDefinition.databaseDefinition?.classSeparator, - ClassName.get(element.enclosingElement as TypeElement).simpleName()) + baseTableDefinition.databaseDefinition?.classSeparator, + ClassName.get(element.enclosingElement as TypeElement).simpleName()) PackagePrivateScopeColumnAccessor.putElement( - (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, - columnName) + (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, + columnName) } else { val isPrivate = element.modifiers.contains(Modifier.PRIVATE) if (isPrivate) { val isBoolean = elementTypeName?.box() == TypeName.BOOLEAN.box() val useIs = isBoolean - && baseTableDefinition is TableDefinition && (baseTableDefinition as TableDefinition).useIsForPrivateBooleans + && baseTableDefinition is TableDefinition && (baseTableDefinition as TableDefinition).useIsForPrivateBooleans columnAccessor = PrivateScopeColumnAccessor(elementName, object : GetterSetter { override val getterName: String = column?.getterName ?: "" override val setterName: String = column?.setterName ?: "" @@ -144,17 +145,15 @@ constructor(processorManager: ProcessorManager, element: Element, } } - val uniqueColumn = element.getAnnotation(Unique::class.java) - if (uniqueColumn != null) { + element.annotation()?.let { uniqueColumn -> unique = uniqueColumn.unique onUniqueConflict = uniqueColumn.onUniqueConflict uniqueColumn.uniqueGroups.forEach { uniqueGroups.add(it) } } - val index = element.getAnnotation(Index::class.java) - if (index != null) { + element.annotation()?.let { index -> // empty index, we assume generic - if (index.indexGroups.size == 0) { + if (index.indexGroups.isEmpty()) { indexGroups.add(IndexGroup.GENERIC) } else { index.indexGroups.forEach { indexGroups.add(it) } @@ -167,18 +166,18 @@ constructor(processorManager: ProcessorManager, element: Element, column?.typeConverter } catch (mte: MirroredTypeException) { typeMirror = mte.typeMirror - typeConverterClassName = ProcessorUtils.fromTypeMirror(typeMirror, manager) + typeConverterClassName = fromTypeMirror(typeMirror, manager) } hasCustomConverter = false if (typeConverterClassName != null && typeMirror != null && - typeConverterClassName != ClassNames.TYPE_CONVERTER) { + typeConverterClassName != ClassNames.TYPE_CONVERTER) { typeConverterDefinition = TypeConverterDefinition(typeConverterClassName, typeMirror, manager) evaluateTypeConverter(typeConverterDefinition, true) } if (!hasCustomConverter) { - val typeElement = ProcessorUtils.getTypeElement(element) + val typeElement = getTypeElement(element) if (typeElement != null && typeElement.kind == ElementKind.ENUM) { wrapperAccessor = EnumColumnAccessor(elementTypeName!!) wrapperTypeName = ClassName.get(String::class.java) @@ -190,7 +189,7 @@ constructor(processorManager: ProcessorManager, element: Element, // do nothing, for now. } else if (elementTypeName is ArrayTypeName) { processorManager.messager.printMessage(Diagnostic.Kind.ERROR, - "Columns cannot be of array type.") + "Columns cannot be of array type.") } else { if (elementTypeName == TypeName.BOOLEAN) { wrapperAccessor = BooleanColumnAccessor() @@ -210,7 +209,7 @@ constructor(processorManager: ProcessorManager, element: Element, } combiner = Combiner(columnAccessor, elementTypeName!!, wrapperAccessor, wrapperTypeName, - subWrapperAccessor) + subWrapperAccessor) } private fun evaluateTypeConverter(typeConverterDefinition: TypeConverterDefinition?, @@ -220,7 +219,7 @@ constructor(processorManager: ProcessorManager, element: Element, if (it.modelTypeName != elementTypeName) { manager.logError("The specified custom TypeConverter's Model Value ${it.modelTypeName}" + - " from ${it.className} must match the type of the column $elementTypeName. ") + " from ${it.className} must match the type of the column $elementTypeName. ") } else { hasTypeConverter = true hasCustomConverter = isCustom @@ -259,21 +258,21 @@ constructor(processorManager: ProcessorManager, element: Element, } val fieldBuilder = FieldSpec.builder(propParam, - columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) if (isNonPrimitiveTypeConverter) { val codeBlock = CodeBlock.builder() codeBlock.add("new \$T(\$T.class, \$S, true,", propParam, tableClass, columnName) codeBlock.add("\nnew \$T() {" + - "\n@Override" + - "\npublic \$T getTypeConverter(Class modelClass) {" + - "\n \$T adapter = (\$T) \$T.getInstanceAdapter(modelClass);" + - "\nreturn adapter.\$L;" + - "\n}" + - "\n})", ClassNames.TYPE_CONVERTER_GETTER, ClassNames.TYPE_CONVERTER, - baseTableDefinition.outputClassName, baseTableDefinition.outputClassName, - ClassNames.FLOW_MANAGER, - (wrapperAccessor as TypeConverterScopeColumnAccessor).typeConverterFieldName) + "\n@Override" + + "\npublic \$T getTypeConverter(Class modelClass) {" + + "\n \$T adapter = (\$T) \$T.getInstanceAdapter(modelClass);" + + "\nreturn adapter.\$L;" + + "\n}" + + "\n})", ClassNames.TYPE_CONVERTER_GETTER, ClassNames.TYPE_CONVERTER, + baseTableDefinition.outputClassName, baseTableDefinition.outputClassName, + ClassNames.FLOW_MANAGER, + (wrapperAccessor as TypeConverterScopeColumnAccessor).typeConverterFieldName) fieldBuilder.initializer(codeBlock.build()) } else { fieldBuilder.initializer("new \$T(\$T.class, \$S)", propParam, tableClass, columnName) @@ -304,7 +303,7 @@ constructor(processorManager: ProcessorManager, element: Element, val code = CodeBlock.builder() ContentValuesCombiner(combiner) - .addCode(code, columnName, getDefaultValueBlock(), 0, modelBlock) + .addCode(code, columnName, getDefaultValueBlock(), 0, modelBlock) return code.build() } @@ -321,7 +320,7 @@ constructor(processorManager: ProcessorManager, element: Element, val builder = CodeBlock.builder() SqliteStatementAccessCombiner(combiner) - .addCode(builder, "start", getDefaultValueBlock(), index.get(), modelBlock) + .addCode(builder, "start", getDefaultValueBlock(), index.get(), modelBlock) return builder.build() } @@ -329,9 +328,9 @@ constructor(processorManager: ProcessorManager, element: Element, val builder = CodeBlock.builder() LoadFromCursorAccessCombiner(combiner, - baseTableDefinition.orderedCursorLookUp, - baseTableDefinition.assignDefaultValuesFromCursor) - .addCode(builder, columnName, getDefaultValueBlock(), index.get(), modelBlock) + baseTableDefinition.orderedCursorLookUp, + baseTableDefinition.assignDefaultValuesFromCursor) + .addCode(builder, columnName, getDefaultValueBlock(), index.get(), modelBlock) return builder.build() } @@ -344,35 +343,35 @@ constructor(processorManager: ProcessorManager, element: Element, get() { val code = CodeBlock.builder() UpdateAutoIncrementAccessCombiner(combiner) - .addCode(code, columnName, getDefaultValueBlock(), - 0, modelBlock) + .addCode(code, columnName, getDefaultValueBlock(), + 0, modelBlock) return code.build() } fun getColumnAccessString(index: Int): CodeBlock { val codeBlock = CodeBlock.builder() CachingIdAccessCombiner(combiner) - .addCode(codeBlock, columnName, getDefaultValueBlock(), index, modelBlock) + .addCode(codeBlock, columnName, getDefaultValueBlock(), index, modelBlock) return codeBlock.build() } fun getSimpleAccessString(): CodeBlock { val codeBlock = CodeBlock.builder() SimpleAccessCombiner(combiner) - .addCode(codeBlock, columnName, getDefaultValueBlock(), 0, modelBlock) + .addCode(codeBlock, columnName, getDefaultValueBlock(), 0, modelBlock) return codeBlock.build() } open fun appendExistenceMethod(codeBuilder: CodeBlock.Builder) { ExistenceAccessCombiner(combiner, isRowId || isPrimaryKeyAutoIncrement, - isQuickCheckPrimaryKeyAutoIncrement, baseTableDefinition.elementClassName!!) - .addCode(codeBuilder, columnName, getDefaultValueBlock(), 0, modelBlock) + isQuickCheckPrimaryKeyAutoIncrement, baseTableDefinition.elementClassName!!) + .addCode(codeBuilder, columnName, getDefaultValueBlock(), 0, modelBlock) } open fun appendPropertyComparisonAccessStatement(codeBuilder: CodeBlock.Builder) { PrimaryReferenceAccessCombiner(combiner) - .addCode(codeBuilder, columnName, getDefaultValueBlock(), - 0, modelBlock) + .addCode(codeBuilder, columnName, getDefaultValueBlock(), + 0, modelBlock) } open val creationName: CodeBlock @@ -383,9 +382,9 @@ constructor(processorManager: ProcessorManager, element: Element, codeBlockBuilder.add(" PRIMARY KEY ") if (baseTableDefinition is TableDefinition && - !(baseTableDefinition as TableDefinition).primaryKeyConflictActionName.isNullOrEmpty()) { + !(baseTableDefinition as TableDefinition).primaryKeyConflictActionName.isNullOrEmpty()) { codeBlockBuilder.add("ON CONFLICT \$L ", - (baseTableDefinition as TableDefinition).primaryKeyConflictActionName) + (baseTableDefinition as TableDefinition).primaryKeyConflictActionName) } codeBlockBuilder.add("AUTOINCREMENT") @@ -422,8 +421,8 @@ constructor(processorManager: ProcessorManager, element: Element, if (elementTypeName == TypeName.BOOLEAN) { defaultValue = "false" } else if (elementTypeName == TypeName.BYTE || elementTypeName == TypeName.INT - || elementTypeName == TypeName.DOUBLE || elementTypeName == TypeName.FLOAT - || elementTypeName == TypeName.LONG || elementTypeName == TypeName.SHORT) { + || elementTypeName == TypeName.DOUBLE || elementTypeName == TypeName.FLOAT + || elementTypeName == TypeName.LONG || elementTypeName == TypeName.SHORT) { defaultValue = "($elementTypeName) 0" } else if (elementTypeName == TypeName.CHAR) { defaultValue = "'\\u0000'" diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt index a6b5719b7..1de4bd7e1 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt @@ -1,13 +1,15 @@ package com.raizlabs.android.dbflow.processor.definition.column +import com.grosner.kpoet.S +import com.grosner.kpoet.`return` +import com.grosner.kpoet.case import com.raizlabs.android.dbflow.annotation.ForeignKey import com.raizlabs.android.dbflow.annotation.ForeignKeyAction import com.raizlabs.android.dbflow.annotation.ForeignKeyReference import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.processor.ClassNames -import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.ProcessorUtils +import com.raizlabs.android.dbflow.processor.* import com.raizlabs.android.dbflow.processor.definition.TableDefinition +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.raizlabs.android.dbflow.processor.utils.toTypeElement import com.raizlabs.android.dbflow.processor.utils.toTypeErasedElement @@ -30,14 +32,14 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab var referencedTableClassName: ClassName? = null - var onDelete: ForeignKeyAction - var onUpdate: ForeignKeyAction + var onDelete = ForeignKeyAction.NO_ACTION + var onUpdate = ForeignKeyAction.NO_ACTION var isStubbedRelationship: Boolean = false var isReferencingTableObject: Boolean = false - var implementsModel: Boolean - var extendsBaseModel: Boolean + var implementsModel = false + var extendsBaseModel = false var references: List? = null @@ -53,49 +55,50 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab init { - val foreignKey = element.getAnnotation(ForeignKey::class.java) - onUpdate = foreignKey.onUpdate - onDelete = foreignKey.onDelete + element.annotation()?.let { foreignKey -> + onUpdate = foreignKey.onUpdate + onDelete = foreignKey.onDelete - isStubbedRelationship = foreignKey.stubbedRelationship + isStubbedRelationship = foreignKey.stubbedRelationship - try { - foreignKey.tableClass - } catch (mte: MirroredTypeException) { - referencedTableClassName = ProcessorUtils.fromTypeMirror(mte.typeMirror, manager) - } + try { + foreignKey.tableClass + } catch (mte: MirroredTypeException) { + referencedTableClassName = fromTypeMirror(mte.typeMirror, manager) + } - val erasedElement = element.toTypeErasedElement() + val erasedElement = element.toTypeErasedElement() - // hopefully intentionally left blank - if (referencedTableClassName == TypeName.OBJECT) { - if (elementTypeName is ParameterizedTypeName) { - val args = (elementTypeName as ParameterizedTypeName).typeArguments - if (args.size > 0) { - referencedTableClassName = ClassName.get(args[0].toTypeElement(manager)) - } - } else { - if (referencedTableClassName == null || referencedTableClassName == ClassName.OBJECT) { - referencedTableClassName = ClassName.get(elementTypeName.toTypeElement()) + // hopefully intentionally left blank + if (referencedTableClassName == TypeName.OBJECT) { + if (elementTypeName is ParameterizedTypeName) { + val args = (elementTypeName as ParameterizedTypeName).typeArguments + if (args.size > 0) { + referencedTableClassName = ClassName.get(args[0].toTypeElement(manager)) + } + } else { + if (referencedTableClassName == null || referencedTableClassName == ClassName.OBJECT) { + referencedTableClassName = ClassName.get(elementTypeName.toTypeElement()) + } } } - } - if (referencedTableClassName == null) { - manager.logError("Referenced was null for $element within $elementTypeName") - } + if (referencedTableClassName == null) { + manager.logError("Referenced was null for $element within $elementTypeName") + } - extendsBaseModel = ProcessorUtils.isSubclass(manager.processingEnvironment, - ClassNames.BASE_MODEL.toString(), erasedElement) - implementsModel = ProcessorUtils.implementsClass(manager.processingEnvironment, ClassNames.MODEL.toString(), erasedElement) - isReferencingTableObject = implementsModel || erasedElement?.getAnnotation(Table::class.java) != null + extendsBaseModel = isSubclass(manager.processingEnvironment, + ClassNames.BASE_MODEL.toString(), erasedElement) + implementsModel = erasedElement.implementsClass(manager.processingEnvironment, ClassNames.MODEL) + isReferencingTableObject = implementsModel || erasedElement.annotation
() != null - nonModelColumn = !isReferencingTableObject + nonModelColumn = !isReferencingTableObject - saveForeignKeyModel = foreignKey.saveForeignKeyModel - deleteForeignKeyModel = foreignKey.deleteForeignKeyModel + saveForeignKeyModel = foreignKey.saveForeignKeyModel + deleteForeignKeyModel = foreignKey.deleteForeignKeyModel - references = foreignKey.references.asList() + references = foreignKey.references.asList() + } } override fun addPropertyDefinition(typeBuilder: TypeSpec.Builder, tableClass: TypeName) { @@ -108,7 +111,7 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab } if (it.columnName.isNullOrEmpty()) { manager.logError("Found empty reference name at ${it.foreignColumnName}" + - " from tablet ${baseTableDefinition.elementName}") + " from table ${baseTableDefinition.elementName}") } typeBuilder.addField(FieldSpec.builder(propParam, it.columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) @@ -120,9 +123,9 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab override fun addPropertyCase(methodBuilder: MethodSpec.Builder) { checkNeedsReferences() _foreignKeyReferenceDefinitionList.forEach { - methodBuilder.beginControlFlow("case \$S: ", QueryBuilder.quoteIfNeeded(it.columnName)) - methodBuilder.addStatement("return \$L", it.columnName) - methodBuilder.endControlFlow() + methodBuilder.case(QueryBuilder.quoteIfNeeded(it.columnName).S) { + `return`(it.columnName) + } } } @@ -161,7 +164,7 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab builder.add(",") } val referenceDefinition = _foreignKeyReferenceDefinitionList[i] - builder.add("\$L", QueryBuilder.quote(referenceDefinition.columnName)) + builder.add(QueryBuilder.quote(referenceDefinition.columnName)) } return builder.build() } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt index 8ea9542db..6560ecdb3 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt @@ -25,4 +25,6 @@ fun TypeMirror?.erasure(manager: ProcessorManager = ProcessorManager.manager): T // TypeName -fun TypeName?.toTypeElement(manager: ProcessorManager = ProcessorManager.manager): TypeElement? = manager.elements.getTypeElement(toString()) \ No newline at end of file +fun TypeName?.toTypeElement(manager: ProcessorManager = ProcessorManager.manager): TypeElement? = manager.elements.getTypeElement(toString()) + +inline fun Element?.annotation() = this?.getAnnotation(T::class.java) \ No newline at end of file diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementUtility.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementUtility.kt index cbfe28abb..c79b8aa50 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementUtility.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementUtility.kt @@ -45,7 +45,7 @@ object ElementUtility { return allFields && element.kind.isField && !element.modifiers.contains(Modifier.STATIC) && !element.modifiers.contains(Modifier.FINAL) && - element.getAnnotation(ColumnIgnore::class.java) == null && + element.annotation() == null && element.asType().toString() != ClassNames.MODEL_ADAPTER.toString() } From d079540bb51474067b319d94769d6ca8ec0e524d Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Fri, 14 Apr 2017 11:21:01 +1000 Subject: [PATCH 02/37] [processor] more code consolidation. replace java string replace with Kotlin version. --- .../android/dbflow/processor/Handlers.kt | 4 +- .../android/dbflow/processor/Validators.kt | 77 ++++++++++--------- .../definition/OneToManyDefinition.kt | 31 ++++---- .../definition/QueryModelDefinition.kt | 5 +- .../definition/column/ColumnAccessor.kt | 37 ++++----- .../definition/column/ColumnDefinition.kt | 4 +- .../column/ForeignKeyColumnDefinition.kt | 8 +- .../processor/{ => utils}/ProcessorUtils.kt | 26 +++---- 8 files changed, 92 insertions(+), 100 deletions(-) rename dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/{ => utils}/ProcessorUtils.kt (81%) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt index 158205827..c49810d7a 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt @@ -10,7 +10,6 @@ import com.raizlabs.android.dbflow.processor.definition.* import com.raizlabs.android.dbflow.processor.utils.annotation import javax.annotation.processing.RoundEnvironment import javax.lang.model.element.Element -import javax.lang.model.element.Modifier import javax.lang.model.element.PackageElement import javax.lang.model.element.TypeElement @@ -136,7 +135,7 @@ class TypeConverterHandler : BaseContainerHandler() { override fun onProcessElement(processorManager: ProcessorManager, element: Element) { if (element is TypeElement) { - val className = fromTypeMirror(element.asType(), processorManager) + val className = com.raizlabs.android.dbflow.processor.utils.fromTypeMirror(element.asType(), processorManager) val converterDefinition = className?.let { TypeConverterDefinition(it, element.asType(), processorManager, element) } converterDefinition?.let { if (VALIDATOR.validate(processorManager, converterDefinition)) { @@ -214,7 +213,6 @@ class DatabaseHandler : BaseContainerHandler() { companion object { val TYPE_CONVERTER_MAP_FIELD_NAME = "typeConverters" - val METHOD_MODIFIERS: Set = Sets.newHashSet(Modifier.PUBLIC, Modifier.FINAL) val MODEL_ADAPTER_MAP_FIELD_NAME = "modelAdapters" val QUERY_MODEL_ADAPTER_MAP_FIELD_NAME = "queryModelAdapterMap" val MIGRATION_FIELD_NAME = "migrationMap" diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Validators.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Validators.kt index 9095508a5..6ad56f0ad 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Validators.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Validators.kt @@ -39,18 +39,18 @@ class ColumnValidator : Validator { // validate getter and setters. if (validatorDefinition.columnAccessor is PrivateScopeColumnAccessor) { val privateColumnAccess = validatorDefinition.columnAccessor as PrivateScopeColumnAccessor - if (!validatorDefinition.baseTableDefinition.classElementLookUpMap.containsKey(privateColumnAccess.getGetterNameElement())) { + if (!validatorDefinition.baseTableDefinition.classElementLookUpMap.containsKey(privateColumnAccess.getterNameElement)) { processorManager.logError(ColumnValidator::class, "Could not find getter for private element: " + "\"%1s\" from table class: %1s. Consider adding a getter with name %1s or making it more accessible.", validatorDefinition.elementName, validatorDefinition.baseTableDefinition.elementName, - privateColumnAccess.getGetterNameElement()) + privateColumnAccess.getterNameElement) success = false } - if (!validatorDefinition.baseTableDefinition.classElementLookUpMap.containsKey(privateColumnAccess.getSetterNameElement())) { + if (!validatorDefinition.baseTableDefinition.classElementLookUpMap.containsKey(privateColumnAccess.setterNameElement)) { processorManager.logError(ColumnValidator::class, - "Could not find setter for private element: " + "\"%1s\" from table class: %1s. Consider adding a setter with name %1s or making it more accessible.", - validatorDefinition.elementName, validatorDefinition.baseTableDefinition.elementName, - privateColumnAccess.getSetterNameElement()) + "Could not find setter for private element: \"${validatorDefinition.elementName}\"" + + " from table class: ${validatorDefinition.baseTableDefinition.elementName}. " + + "Consider adding a setter with name ${privateColumnAccess.setterNameElement} or making it more accessible.") success = false } } @@ -74,23 +74,22 @@ class ColumnValidator : Validator { if (validatorDefinition.columnAccessor is EnumColumnAccessor) { if (validatorDefinition.isPrimaryKey) { success = false - processorManager.logError("Enums cannot be primary keys. Column: %1s and type: %1s", validatorDefinition.columnName, - validatorDefinition.elementTypeName) + processorManager.logError("Enums cannot be primary keys. Column: ${validatorDefinition.columnName}" + + " and type: ${validatorDefinition.elementTypeName}") } else if (validatorDefinition is ForeignKeyColumnDefinition) { success = false - processorManager.logError("Enums cannot be foreign keys. Column: %1s and type: %1s", validatorDefinition.columnName, - validatorDefinition.elementTypeName) + processorManager.logError("Enums cannot be foreign keys. Column: ${validatorDefinition.columnName}" + + " and type: ${validatorDefinition.elementTypeName}") } } if (validatorDefinition is ForeignKeyColumnDefinition) { validatorDefinition.column?.let { - if (it.name.length > 0) { + if (it.name.isNotEmpty()) { success = false - processorManager.logError("Foreign Key %1s cannot specify the column() field. " - + "Use a @ForeignKeyReference(columnName = {NAME} instead. Column: %1s and type: %1s", - validatorDefinition.elementName, validatorDefinition.columnName, - validatorDefinition.elementTypeName) + processorManager.logError("Foreign Key ${validatorDefinition.elementName} cannot specify the column() field. " + + "Use a @ForeignKeyReference(columnName = {NAME} instead. " + + "Column: ${validatorDefinition.columnName} and type: ${validatorDefinition.elementTypeName}") } } @@ -104,9 +103,8 @@ class ColumnValidator : Validator { if (autoIncrementingPrimaryKey == null) { autoIncrementingPrimaryKey = validatorDefinition } else if (autoIncrementingPrimaryKey != validatorDefinition) { - processorManager.logError( - "Only one autoincrementing primary key is allowed on a table. Found Column: %1s and type: %1s", - validatorDefinition.columnName, validatorDefinition.elementTypeName) + processorManager.logError("Only one auto-incrementing primary key is allowed on a table. " + + "Found Column: ${validatorDefinition.columnName} and type: ${validatorDefinition.elementTypeName}") success = false } } @@ -125,8 +123,8 @@ class ContentProviderValidator : Validator { var success = true if (validatorDefinition.endpointDefinitions.isEmpty()) { - processorManager.logError("The content provider %1s must have at least 1 @TableEndpoint associated with it", - validatorDefinition.element.simpleName) + processorManager.logError("The content provider ${validatorDefinition.element.simpleName} " + + "must have at least 1 @TableEndpoint associated with it") success = false } @@ -163,7 +161,8 @@ class TableEndpointValidator : Validator { var success = true if (validatorDefinition.contentUriDefinitions.isEmpty()) { - processorManager.logError("A table endpoint %1s must supply at least one @ContentUri", validatorDefinition.elementClassName) + processorManager.logError("A table endpoint ${validatorDefinition.elementClassName} " + + "must supply at least one @ContentUri") success = false } @@ -180,31 +179,35 @@ class TableValidator : Validator { var success = true if (!validatorDefinition.hasPrimaryConstructor) { - processorManager.logError(TableValidator::class, "Table ${ - validatorDefinition.elementClassName}" + + processorManager.logError(TableValidator::class, "Table ${validatorDefinition.elementClassName}" + " must provide a visible, default constructor.") success = false } if (validatorDefinition.columnDefinitions.isEmpty()) { - processorManager.logError(TableValidator::class, "Table %1s of %1s, %1s needs to define at least one column", validatorDefinition.tableName, - validatorDefinition.elementClassName, validatorDefinition.element.javaClass) + processorManager.logError(TableValidator::class, "Table ${validatorDefinition.tableName} " + + "of ${validatorDefinition.elementClassName}, ${validatorDefinition.element.javaClass} " + + "needs to define at least one column") success = false } - val hasTwoKinds = (validatorDefinition.hasAutoIncrement || validatorDefinition.hasRowID) && !validatorDefinition._primaryColumnDefinitions.isEmpty() + val hasTwoKinds = (validatorDefinition.hasAutoIncrement || validatorDefinition.hasRowID) + && !validatorDefinition._primaryColumnDefinitions.isEmpty() if (hasTwoKinds) { - processorManager.logError(TableValidator::class, "Table %1s cannot mix and match autoincrement and composite primary keys", - validatorDefinition.tableName) + processorManager.logError(TableValidator::class, "Table ${validatorDefinition.tableName}" + + " cannot mix and match autoincrement and composite primary keys") success = false } - val hasPrimary = (validatorDefinition.hasAutoIncrement || validatorDefinition.hasRowID) && validatorDefinition._primaryColumnDefinitions.isEmpty() - || !validatorDefinition.hasAutoIncrement && !validatorDefinition.hasRowID && !validatorDefinition._primaryColumnDefinitions.isEmpty() + val hasPrimary = (validatorDefinition.hasAutoIncrement || validatorDefinition.hasRowID) + && validatorDefinition._primaryColumnDefinitions.isEmpty() + || !validatorDefinition.hasAutoIncrement && !validatorDefinition.hasRowID + && !validatorDefinition._primaryColumnDefinitions.isEmpty() if (!hasPrimary) { - processorManager.logError(TableValidator::class, "Table %1s needs to define at least one primary key", validatorDefinition.tableName) + processorManager.logError(TableValidator::class, "Table ${validatorDefinition.tableName} " + + "needs to define at least one primary key") success = false } @@ -218,14 +221,14 @@ class TypeConverterValidator : Validator { var success = true if (validatorDefinition.modelTypeName == null) { - processorManager.logError("TypeConverter: " + validatorDefinition.className.toString() + - " uses an unsupported Model Element parameter. If it has type parameters, you must remove them or subclass it" + - "for proper usage.") + processorManager.logError("TypeConverter: ${validatorDefinition.className} uses an " + + "unsupported Model Element parameter. If it has type parameters, you must " + + "remove them or subclass it for proper usage.") success = false } else if (validatorDefinition.dbTypeName == null) { - processorManager.logError("TypeConverter: " + validatorDefinition.className.toString() + - " uses an unsupported DB Element parameter. If it has type parameters, you must remove them or subclass it " + - "for proper usage.") + processorManager.logError("TypeConverter: ${validatorDefinition.className} uses an " + + "unsupported DB Element parameter. If it has type parameters, you must remove" + + " them or subclass it for proper usage.") success = false } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 2ca294d79..7829edccd 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -1,17 +1,20 @@ package com.raizlabs.android.dbflow.processor.definition import com.google.common.collect.Lists +import com.grosner.kpoet.`for` +import com.grosner.kpoet.`if` +import com.grosner.kpoet.end +import com.grosner.kpoet.statement import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.* -import com.raizlabs.android.dbflow.processor.isSubclass +import com.raizlabs.android.dbflow.processor.utils.isSubclass import com.raizlabs.android.dbflow.processor.utils.ModelUtils import com.raizlabs.android.dbflow.processor.utils.addStatement import com.raizlabs.android.dbflow.processor.utils.annotation -import com.raizlabs.android.dbflow.processor.utils.controlFlow +import com.raizlabs.android.dbflow.processor.utils.toTypeElement import com.squareup.javapoet.* -import java.util.* import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement @@ -55,7 +58,7 @@ class OneToManyDefinition(typeElement: ExecutableElement, _variableName = _methodName.replace("get", "") _variableName = _variableName.substring(0, 1).toLowerCase() + _variableName.substring(1) } - methods.addAll(Arrays.asList(*oneToMany.methods)) + methods.addAll(oneToMany.methods) if (oneToMany.isVariablePrivate) { columnAccessor = PrivateScopeColumnAccessor(_variableName, object : GetterSetter { @@ -78,11 +81,9 @@ class OneToManyDefinition(typeElement: ExecutableElement, } referencedTableType = refTableType - referencedType = manager.elements.getTypeElement(referencedTableType?.toString()) - extendsBaseModel = isSubclass(manager.processingEnvironment, - ClassNames.BASE_MODEL.toString(), referencedType) - extendsModel = isSubclass(manager.processingEnvironment, - ClassNames.MODEL.toString(), referencedType) + referencedType = referencedTableType.toTypeElement(manager) + extendsBaseModel = referencedType.isSubclass(manager.processingEnvironment, ClassNames.BASE_MODEL) + extendsModel = referencedType.isSubclass(manager.processingEnvironment, ClassNames.MODEL) } } } @@ -126,24 +127,24 @@ class OneToManyDefinition(typeElement: ExecutableElement, private fun writeLoopWithMethod(codeBuilder: CodeBlock.Builder, methodName: String, useWrapper: Boolean) { val oneToManyMethodName = this@OneToManyDefinition.methodName codeBuilder.apply { - codeBuilder.controlFlow("if (\$L != null) ", oneToManyMethodName) { + codeBuilder.`if`("($oneToManyMethodName != null)") { val loopClass: ClassName? = if (extendsBaseModel) ClassNames.BASE_MODEL else ClassName.get(referencedType) // need to load adapter for non-model classes if (!extendsModel) { - addStatement("\$T adapter = \$T.getModelAdapter(\$T.class)", + statement("\$T adapter = \$T.getModelAdapter(\$T.class)", ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), ClassNames.FLOW_MANAGER, referencedTableType) - addStatement("adapter.\$LAll(\$L\$L)", methodName, oneToManyMethodName, + statement("adapter.${methodName}All($oneToManyMethodName\$L)", if (useWrapper) ", " + ModelUtils.wrapper else "") } else { - controlFlow("for (\$T value: \$L) ", loopClass, oneToManyMethodName) { - codeBuilder.addStatement("value.\$L(\$L)", methodName, if (useWrapper) ModelUtils.wrapper else "") + `for`("(\$T value: $oneToManyMethodName)", loopClass) { + statement("value.$methodName(\$L)", if (useWrapper) ModelUtils.wrapper else "") } } - } + }.end() } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index 79c93c322..d950b8969 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -7,7 +7,7 @@ import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ColumnValidator import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.raizlabs.android.dbflow.processor.implementsClass +import com.raizlabs.android.dbflow.processor.utils.implementsClass import com.raizlabs.android.dbflow.processor.utils.ElementUtility import com.raizlabs.android.dbflow.processor.utils.`override fun` import com.raizlabs.android.dbflow.processor.utils.annotation @@ -60,8 +60,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana columnDefinitions.clear() packagePrivateList.clear() - val queryModel = typeElement?.getAnnotation(QueryModel::class.java) - if (queryModel != null) { + typeElement.annotation()?.let { queryModel -> databaseDefinition = manager.getDatabaseHolderDefinition(databaseTypeName)?.databaseDefinition setOutputClassName(databaseDefinition?.classSeparator + DBFLOW_QUERY_MODEL_TAG) allFields = queryModel.allFields diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt index fe8b08bec..a66b73208 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt @@ -1,6 +1,7 @@ package com.raizlabs.android.dbflow.processor.definition.column import com.google.common.collect.Maps +import com.grosner.kpoet.code import com.raizlabs.android.dbflow.data.Blob import com.raizlabs.android.dbflow.processor.utils.capitalizeFirstLetter import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty @@ -79,23 +80,19 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette private var getterName: String = "" private var setterName: String = "" - override fun get(existingBlock: CodeBlock?): CodeBlock { - val codeBlock: CodeBlock.Builder = CodeBlock.builder() - existingBlock?.let { codeBlock.add("\$L.", existingBlock) } - return codeBlock.add("\$L()", getGetterNameElement()) - .build() + override fun get(existingBlock: CodeBlock?) = code { + existingBlock?.let { this.add("$existingBlock.") } + add("$getterNameElement()") } override fun set(existingBlock: CodeBlock?, baseVariableName: CodeBlock?, - isDefault: Boolean): CodeBlock { - val codeBlock: CodeBlock.Builder = CodeBlock.builder() - baseVariableName?.let { codeBlock.add("\$L.", baseVariableName) } - return codeBlock.add("\$L(\$L)", getSetterNameElement(), existingBlock) - .build() + isDefault: Boolean) = code { + baseVariableName?.let { add("$baseVariableName.") } + add("$setterNameElement($existingBlock)") } - fun getGetterNameElement(): String { - return if (getterName.isNullOrEmpty()) { + val getterNameElement: String + get() = if (getterName.isNullOrEmpty()) { if (propertyName != null) { if (useIsForPrivateBooleans && !propertyName.startsWith("is", ignoreCase = true)) { "is" + propertyName.capitalizeFirstLetter() @@ -106,12 +103,11 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette "" } } else getterName - } - fun getSetterNameElement(): String { - if (propertyName != null) { + val setterNameElement: String + get() = if (propertyName != null) { var setElementName = propertyName - return if (setterName.isNullOrEmpty()) { + if (setterName.isNullOrEmpty()) { if (!setElementName.startsWith("set", ignoreCase = true)) { if (useIsForPrivateBooleans && setElementName.startsWith("is")) { setElementName = setElementName.replaceFirst("is".toRegex(), "") @@ -121,8 +117,7 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette "set" + setElementName.capitalizeFirstLetter() } else setElementName.lower() } else setterName - } else return "" - } + } else "" init { getterSetter?.let { @@ -134,7 +129,7 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette class PackagePrivateScopeColumnAccessor( propertyName: String, packageName: String, separator: String?, tableClassName: String) -: ColumnAccessor(propertyName) { + : ColumnAccessor(propertyName) { val helperClassName: ClassName val internalHelperClassName: ClassName @@ -186,7 +181,7 @@ class PackagePrivateScopeColumnAccessor( class TypeConverterScopeColumnAccessor(val typeConverterFieldName: String, propertyName: String? = null) -: ColumnAccessor(propertyName) { + : ColumnAccessor(propertyName) { override fun get(existingBlock: CodeBlock?): CodeBlock { val codeBlock = CodeBlock.builder() @@ -209,7 +204,7 @@ class TypeConverterScopeColumnAccessor(val typeConverterFieldName: String, class EnumColumnAccessor(val propertyTypeName: TypeName, propertyName: String? = null) -: ColumnAccessor(propertyName) { + : ColumnAccessor(propertyName) { override fun get(existingBlock: CodeBlock?): CodeBlock { return appendAccess { add("\$L.name()", existingBlock) } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index 7044e783d..dfe73f9ec 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt @@ -8,8 +8,8 @@ import com.raizlabs.android.dbflow.processor.definition.BaseDefinition import com.raizlabs.android.dbflow.processor.definition.BaseTableDefinition import com.raizlabs.android.dbflow.processor.definition.TableDefinition import com.raizlabs.android.dbflow.processor.definition.TypeConverterDefinition -import com.raizlabs.android.dbflow.processor.fromTypeMirror -import com.raizlabs.android.dbflow.processor.getTypeElement +import com.raizlabs.android.dbflow.processor.utils.fromTypeMirror +import com.raizlabs.android.dbflow.processor.utils.getTypeElement import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.raizlabs.android.dbflow.sql.QueryBuilder diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt index 1de4bd7e1..ac4f07b55 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt @@ -9,10 +9,7 @@ import com.raizlabs.android.dbflow.annotation.ForeignKeyReference import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.processor.* import com.raizlabs.android.dbflow.processor.definition.TableDefinition -import com.raizlabs.android.dbflow.processor.utils.annotation -import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty -import com.raizlabs.android.dbflow.processor.utils.toTypeElement -import com.raizlabs.android.dbflow.processor.utils.toTypeErasedElement +import com.raizlabs.android.dbflow.processor.utils.* import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.* import java.util.* @@ -87,8 +84,7 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab manager.logError("Referenced was null for $element within $elementTypeName") } - extendsBaseModel = isSubclass(manager.processingEnvironment, - ClassNames.BASE_MODEL.toString(), erasedElement) + extendsBaseModel = erasedElement.isSubclass(manager.processingEnvironment, ClassNames.BASE_MODEL) implementsModel = erasedElement.implementsClass(manager.processingEnvironment, ClassNames.MODEL) isReferencingTableObject = implementsModel || erasedElement.annotation
() != null diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ProcessorUtils.kt similarity index 81% rename from dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt rename to dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ProcessorUtils.kt index f47beada2..7354b0fec 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ProcessorUtils.kt @@ -1,5 +1,6 @@ -package com.raizlabs.android.dbflow.processor +package com.raizlabs.android.dbflow.processor.utils +import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.ProcessorManager.Companion.manager import com.raizlabs.android.dbflow.processor.utils.ElementUtility import com.raizlabs.android.dbflow.processor.utils.erasure @@ -16,7 +17,7 @@ import javax.tools.Diagnostic * Whether the specified element implements the [ClassName] */ fun TypeElement?.implementsClass(processingEnvironment: ProcessingEnvironment - = ProcessorManager.manager.processingEnvironment, className: ClassName) + = manager.processingEnvironment, className: ClassName) = implementsClass(processingEnvironment, className.toString()) /** @@ -47,24 +48,23 @@ fun TypeElement?.implementsClass(processingEnvironment: ProcessingEnvironment, f } /** - * Whether the specified element is assignable to the fqTn parameter + * Whether the specified element is assignable to the [className] parameter + */ +fun TypeElement?.isSubclass(processingEnvironment: ProcessingEnvironment + = manager.processingEnvironment, className: ClassName) + = isSubclass(processingEnvironment, className.toString()) - * @param processingEnvironment The environment this runs in - * * - * @param fqTn THe fully qualified type name of the element we want to check - * * - * @param element The element to check that implements - * * - * @return true if element implements the fqTn +/** + * Whether the specified element is assignable to the [fqTn] parameter */ -fun isSubclass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { +fun TypeElement?.isSubclass(processingEnvironment: ProcessingEnvironment, fqTn: String): Boolean { val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) if (typeElement == null) { processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") return false } else { val classMirror = typeElement.asType() - return classMirror != null && element != null && element.asType() != null && processingEnvironment.typeUtils.isSubtype(element.asType(), classMirror) + return classMirror != null && this != null && this.asType() != null && processingEnvironment.typeUtils.isSubtype(this.asType(), classMirror) } } @@ -88,7 +88,7 @@ fun getTypeElement(element: Element): TypeElement? { } fun getTypeElement(typeMirror: TypeMirror): TypeElement? { - val manager = ProcessorManager.manager + val manager = manager var typeElement: TypeElement? = typeMirror.toTypeElement(manager) if (typeElement == null) { val el = manager.typeUtils.asElement(typeMirror) From c7004e435444cfba6109cc2ba05a8a21d91a59f7 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 08:38:51 +1000 Subject: [PATCH 03/37] [cursor] add flowcursor which wraps normal cursor and allows convenience methods to cut down on code gen complexitiy. --- .../android/dbflow/processor/ClassNames.kt | 1 + .../android/dbflow/processor/SQLiteHelper.kt | 2 +- .../definition/InternalAdapterHelper.kt | 4 +- .../dbflow/processor/definition/Methods.kt | 5 +- .../processor/definition/TableDefinition.kt | 2 +- .../definition/column/ColumnAccessCombiner.kt | 76 +++++------- .../definition/column/ColumnAccessor.kt | 2 +- .../dbflow/sqlcipher/SQLCipherDatabase.java | 23 ++-- .../android/dbflow/list/FlowCursorList.java | 7 +- .../dbflow/sql/language/BaseQueriable.java | 5 +- .../sql/language/BaseTransformable.java | 6 +- .../dbflow/sql/language/CursorResult.java | 7 +- .../android/dbflow/sql/language/Where.java | 7 +- .../queriable/CacheableListModelLoader.java | 4 +- .../sql/queriable/CacheableModelLoader.java | 4 +- .../dbflow/sql/queriable/ListModelLoader.java | 8 +- .../dbflow/sql/queriable/ModelLoader.java | 9 +- .../dbflow/sql/queriable/Queriable.java | 6 +- .../SingleKeyCacheableListModelLoader.java | 5 +- .../SingleKeyCacheableModelLoader.java | 4 +- .../sql/queriable/SingleModelLoader.java | 7 +- .../dbflow/sql/queriable/StringQuery.java | 6 +- .../dbflow/structure/ModelAdapter.java | 14 +-- .../dbflow/structure/RetrievalAdapter.java | 3 +- .../structure/database/AndroidDatabase.java | 22 ++-- .../structure/database/DatabaseWrapper.java | 9 +- .../dbflow/structure/database/FlowCursor.java | 116 ++++++++++++++++++ .../structure/provider/BaseProviderModel.java | 9 +- .../provider/BaseSyncableProviderModel.java | 5 +- .../structure/provider/ContentUtils.java | 11 +- 30 files changed, 250 insertions(+), 139 deletions(-) create mode 100644 dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ClassNames.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ClassNames.kt index 23d377465..2889493fb 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ClassNames.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ClassNames.kt @@ -33,6 +33,7 @@ object ClassNames { val URI = ClassName.get("android.net", "Uri") val URI_MATCHER = ClassName.get("android.content", "UriMatcher") val CURSOR = ClassName.get("android.database", "Cursor") + val FLOW_CURSOR = ClassName.get(DATABASE, "FlowCursor") val DATABASE_UTILS = ClassName.get("android.database", "DatabaseUtils") val CONTENT_VALUES = ClassName.get("android.content", "ContentValues") val CONTENT_URIS = ClassName.get("android.content", "ContentUris") diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt index 4cf734644..2fd1cfe98 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt @@ -52,7 +52,7 @@ enum class SQLiteHelper { private val sMethodMap = hashMapOf(ArrayTypeName.of(TypeName.BYTE) to "getBlob", ArrayTypeName.of(TypeName.BYTE.box()) to "getBlob", - TypeName.BOOLEAN to "getInt", + TypeName.BOOLEAN to "getBoolean", TypeName.BYTE to "getInt", TypeName.BYTE.box() to "getInt", TypeName.CHAR to "getString", diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt index 7eebb341e..95af0c54c 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt @@ -55,7 +55,7 @@ object InternalAdapterHelper { methodBuilder = MethodSpec.methodBuilder("getCachingColumnValuesFromCursor") .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addParameter(ArrayTypeName.of(Any::class.java), "inValues") - .addParameter(ClassNames.CURSOR, "cursor") + .addParameter(ClassNames.FLOW_CURSOR, "cursor") for (i in primaryColumns.indices) { val column = primaryColumns[i] val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) @@ -75,7 +75,7 @@ object InternalAdapterHelper { methodBuilder = MethodSpec.methodBuilder("getCachingColumnValueFromCursor") .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.CURSOR, "cursor") + .addParameter(ClassNames.FLOW_CURSOR, "cursor") val column = primaryColumns[0] val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) methodBuilder.addStatement("return \$L.\$L(\$L.getColumnIndex(\$S))", LoadFromCursorMethod.PARAM_CURSOR, diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index fc6f89ea7..34d3f3544 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -373,9 +373,10 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) override val methodSpec: MethodSpec get() { - val methodBuilder = MethodSpec.methodBuilder("loadFromCursor").addAnnotation(Override::class.java) + val methodBuilder = MethodSpec.methodBuilder("loadFromCursor") + .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.CURSOR, PARAM_CURSOR) + .addParameter(ClassNames.FLOW_CURSOR, PARAM_CURSOR) .addParameter(baseTableDefinition.parameterClassName, ModelUtils.variable).returns(TypeName.VOID) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index 569ab8d25..68aa8df7a 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -513,7 +513,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `override fun`(TypeName.VOID, "reloadRelationships", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { + param(ClassNames.FLOW_CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { modifiers(public, final) code { val noIndex = AtomicInteger(-1) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index c4870132d..5abd3f314 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -1,5 +1,6 @@ package com.raizlabs.android.dbflow.processor.definition.column +import com.grosner.kpoet.S import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper import com.raizlabs.android.dbflow.processor.utils.ModelUtils @@ -27,9 +28,9 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { fieldAccess = CodeBlock.of("ref" + fieldLevelAccessor.propertyName) existingBuilder.addStatement("\$T \$L = \$L != null ? \$L : null", - wrapperFieldTypeName, fieldAccess, - fieldLevelAccessor.get(modelBlock), - wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock))) + wrapperFieldTypeName, fieldAccess, + fieldLevelAccessor.get(modelBlock), + wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock))) } else { if (useWrapper && wrapperLevelAccessor != null) { fieldAccess = wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock)) @@ -52,7 +53,7 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { protected fun useStoredFieldRef(): Boolean { return combiner.wrapperLevelAccessor == null && - combiner.fieldLevelAccessor !is VisibleScopeColumnAccessor + combiner.fieldLevelAccessor !is VisibleScopeColumnAccessor } } @@ -98,7 +99,7 @@ class ExistenceAccessCombiner(combiner: Combiner, } code.add("\$T.selectCountOf()\n.from(\$T.class)\n.where(getPrimaryConditionClause(\$L))\n.hasData(wrapper)", - ClassNames.SQLITE, tableClassName, modelBlock) + ClassNames.SQLITE, tableClassName, modelBlock) } code.add(";\n") } @@ -129,10 +130,10 @@ class ContentValuesCombiner(combiner: Combiner) subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) } code.addStatement("values.put(\$S, \$L != null ? \$L : \$L)", - QueryBuilder.quote(columnRepresentation), storedFieldAccess, subWrapperFieldAccess, defaultValue) + QueryBuilder.quote(columnRepresentation), storedFieldAccess, subWrapperFieldAccess, defaultValue) } else { code.addStatement("values.put(\$S, \$L)", - QueryBuilder.quote(columnRepresentation), fieldAccess) + QueryBuilder.quote(columnRepresentation), fieldAccess) } } } @@ -153,8 +154,8 @@ class SqliteStatementAccessCombiner(combiner: Combiner) if (fieldTypeName.isPrimitive) { code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, fieldAccess) + SQLiteHelper[fieldTypeName].sqLiteStatementMethod, + index, columnRepresentation, fieldAccess) } else { if (defaultValue != null) { var storedFieldAccess = CodeBlock.of("ref\$L", fieldLevelAccessor.propertyName) @@ -170,22 +171,22 @@ class SqliteStatementAccessCombiner(combiner: Combiner) subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) } code.beginControlFlow("if (\$L != null) ", storedFieldAccess) - .addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, subWrapperFieldAccess) - .nextControlFlow("else") + .addStatement("statement.bind\$L(\$L + \$L, \$L)", + SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, + index, columnRepresentation, subWrapperFieldAccess) + .nextControlFlow("else") if (!defaultValue.toString().isNullOrEmpty()) { code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, defaultValue) + SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, + index, columnRepresentation, defaultValue) } else { code.addStatement("statement.bindNull(\$L + \$L)", index, columnRepresentation) } code.endControlFlow() } else { code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, index, - columnRepresentation, fieldAccess) + SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, index, + columnRepresentation, fieldAccess) } } } @@ -207,17 +208,13 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, combiner.apply { val indexName: CodeBlock if (!orderedCursorLookup) { - indexName = CodeBlock.of("index_\$L", columnRepresentation) - code.addStatement("\$T \$L = cursor.getColumnIndex(\$S)", Int::class.java, indexName, - columnRepresentation) - code.beginControlFlow("if (\$1L != -1 && !cursor.isNull(\$1L))", indexName) + indexName = CodeBlock.of(columnRepresentation.S) } else { indexName = CodeBlock.of(index.toString()) - code.beginControlFlow("if (!cursor.isNull(\$1L))", index) } - val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", - SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) + val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L, $defaultValue)", + SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) if (wrapperLevelAccessor != null) { // special case where we need to append try catch hack val isEnum = wrapperLevelAccessor is EnumColumnAccessor @@ -226,16 +223,16 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } if (subWrapperAccessor != null) { code.addStatement(fieldLevelAccessor.set( - wrapperLevelAccessor.set(subWrapperAccessor.set(cursorAccess)), modelBlock)) + wrapperLevelAccessor.set(subWrapperAccessor.set(cursorAccess)), modelBlock)) } else { code.addStatement(fieldLevelAccessor.set( - wrapperLevelAccessor.set(cursorAccess), modelBlock)) + wrapperLevelAccessor.set(cursorAccess), modelBlock)) } if (isEnum) { code.nextControlFlow("catch (\$T i)", IllegalArgumentException::class.java) if (assignDefaultValuesFromCursor) { code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) + isDefault = true), modelBlock)) } else { code.addStatement(fieldLevelAccessor.set(defaultValue, modelBlock)) } @@ -244,17 +241,6 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } else { code.addStatement(fieldLevelAccessor.set(cursorAccess, modelBlock)) } - - if (assignDefaultValuesFromCursor) { - code.nextControlFlow("else") - if (wrapperLevelAccessor != null) { - code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) - } else { - code.addStatement(fieldLevelAccessor.set(defaultValue, modelBlock)) - } - } - code.endControlFlow() } } } @@ -266,13 +252,13 @@ class PrimaryReferenceAccessCombiner(combiner: Combiner) modelBlock: CodeBlock) { val wrapperLevelAccessor = this.combiner.wrapperLevelAccessor code.addStatement("clause.and(\$L.\$Leq(\$L))", columnRepresentation, - if (!wrapperLevelAccessor.isPrimitiveTarget()) "invertProperty()." else "", - getFieldAccessBlock(code, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) + if (!wrapperLevelAccessor.isPrimitiveTarget()) "invertProperty()." else "", + getFieldAccessBlock(code, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) } override fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int) { code.addStatement("clause.and(\$L.eq((\$T) \$L))", columnRepresentation, - ClassNames.ICONDITIONAL, "null") + ClassNames.ICONDITIONAL, "null") } } @@ -312,10 +298,10 @@ class SaveModelAccessCombiner(combiner: Combiner, code.beginControlFlow("if (\$L != null)", access) if (implementsModel) { code.addStatement("\$L.save(\$L)", access, - if (extendsBaseModel) ModelUtils.wrapper else "") + if (extendsBaseModel) ModelUtils.wrapper else "") } else { code.addStatement("\$T.getModelAdapter(\$T.class).save(\$L, \$L)", - ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) + ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) } code.endControlFlow() } @@ -334,10 +320,10 @@ class DeleteModelAccessCombiner(combiner: Combiner, code.beginControlFlow("if (\$L != null)", access) if (implementsModel) { code.addStatement("\$L.delete(\$L)", access, - if (extendsBaseModel) ModelUtils.wrapper else "") + if (extendsBaseModel) ModelUtils.wrapper else "") } else { code.addStatement("\$T.getModelAdapter(\$T.class).delete(\$L, \$L)", - ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) + ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) } code.endControlFlow() } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt index a66b73208..af042df31 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt @@ -248,7 +248,7 @@ class BooleanColumnAccessor(propertyName: String? = null) : ColumnAccessor(prope isDefault: Boolean): CodeBlock { return appendAccess { if (isDefault) add(existingBlock) - else add("\$L == 1 ? true : false", existingBlock) + else add("\$L", existingBlock) } } diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java index 4f783bc08..5b4f71c56 100644 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java +++ b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java @@ -1,12 +1,12 @@ package com.raizlabs.android.dbflow.sqlcipher; import android.content.ContentValues; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import net.sqlcipher.database.SQLiteDatabase; @@ -60,8 +60,8 @@ public SQLiteDatabase getDatabase() { } @Override - public Cursor rawQuery(String query, String[] selectionArgs) { - return database.rawQuery(query, selectionArgs); + public FlowCursor rawQuery(String query, String[] selectionArgs) { + return FlowCursor.from(database.rawQuery(query, selectionArgs)); } @Override @@ -75,15 +75,14 @@ public long insertWithOnConflict(String tableName, String nullColumnHack, Conten } @Override - public Cursor query( - @NonNull String tableName, - @Nullable String[] columns, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String groupBy, - @Nullable String having, - @Nullable String orderBy) { - return database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy); + public FlowCursor query(@NonNull String tableName, + @Nullable String[] columns, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String groupBy, + @Nullable String having, + @Nullable String orderBy) { + return FlowCursor.from(database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy)); } @Override diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java index 5eddd53b4..db7c0d637 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java @@ -13,6 +13,7 @@ import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.raizlabs.android.dbflow.structure.cache.ModelCache; import com.raizlabs.android.dbflow.structure.cache.ModelLruCache; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.HashSet; @@ -48,7 +49,7 @@ public interface OnCursorRefreshListener { public static final int MIN_CACHE_SIZE = 20; @Nullable - private Cursor cursor; + private FlowCursor cursor; private Class table; private ModelCache modelCache; @@ -301,7 +302,7 @@ public Builder newBuilder() { public static class Builder { private final Class modelClass; - private Cursor cursor; + private FlowCursor cursor; private ModelQueriable modelQueriable; private boolean cacheModels = true; private ModelCache modelCache; @@ -316,7 +317,7 @@ public Builder(@NonNull ModelQueriable modelQueriable) { } public Builder cursor(Cursor cursor) { - this.cursor = cursor; + this.cursor = FlowCursor.from(cursor); return this; } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java index 886660671..e453e874b 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java @@ -14,6 +14,7 @@ import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; import com.raizlabs.android.dbflow.structure.database.DatabaseStatementWrapper; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Base implementation of something that can be queried from the database. @@ -74,13 +75,13 @@ public boolean hasData(DatabaseWrapper databaseWrapper) { } @Override - public Cursor query() { + public FlowCursor query() { query(FlowManager.getWritableDatabaseForTable(table)); return null; } @Override - public Cursor query(DatabaseWrapper databaseWrapper) { + public FlowCursor query(DatabaseWrapper databaseWrapper) { if (getPrimaryAction().equals(BaseModel.Action.INSERT)) { // inserting, let's compile and insert DatabaseStatement databaseStatement = compileStatement(databaseWrapper); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java index 0e20dee43..39778fbef 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java @@ -1,10 +1,10 @@ package com.raizlabs.android.dbflow.sql.language; -import android.database.Cursor; import android.support.annotation.NonNull; import com.raizlabs.android.dbflow.sql.language.property.IProperty; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.List; @@ -29,12 +29,12 @@ public Where where(SQLOperator... conditions) { } @Override - public Cursor query() { + public FlowCursor query() { return where().query(); } @Override - public Cursor query(DatabaseWrapper databaseWrapper) { + public FlowCursor query(DatabaseWrapper databaseWrapper) { return where().query(databaseWrapper); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java index d33f682ea..f53038135 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java @@ -8,6 +8,7 @@ import com.raizlabs.android.dbflow.list.FlowCursorIterator; import com.raizlabs.android.dbflow.list.IFlowCursorIterator; import com.raizlabs.android.dbflow.structure.InstanceAdapter; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.List; @@ -21,18 +22,18 @@ public class CursorResult implements IFlowCursorIterator { private final InstanceAdapter retrievalAdapter; @Nullable - private Cursor cursor; + private FlowCursor cursor; @SuppressWarnings("unchecked") CursorResult(Class modelClass, @Nullable Cursor cursor) { - this.cursor = cursor; + this.cursor = FlowCursor.from(cursor); retrievalAdapter = FlowManager.getInstanceAdapter(modelClass); } /** * Swaps the current cursor and will close existing one. */ - public void swapCursor(@Nullable Cursor cursor) { + public void swapCursor(@Nullable FlowCursor cursor) { if (this.cursor != null) { if (!this.cursor.isClosed()) { this.cursor.close(); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java index 5b2d02d56..5fd3d11fd 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java @@ -10,6 +10,7 @@ import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.Collections; @@ -207,9 +208,9 @@ public String getQuery() { * @return the result of the query as a {@link Cursor}. */ @Override - public Cursor query(DatabaseWrapper wrapper) { + public FlowCursor query(DatabaseWrapper wrapper) { // Query the sql here - Cursor cursor; + FlowCursor cursor; if (whereBase.getQueryBuilderBase() instanceof Select) { cursor = wrapper.rawQuery(getQuery(), null); } else { @@ -220,7 +221,7 @@ public Cursor query(DatabaseWrapper wrapper) { } @Override - public Cursor query() { + public FlowCursor query() { return query(FlowManager.getDatabaseForTable(getTable()).getWritableDatabase()); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java index ea8ad462a..4c8c96a64 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java @@ -1,12 +1,12 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.raizlabs.android.dbflow.structure.cache.ModelCache; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.List; @@ -51,7 +51,7 @@ public ModelAdapter getModelAdapter() { @NonNull @SuppressWarnings("unchecked") @Override - public List convertToData(@NonNull Cursor cursor, @Nullable List data) { + public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { if (data == null) { data = new ArrayList<>(); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java index 8a604222a..b3716c4a7 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java @@ -1,12 +1,12 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.raizlabs.android.dbflow.structure.cache.ModelCache; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Loads model data that is backed by a {@link ModelCache}. Used when {@link Table#cachingEnabled()} @@ -51,7 +51,7 @@ public ModelAdapter getModelAdapter() { */ @Nullable @Override - public TModel convertToData(@NonNull Cursor cursor, @Nullable TModel data, boolean moveToFirst) { + public TModel convertToData(@NonNull FlowCursor cursor, @Nullable TModel data, boolean moveToFirst) { if (!moveToFirst || cursor.moveToFirst()) { Object[] values = getModelAdapter().getCachingColumnValuesFromCursor( new Object[getModelAdapter().getCachingColumns().length], cursor); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java index 37754f6b9..ea3bc4283 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java @@ -1,10 +1,10 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.List; @@ -46,13 +46,13 @@ public List load(@NonNull DatabaseWrapper databaseWrapper, String query, @NonNull @Override - public List load(@Nullable Cursor cursor) { + public List load(@Nullable FlowCursor cursor) { return super.load(cursor); } @NonNull @Override - public List load(@Nullable Cursor cursor, @Nullable List data) { + public List load(@Nullable FlowCursor cursor, @Nullable List data) { if (data == null) { data = new ArrayList<>(); } else { @@ -64,7 +64,7 @@ public List load(@Nullable Cursor cursor, @Nullable List data) { @SuppressWarnings("unchecked") @Override @NonNull - public List convertToData(@NonNull Cursor cursor, @Nullable List data) { + public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { if (data == null) { data = new ArrayList<>(); } else { diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java index bb7de4b4a..5aa6d0d95 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java @@ -9,6 +9,7 @@ import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.structure.InstanceAdapter; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Represents how models load from DB. It will query a {@link SQLiteDatabase} @@ -54,17 +55,17 @@ public TReturn load(@NonNull DatabaseWrapper databaseWrapper, String query) { @Nullable public TReturn load(@NonNull DatabaseWrapper databaseWrapper, String query, @Nullable TReturn data) { - final Cursor cursor = databaseWrapper.rawQuery(query, null); + final FlowCursor cursor = databaseWrapper.rawQuery(query, null); return load(cursor, data); } @Nullable - public TReturn load(@Nullable Cursor cursor) { + public TReturn load(@Nullable FlowCursor cursor) { return load(cursor, null); } @Nullable - public TReturn load(@Nullable Cursor cursor, @Nullable TReturn data) { + public TReturn load(@Nullable FlowCursor cursor, @Nullable TReturn data) { if (cursor != null) { try { data = convertToData(cursor, data); @@ -103,5 +104,5 @@ public DatabaseDefinition getDatabaseDefinition() { * @return A new (or reused) instance that represents the {@link Cursor}. */ @Nullable - public abstract TReturn convertToData(@NonNull final Cursor cursor, @Nullable TReturn data); + public abstract TReturn convertToData(@NonNull final FlowCursor cursor, @Nullable TReturn data); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java index cc264ce1e..ab6f79e1c 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java @@ -1,6 +1,5 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.sql.Query; @@ -11,6 +10,7 @@ import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: The most basic interface that some of the classes such as {@link Insert}, {@link ModelQueriable}, @@ -22,7 +22,7 @@ public interface Queriable extends Query { * @return A cursor from the DB based on this query */ @Nullable - Cursor query(); + FlowCursor query(); /** * Allows you to pass in a {@link DatabaseWrapper} manually. @@ -31,7 +31,7 @@ public interface Queriable extends Query { * @return A cursor from the DB based on this query */ @Nullable - Cursor query(DatabaseWrapper databaseWrapper); + FlowCursor query(DatabaseWrapper databaseWrapper); /** diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java index e735ab7c9..101eb8add 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java @@ -1,9 +1,10 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; + import java.util.ArrayList; import java.util.List; @@ -19,7 +20,7 @@ public SingleKeyCacheableListModelLoader(Class tModelClass) { @NonNull @SuppressWarnings("unchecked") @Override - public List convertToData(@NonNull Cursor cursor, @Nullable List data) { + public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { if (data == null) { data = new ArrayList<>(); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java index 9c746c76d..da4a2774f 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java @@ -1,10 +1,10 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: More optimized version of {@link CacheableModelLoader} which assumes that the {@link Model} @@ -24,7 +24,7 @@ public SingleKeyCacheableModelLoader(Class modelClass) { */ @Nullable @Override - public TModel convertToData(@NonNull Cursor cursor, @Nullable TModel data, boolean moveToFirst) { + public TModel convertToData(@NonNull FlowCursor cursor, @Nullable TModel data, boolean moveToFirst) { if (!moveToFirst || cursor.moveToFirst()) { Object value = getModelAdapter().getCachingColumnValueFromCursor(cursor); TModel model = getModelCache().get(value); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java index f17502796..01dd7da1c 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java @@ -1,9 +1,10 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; + /** * Description: Responsible for loading data into a single object. */ @@ -15,7 +16,7 @@ public SingleModelLoader(Class modelClass) { @SuppressWarnings("unchecked") @Nullable - public TModel convertToData(@NonNull final Cursor cursor, @Nullable TModel data, boolean moveToFirst) { + public TModel convertToData(@NonNull final FlowCursor cursor, @Nullable TModel data, boolean moveToFirst) { if (!moveToFirst || cursor.moveToFirst()) { if (data == null) { data = getInstanceAdapter().newInstance(); @@ -26,7 +27,7 @@ public TModel convertToData(@NonNull final Cursor cursor, @Nullable TModel data, } @Override - public TModel convertToData(@NonNull final Cursor cursor, @Nullable TModel data) { + public TModel convertToData(@NonNull final FlowCursor cursor, @Nullable TModel data) { return convertToData(cursor, data, true); } } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java index 7f03e04e2..efdb1b5bb 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java @@ -1,6 +1,5 @@ package com.raizlabs.android.dbflow.sql.queriable; -import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.raizlabs.android.dbflow.config.FlowManager; @@ -9,6 +8,7 @@ import com.raizlabs.android.dbflow.sql.language.Delete; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Provides a very basic query mechanism for strings. Allows you to easily perform custom SQL query string @@ -41,12 +41,12 @@ public String getQuery() { } @Override - public Cursor query() { + public FlowCursor query() { return query(FlowManager.getDatabaseForTable(getTable()).getWritableDatabase()); } @Override - public Cursor query(DatabaseWrapper databaseWrapper) { + public FlowCursor query(DatabaseWrapper databaseWrapper) { return databaseWrapper.rawQuery(query, args); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java index b3a1889e8..401d7b8be 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java @@ -1,7 +1,6 @@ package com.raizlabs.android.dbflow.structure; import android.content.ContentValues; -import android.database.Cursor; import android.database.sqlite.SQLiteStatement; import android.support.annotation.NonNull; @@ -21,6 +20,7 @@ import com.raizlabs.android.dbflow.structure.cache.SimpleMapCache; import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.Collection; @@ -115,7 +115,7 @@ public DatabaseStatement getCompiledStatement(DatabaseWrapper databaseWrapper) { * @param cursor The cursor to load * @return A new {@link TModel} */ - public TModel loadFromCursor(Cursor cursor) { + public TModel loadFromCursor(FlowCursor cursor) { TModel model = newInstance(); loadFromCursor(cursor, model); return model; @@ -286,14 +286,14 @@ public String[] getCachingColumns() { } /** - * Loads all primary keys from the {@link Cursor} into the inValues. The size of the array must + * Loads all primary keys from the {@link FlowCursor} into the inValues. The size of the array must * match all primary keys. This method gets generated when caching is enabled. * * @param inValues The reusable array of values to populate. * @param cursor The cursor to load from. * @return The populated set of values to load from cache. */ - public Object[] getCachingColumnValuesFromCursor(Object[] inValues, Cursor cursor) { + public Object[] getCachingColumnValuesFromCursor(Object[] inValues, FlowCursor cursor) { throwCachingError(); return null; } @@ -302,7 +302,7 @@ public Object[] getCachingColumnValuesFromCursor(Object[] inValues, Cursor curso * @param cursor The cursor to load caching id from. * @return The single cache column from cursor (if single). */ - public Object getCachingColumnValueFromCursor(Cursor cursor) { + public Object getCachingColumnValueFromCursor(FlowCursor cursor) { throwSingleCachingError(); return null; } @@ -384,12 +384,12 @@ public void setModelSaver(ModelSaver modelSaver) { } /** - * Reloads relationships when loading from {@link Cursor} in a model that's cacheable. By having + * Reloads relationships when loading from {@link FlowCursor} in a model that's cacheable. By having * relationships with cached models, the retrieval will be very fast. * * @param cursor The cursor to reload from. */ - public void reloadRelationships(@NonNull TModel model, Cursor cursor) { + public void reloadRelationships(@NonNull TModel model, FlowCursor cursor) { throwCachingError(); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java index 09cc2ce10..c74a56702 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java @@ -12,6 +12,7 @@ import com.raizlabs.android.dbflow.sql.queriable.ListModelLoader; import com.raizlabs.android.dbflow.sql.queriable.SingleModelLoader; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Provides a base retrieval class for all {@link Model} backed @@ -59,7 +60,7 @@ public void load(TModel model, DatabaseWrapper databaseWrapper) { * @param model The model to assign cursor data to * @param cursor The cursor to load into the model */ - public abstract void loadFromCursor(Cursor cursor, TModel model); + public abstract void loadFromCursor(FlowCursor cursor, TModel model); /** * @param model The model to query values from diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java index a73ee18cd..902245c65 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java @@ -1,7 +1,6 @@ package com.raizlabs.android.dbflow.structure.database; import android.content.ContentValues; -import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.support.annotation.NonNull; @@ -57,8 +56,8 @@ public DatabaseStatement compileStatement(String rawQuery) { } @Override - public Cursor rawQuery(String query, String[] selectionArgs) { - return database.rawQuery(query, selectionArgs); + public FlowCursor rawQuery(String query, String[] selectionArgs) { + return FlowCursor.from(database.rawQuery(query, selectionArgs)); } @Override @@ -84,15 +83,14 @@ public long insertWithOnConflict(String tableName, String nullColumnHack, Conten } @Override - public Cursor query( - @NonNull String tableName, - @Nullable String[] columns, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String groupBy, - @Nullable String having, - @Nullable String orderBy) { - return database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy); + public FlowCursor query(@NonNull String tableName, + @Nullable String[] columns, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String groupBy, + @Nullable String having, + @Nullable String orderBy) { + return FlowCursor.from(database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy)); } @Override diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java index 9233f9355..537916adf 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java @@ -1,7 +1,6 @@ package com.raizlabs.android.dbflow.structure.database; import android.content.ContentValues; -import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -23,7 +22,7 @@ public interface DatabaseWrapper { DatabaseStatement compileStatement(String rawQuery); - Cursor rawQuery(String query, String[] selectionArgs); + FlowCursor rawQuery(String query, String[] selectionArgs); long updateWithOnConflict(String tableName, ContentValues contentValues, String where, String[] whereArgs, int conflictAlgorithm); @@ -31,9 +30,9 @@ long updateWithOnConflict(String tableName, ContentValues contentValues, String long insertWithOnConflict(String tableName, String nullColumnHack, ContentValues values, int sqLiteDatabaseAlgorithmInt); - Cursor query(@NonNull String tableName, @Nullable String[] columns, @Nullable String selection, - @Nullable String[] selectionArgs, @Nullable String groupBy, - @Nullable String having, @Nullable String orderBy); + FlowCursor query(@NonNull String tableName, @Nullable String[] columns, @Nullable String selection, + @Nullable String[] selectionArgs, @Nullable String groupBy, + @Nullable String having, @Nullable String orderBy); int delete(@NonNull String tableName, @Nullable String whereClause, @Nullable String[] whereArgs); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java new file mode 100644 index 000000000..72509912f --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -0,0 +1,116 @@ +package com.raizlabs.android.dbflow.structure.database; + +import android.database.Cursor; +import android.database.CursorWrapper; +import android.support.annotation.NonNull; + +public class FlowCursor extends CursorWrapper { + + public static FlowCursor from(Cursor cursor) { + if (cursor instanceof FlowCursor) { + return (FlowCursor) cursor; + } else { + return new FlowCursor(cursor); + } + } + + private FlowCursor(@NonNull Cursor cursor) { + super(cursor); + } + + public String getStringOrDefault(int index, String defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getString(index); + } else { + return defValue; + } + } + + public String getStringOrDefault(String columnName, String defValue) { + return getStringOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public int getIntOrDefault(int index, int defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getInt(index); + } else { + return defValue; + } + } + + public int getIntOrDefault(String columnName, int defValue) { + return getIntOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public double getDoubleOrDefault(int index, double defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getDouble(index); + } else { + return defValue; + } + } + + public double getDoubleOrDefault(String columnName, double defValue) { + return getDoubleOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public float getFloatOrDefault(int index, float defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getFloat(index); + } else { + return defValue; + } + } + + public float getFloatOrDefault(String columnName, float defValue) { + return getFloatOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public long getLongOrDefault(int index, long defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getLong(index); + } else { + return defValue; + } + } + + public long getLongOrDefault(String columnName, long defValue) { + return getLongOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public short getShortOrDefault(int index, short defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getShort(index); + } else { + return defValue; + } + } + + public short getShortOrDefault(String columnName, short defValue) { + return getShortOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public byte[] getBlobOrDefault(int index, byte[] defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getBlob(index); + } else { + return defValue; + } + } + + public byte[] getBlobOrDefault(String columnName, byte[] defValue) { + return getBlobOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + + public boolean getBooleanOrDefault(int index, boolean defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getInt(index) == 1; + } else { + return defValue; + } + } + + public boolean getBooleanOrDefault(String columnName, boolean defValue) { + return getBooleanOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java index 9d88f7bdb..44437bd40 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java @@ -7,6 +7,7 @@ import com.raizlabs.android.dbflow.sql.language.OperatorGroup; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Provides a base implementation of a {@link Model} backed @@ -15,7 +16,7 @@ * keep modifications locally from the {@link ContentProvider} */ public abstract class BaseProviderModel - extends BaseModel implements ModelProvider { + extends BaseModel implements ModelProvider { @Override public boolean delete() { @@ -52,7 +53,7 @@ public long insert() { @SuppressWarnings("unchecked") public boolean exists() { Cursor cursor = ContentUtils.query(FlowManager.getContext().getContentResolver(), - getQueryUri(), getModelAdapter().getPrimaryConditionClause(this), ""); + getQueryUri(), getModelAdapter().getPrimaryConditionClause(this), ""); boolean exists = (cursor != null && cursor.getCount() > 0); if (cursor != null) { cursor.close(); @@ -64,8 +65,8 @@ public boolean exists() { @SuppressWarnings("unchecked") public void load(OperatorGroup whereConditions, String orderBy, String... columns) { - Cursor cursor = ContentUtils.query(FlowManager.getContext().getContentResolver(), - getQueryUri(), whereConditions, orderBy, columns); + FlowCursor cursor = FlowCursor.from(ContentUtils.query(FlowManager.getContext().getContentResolver(), + getQueryUri(), whereConditions, orderBy, columns)); if (cursor != null && cursor.moveToFirst()) { getModelAdapter().loadFromCursor(cursor, this); cursor.close(); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java index ee5774887..5b4e4b991 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java @@ -1,12 +1,12 @@ package com.raizlabs.android.dbflow.structure.provider; import android.content.ContentProvider; -import android.database.Cursor; import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.sql.language.OperatorGroup; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; /** * Description: Provides a base implementation of a {@link Model} backed @@ -44,7 +44,8 @@ public boolean update() { @SuppressWarnings("unchecked") public void load(OperatorGroup whereOperatorGroup, String orderBy, String... columns) { - Cursor cursor = ContentUtils.query(FlowManager.getContext().getContentResolver(), getQueryUri(), whereOperatorGroup, orderBy, columns); + FlowCursor cursor = FlowCursor.from(ContentUtils.query(FlowManager.getContext().getContentResolver(), + getQueryUri(), whereOperatorGroup, orderBy, columns)); if (cursor != null && cursor.moveToFirst()) { getModelAdapter().loadFromCursor(cursor, this); cursor.close(); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java index 3a2d40b47..cdf599fa1 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java @@ -12,6 +12,7 @@ import com.raizlabs.android.dbflow.sql.language.OperatorGroup; import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.structure.ModelAdapter; +import com.raizlabs.android.dbflow.structure.database.FlowCursor; import java.util.ArrayList; import java.util.List; @@ -254,16 +255,16 @@ public static List queryList(Uri queryUri, Class The class that implements {@link Model} * @return A list of {@link TableClass} */ - public static List queryList(ContentResolver contentResolver, Uri queryUri, Class table, + public static List queryList(ContentResolver contentResolver, Uri queryUri, + Class table, OperatorGroup whereConditions, String orderBy, String... columns) { - Cursor cursor = contentResolver.query(queryUri, columns, whereConditions.getQuery(), null, orderBy); + FlowCursor cursor = FlowCursor.from(contentResolver.query(queryUri, columns, whereConditions.getQuery(), null, orderBy)); if (cursor != null) { return FlowManager.getModelAdapter(table) - .getListModelLoader() - .load(cursor); + .getListModelLoader() + .load(cursor); } - return new ArrayList<>(); } From eee17afed1fc2e7ab37aa930085a897a09328b4d Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 08:51:51 +1000 Subject: [PATCH 04/37] [cursor] add boolean convenience method. add dont assign default vlues from cursor model. --- .../android/dbflow/models/SimpleTestModels.kt | 7 ++- .../dbflow/structure/database/FlowCursor.java | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index f0e5d98ba..b6d4ca128 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -34,4 +34,9 @@ open class AllFieldsModel(@PrimaryKey var name: String? = null, } @Table(database = TestDatabase::class, allFields = true) -class SubclassAllFields(@Column var order: Int = 0) : AllFieldsModel() \ No newline at end of file +class SubclassAllFields(@Column var order: Int = 0) : AllFieldsModel() + +@Table(database = TestDatabase::class, assignDefaultValuesFromCursor = false) +class DontAssignDefaultModel(@PrimaryKey var name: String? = null, + @Column(getterName = "getNullableBool") var nullableBool: Boolean? = null, + @Column var index: Int = 0) \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java index 72509912f..70715e091 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -42,6 +42,18 @@ public int getIntOrDefault(String columnName, int defValue) { return getIntOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + public Integer getIntOrDefault(int index, Integer defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getInt(index); + } else { + return defValue; + } + } + + public Integer getIntOrDefault(String columnName, Integer defValue) { + return getIntOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + public double getDoubleOrDefault(int index, double defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { return getWrappedCursor().getDouble(index); @@ -54,6 +66,18 @@ public double getDoubleOrDefault(String columnName, double defValue) { return getDoubleOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + public Double getDoubleOrDefault(int index, Double defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getDouble(index); + } else { + return defValue; + } + } + + public Double getDoubleOrDefault(String columnName, Double defValue) { + return getDoubleOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + public float getFloatOrDefault(int index, float defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { return getWrappedCursor().getFloat(index); @@ -66,6 +90,18 @@ public float getFloatOrDefault(String columnName, float defValue) { return getFloatOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + public Float getFloatOrDefault(int index, Float defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getFloat(index); + } else { + return defValue; + } + } + + public Float getFloatOrDefault(String columnName, Float defValue) { + return getFloatOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + public long getLongOrDefault(int index, long defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { return getWrappedCursor().getLong(index); @@ -78,6 +114,18 @@ public long getLongOrDefault(String columnName, long defValue) { return getLongOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + public Long getLongOrDefault(int index, Long defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getLong(index); + } else { + return defValue; + } + } + + public Long getLongOrDefault(String columnName, Long defValue) { + return getLongOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + public short getShortOrDefault(int index, short defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { return getWrappedCursor().getShort(index); @@ -90,6 +138,18 @@ public short getShortOrDefault(String columnName, short defValue) { return getShortOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + public Short getShortOrDefault(int index, Short defValue) { + if (index != -1 && !getWrappedCursor().isNull(index)) { + return getWrappedCursor().getShort(index); + } else { + return defValue; + } + } + + public Short getShortOrDefault(String columnName, Short defValue) { + return getShortOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + } + public byte[] getBlobOrDefault(int index, byte[] defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { return getWrappedCursor().getBlob(index); From 6e5fa596e0e68ae2d52ab730b6ba36fff538ede5 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 12:07:58 +1000 Subject: [PATCH 05/37] [code] consolidate more code gen logic and code. --- .../dbflow/processor/definition/Methods.kt | 6 +- .../definition/ModelViewDefinition.kt | 15 +- .../definition/QueryModelDefinition.kt | 9 +- .../processor/definition/TableDefinition.kt | 200 ++++++++---------- .../definition/column/ColumnAccessCombiner.kt | 31 ++- .../android/dbflow/models/SimpleTestModels.kt | 6 +- .../dbflow/structure/RetrievalAdapter.java | 10 +- .../dbflow/structure/database/FlowCursor.java | 8 +- 8 files changed, 147 insertions(+), 138 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 34d3f3544..442e05c7b 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -389,9 +389,9 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) if (baseTableDefinition is TableDefinition) { val codeBuilder = CodeBlock.builder() - for (oneToMany in baseTableDefinition.oneToManyDefinitions) { - if (oneToMany.isLoad) oneToMany.writeLoad(codeBuilder) - } + baseTableDefinition.oneToManyDefinitions + .filter { it.isLoad } + .forEach { it.writeLoad(codeBuilder) } methodBuilder.addCode(codeBuilder.build()) } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index 70d64b78e..da5b5c712 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -4,7 +4,9 @@ import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.Column import com.raizlabs.android.dbflow.annotation.ModelView import com.raizlabs.android.dbflow.annotation.ModelViewQuery -import com.raizlabs.android.dbflow.processor.* +import com.raizlabs.android.dbflow.processor.ClassNames +import com.raizlabs.android.dbflow.processor.ColumnValidator +import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.utils.* @@ -69,11 +71,11 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab val modelView = element.getAnnotation(ModelView::class.java) if (modelView != null) { databaseDefinition = manager.getDatabaseHolderDefinition(databaseName)?.databaseDefinition - setOutputClassName(databaseDefinition?.classSeparator + DBFLOW_MODEL_VIEW_TAG) + setOutputClassName("${databaseDefinition?.classSeparator}ViewTable") typeElement?.let { createColumnDefinitions(it) } } else { - setOutputClassName(DBFLOW_MODEL_VIEW_TAG) + setOutputClassName("ViewTable") } } @@ -181,9 +183,4 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab override fun compareTo(other: ModelViewDefinition): Int { return Integer.valueOf(priority)!!.compareTo(other.priority) } - - companion object { - - private val DBFLOW_MODEL_VIEW_TAG = "ViewTable" - } -} +} \ No newline at end of file diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index d950b8969..61c2513c5 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -7,10 +7,10 @@ import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ColumnValidator import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.raizlabs.android.dbflow.processor.utils.implementsClass import com.raizlabs.android.dbflow.processor.utils.ElementUtility import com.raizlabs.android.dbflow.processor.utils.`override fun` import com.raizlabs.android.dbflow.processor.utils.annotation +import com.raizlabs.android.dbflow.processor.utils.implementsClass import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec @@ -62,7 +62,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana typeElement.annotation()?.let { queryModel -> databaseDefinition = manager.getDatabaseHolderDefinition(databaseTypeName)?.databaseDefinition - setOutputClassName(databaseDefinition?.classSeparator + DBFLOW_QUERY_MODEL_TAG) + setOutputClassName("${databaseDefinition?.classSeparator}QueryTable") allFields = queryModel.allFields typeElement?.let { createColumnDefinitions(it) } @@ -136,9 +136,4 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana val primaryColumnDefinitions: List get() = ArrayList() - companion object { - - private val DBFLOW_QUERY_MODEL_TAG = "QueryTable" - } - } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index 68aa8df7a..96d8ecb16 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -4,7 +4,10 @@ import com.google.common.collect.Lists import com.google.common.collect.Maps import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.* -import com.raizlabs.android.dbflow.processor.* +import com.raizlabs.android.dbflow.processor.ClassNames +import com.raizlabs.android.dbflow.processor.ColumnValidator +import com.raizlabs.android.dbflow.processor.OneToManyValidator +import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.utils.* @@ -164,7 +167,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } databaseDefinition?.let { - setOutputClassName(it.classSeparator + DBFLOW_TABLE_TAG) + setOutputClassName("${it.classSeparator}Table") // globular default var insertConflict: ConflictAction? = table.insertConflict @@ -339,57 +342,81 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab get() = ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, elementClassName) override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { + typeBuilder.apply { - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) - InternalAdapterHelper.writeGetTableName(typeBuilder, tableName) + InternalAdapterHelper.writeGetModelClass(this, elementClassName) + InternalAdapterHelper.writeGetTableName(this, tableName) - val getAllColumnPropertiesMethod = FieldSpec.builder( - ArrayTypeName.of(ClassNames.IPROPERTY), "ALL_COLUMN_PROPERTIES", - Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - val getPropertiesBuilder = CodeBlock.builder() + val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@TableDefinition) + customTypeConverterPropertyMethod.addToType(this) - val paramColumnName = "columnName" - val getPropertyForNameMethod = MethodSpec.methodBuilder("getProperty") - .addAnnotation(Override::class.java) - .addParameter(ClassName.get(String::class.java), paramColumnName) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassNames.PROPERTY) + constructor(param(ClassNames.DATABASE_HOLDER, "holder"), + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + modifiers(public) + statement("super(databaseDefinition)") + code { + customTypeConverterPropertyMethod.addCode(this) + } + } - getPropertyForNameMethod.addStatement("\$L = \$T.quoteIfNeeded(\$L)", paramColumnName, - ClassName.get(QueryBuilder::class.java), paramColumnName) + `override fun`(elementClassName!!, "newInstance") { + modifiers(public, final) + `return`("new \$T()", elementClassName) + } - getPropertyForNameMethod.beginControlFlow("switch (\$L) ", paramColumnName) - columnDefinitions.indices.forEach { i -> - if (i > 0) { - getPropertiesBuilder.add(",") + if (updateConflictActionName.isNotEmpty()) { + `override fun`(ClassNames.CONFLICT_ACTION, "getUpdateOnConflictAction") { + modifiers(public, final) + `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, updateConflictActionName) + } + } + + if (insertConflictActionName.isNotEmpty()) { + `override fun`(ClassNames.CONFLICT_ACTION, "getInsertOnConflictAction") { + modifiers(public, final) + `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, insertConflictActionName) + } } - val columnDefinition = columnDefinitions[i] - elementClassName?.let { columnDefinition.addPropertyDefinition(typeBuilder, it) } - columnDefinition.addPropertyCase(getPropertyForNameMethod) - columnDefinition.addColumnName(getPropertiesBuilder) - } - getPropertyForNameMethod.controlFlow("default:") { - addStatement("throw new \$T(\$S)", IllegalArgumentException::class.java, - "Invalid column name passed. Ensure you are calling the correct table's column") - } - getPropertyForNameMethod.endControlFlow() - getAllColumnPropertiesMethod.initializer("new \$T[]{\$L}", ClassNames.IPROPERTY, getPropertiesBuilder.build().toString()) - typeBuilder.addField(getAllColumnPropertiesMethod.build()) + val paramColumnName = "columnName" + val getPropertiesBuilder = CodeBlock.builder() - // add index properties here - for (indexGroupsDefinition in indexGroupsDefinitions) { - typeBuilder.addField(indexGroupsDefinition.fieldSpec) - } + `override fun`(ClassNames.PROPERTY, "getProperty", + param(String::class, paramColumnName)) { + modifiers(public, final) + statement("$paramColumnName = \$T.quoteIfNeeded($paramColumnName)", ClassName.get(QueryBuilder::class.java)) - typeBuilder.addMethod(getPropertyForNameMethod.build()) + switch("($paramColumnName)") { + columnDefinitions.indices.forEach { i -> + if (i > 0) { + getPropertiesBuilder.add(",") + } + val columnDefinition = columnDefinitions[i] + elementClassName?.let { columnDefinition.addPropertyDefinition(typeBuilder, it) } + columnDefinition.addPropertyCase(this) + columnDefinition.addColumnName(getPropertiesBuilder) + } - if (hasAutoIncrement || hasRowID) { - val autoIncrement = autoIncrementColumn - autoIncrement?.let { - InternalAdapterHelper.writeUpdateAutoIncrement(typeBuilder, elementClassName, autoIncrement) + default { + `throw new`(IllegalArgumentException::class, "Invalid column name passed. Ensure you are calling the correct table's column") + } + } + } + + `public static final field`(ArrayTypeName.of(ClassNames.IPROPERTY), "ALL_COLUMN_PROPERTIES") { + `=`("new \$T[]{\$L}", ClassNames.IPROPERTY, getPropertiesBuilder.build().toString()) + } + + // add index properties here + for (indexGroupsDefinition in indexGroupsDefinitions) { + addField(indexGroupsDefinition.fieldSpec) + } + + if (hasAutoIncrement || hasRowID) { + val autoIncrement = autoIncrementColumn + autoIncrement?.let { + InternalAdapterHelper.writeUpdateAutoIncrement(typeBuilder, elementClassName, autoIncrement) - typeBuilder.apply { `override fun`(Number::class, "getAutoIncrementingId", param(elementClassName!!, ModelUtils.variable)) { modifiers(public, final) addCode(autoIncrement.getSimpleAccessString()) @@ -400,49 +427,42 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } } } - } - val saveForeignKeyFields = columnDefinitions.filter { (it is ForeignKeyColumnDefinition) && it.saveForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } - if (saveForeignKeyFields.isNotEmpty()) { - val code = CodeBlock.builder() - saveForeignKeyFields.forEach { - it.appendSaveMethod(code) - } + val saveForeignKeyFields = columnDefinitions + .filter { (it is ForeignKeyColumnDefinition) && it.saveForeignKeyModel } + .map { it as ForeignKeyColumnDefinition } + if (saveForeignKeyFields.isNotEmpty()) { + val code = CodeBlock.builder() + saveForeignKeyFields.forEach { it.appendSaveMethod(code) } - typeBuilder.apply { `override fun`(TypeName.VOID, "saveForeignKeys", param(elementClassName!!, ModelUtils.variable), param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { modifiers(public, final) addCode(code.build()) } } - } - val deleteForeignKeyFields = columnDefinitions.filter { (it is ForeignKeyColumnDefinition) && it.deleteForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } - if (deleteForeignKeyFields.isNotEmpty()) { - val code = CodeBlock.builder() - deleteForeignKeyFields.forEach { - it.appendDeleteMethod(code) + val deleteForeignKeyFields = columnDefinitions + .filter { (it is ForeignKeyColumnDefinition) && it.deleteForeignKeyModel } + .map { it as ForeignKeyColumnDefinition } + if (deleteForeignKeyFields.isNotEmpty()) { + val code = CodeBlock.builder() + deleteForeignKeyFields.forEach { it.appendDeleteMethod(code) } + + `override fun`(TypeName.VOID, "deleteForeignKeys", param(elementClassName!!, ModelUtils.variable), + param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + modifiers(public, final) + addCode(code.build()) + } } - typeBuilder.`override fun`(TypeName.VOID, "deleteForeignKeys", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + + `override fun`(ArrayTypeName.of(ClassNames.IPROPERTY), "getAllColumnProperties") { modifiers(public, final) - addCode(code.build()) + `return`("ALL_COLUMN_PROPERTIES") } - } - typeBuilder.`fun`(ArrayTypeName.of(ClassNames.IPROPERTY), "getAllColumnProperties") { - modifiers(public, final) - `return`("ALL_COLUMN_PROPERTIES") - } - - if (cachingEnabled) { - - // TODO: pass in model cache loaders. + if (cachingEnabled) { - typeBuilder.apply { val singlePrimaryKey = primaryColumnDefinitions.size == 1 `override fun`(ClassNames.SINGLE_MODEL_LOADER, "createSingleModelLoader") { @@ -527,45 +547,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } } - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this) - customTypeConverterPropertyMethod.addToType(typeBuilder) - - typeBuilder.apply { - constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) - } - } - - `override fun`(elementClassName!!, "newInstance") { - modifiers(public, final) - `return`("new \$T()", elementClassName) - } - - if (!updateConflictActionName.isEmpty()) { - `override fun`(ClassNames.CONFLICT_ACTION, "getUpdateOnConflictAction") { - modifiers(public, final) - `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, updateConflictActionName) - } - } - - if (!insertConflictActionName.isEmpty()) { - `override fun`(ClassNames.CONFLICT_ACTION, "getInsertOnConflictAction") { - modifiers(public, final) - `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, insertConflictActionName) - } - } - } - methods.mapNotNull { it.methodSpec } .forEach { typeBuilder.addMethod(it) } } - - companion object { - - val DBFLOW_TABLE_TAG = "Table" - } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index 5abd3f314..59aca2eb9 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -206,16 +206,24 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { combiner.apply { - val indexName: CodeBlock + var indexName: CodeBlock if (!orderedCursorLookup) { indexName = CodeBlock.of(columnRepresentation.S) } else { indexName = CodeBlock.of(index.toString()) } - val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L, $defaultValue)", - SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) if (wrapperLevelAccessor != null) { + if (!orderedCursorLookup) { + indexName = CodeBlock.of("index_\$L", columnRepresentation) + code.addStatement("\$T \$L = cursor.getColumnIndex(\$S)", Int::class.java, indexName, + columnRepresentation) + code.beginControlFlow("if (\$1L != -1 && !cursor.isNull(\$1L))", indexName) + } else { + code.beginControlFlow("if (!cursor.isNull(\$1L))", index) + } + val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", + SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) // special case where we need to append try catch hack val isEnum = wrapperLevelAccessor is EnumColumnAccessor if (isEnum) { @@ -238,7 +246,24 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } code.endControlFlow() } + if (assignDefaultValuesFromCursor) { + code.nextControlFlow("else") + if (wrapperLevelAccessor != null) { + code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, + isDefault = true), modelBlock)) + } else { + code.addStatement(fieldLevelAccessor.set(defaultValue, modelBlock)) + } + } + code.endControlFlow() } else { + + var defaultValueBlock = defaultValue + if (!assignDefaultValuesFromCursor) { + defaultValueBlock = fieldLevelAccessor.get(modelBlock) + } + val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L, $defaultValueBlock)", + SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) code.addStatement(fieldLevelAccessor.set(cursorAccess, modelBlock)) } } diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index b6d4ca128..8267663a1 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -39,4 +39,8 @@ class SubclassAllFields(@Column var order: Int = 0) : AllFieldsModel() @Table(database = TestDatabase::class, assignDefaultValuesFromCursor = false) class DontAssignDefaultModel(@PrimaryKey var name: String? = null, @Column(getterName = "getNullableBool") var nullableBool: Boolean? = null, - @Column var index: Int = 0) \ No newline at end of file + @Column var index: Int = 0) + +@Table(database = TestDatabase::class, orderedCursorLookUp = true) +class OrderCursorModel(@PrimaryKey var id: Int = 0, @Column var name: String? = "", + @Column var age: Int = 0) \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java index c74a56702..cc3cd6a41 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java @@ -27,7 +27,7 @@ public abstract class RetrievalAdapter { public RetrievalAdapter(DatabaseDefinition databaseDefinition) { DatabaseConfig databaseConfig = FlowManager.getConfig() - .getConfigForDatabase(databaseDefinition.getAssociatedDatabaseClassFile()); + .getConfigForDatabase(databaseDefinition.getAssociatedDatabaseClassFile()); if (databaseConfig != null) { tableConfig = databaseConfig.getTableConfigForTable(getModelClass()); if (tableConfig != null) { @@ -48,10 +48,10 @@ public void load(TModel model) { public void load(TModel model, DatabaseWrapper databaseWrapper) { getSingleModelLoader().load(databaseWrapper, - SQLite.select() - .from(getModelClass()) - .where(getPrimaryConditionClause(model)).getQuery(), - model); + SQLite.select() + .from(getModelClass()) + .where(getPrimaryConditionClause(model)).getQuery(), + model); } /** diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java index 70715e091..c12317a47 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -164,7 +164,7 @@ public byte[] getBlobOrDefault(String columnName, byte[] defValue) { public boolean getBooleanOrDefault(int index, boolean defValue) { if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getInt(index) == 1; + return getBoolean(index); } else { return defValue; } @@ -173,4 +173,10 @@ public boolean getBooleanOrDefault(int index, boolean defValue) { public boolean getBooleanOrDefault(String columnName, boolean defValue) { return getBooleanOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); } + + public boolean getBoolean(int index) { + return getWrappedCursor().getInt(index) == 1; + } + } + From cccfb49614248a7370ed1810627d4a2e0fdda1e5 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 16:12:55 +1000 Subject: [PATCH 06/37] [statement] bind or null methods into the databasestatement class so generated code is much more efficient. --- .../android/dbflow/processor/SQLiteHelper.kt | 6 ++ .../definition/BaseTableDefinition.kt | 81 +++++++++---------- .../definition/DatabaseDefinition.kt | 18 ++--- .../definition/DatabaseHolderDefinition.kt | 53 +++++------- .../definition/IndexGroupsDefinition.kt | 4 +- .../definition/ManyToManyDefinition.kt | 18 ++--- .../definition/ModelViewDefinition.kt | 48 +++++------ .../definition/OneToManyDefinition.kt | 8 +- .../definition/QueryModelDefinition.kt | 32 ++++---- .../definition/column/ColumnAccessCombiner.kt | 33 ++++---- .../processor/utils/ElementExtensions.kt | 7 +- .../dbflow/sqlcipher/SQLCipherStatement.java | 3 +- .../android/dbflow/models/SimpleTestModels.kt | 2 +- .../database/AndroidDatabaseStatement.java | 2 +- .../database/BaseDatabaseStatement.java | 37 +++++++++ .../structure/database/DatabaseStatement.java | 7 ++ .../database/DatabaseStatementWrapper.java | 2 +- 17 files changed, 193 insertions(+), 168 deletions(-) create mode 100644 dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt index 2fd1cfe98..b81baa40c 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/SQLiteHelper.kt @@ -13,6 +13,9 @@ enum class SQLiteHelper { INTEGER { override val sqLiteStatementMethod = "Long" + + override val sqliteStatementWrapperMethod: String + get() = "Number" }, REAL { override val sqLiteStatementMethod = "Double" @@ -26,6 +29,9 @@ enum class SQLiteHelper { abstract val sqLiteStatementMethod: String + open val sqliteStatementWrapperMethod + get() = sqLiteStatementMethod + companion object { private val sTypeMap = hashMapOf(TypeName.BYTE to SQLiteHelper.INTEGER, diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt index 003a635f8..b7acd1c7e 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt @@ -1,14 +1,19 @@ package com.raizlabs.android.dbflow.processor.definition import com.google.common.collect.Lists +import com.grosner.kpoet.`public static final` +import com.grosner.kpoet.`return` +import com.grosner.kpoet.param +import com.grosner.kpoet.statement import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.PackagePrivateScopeColumnAccessor -import com.raizlabs.android.dbflow.processor.utils.ElementUtility -import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.processor.utils.capitalizeFirstLetter -import com.squareup.javapoet.* +import com.raizlabs.android.dbflow.processor.utils.* +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec import java.io.IOException import java.util.* import javax.annotation.processing.ProcessingEnvironment @@ -34,7 +39,7 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce var associatedTypeConverters: MutableMap> = HashMap() var globalTypeConverters: MutableMap> = HashMap() val packagePrivateList: MutableList = - Lists.newArrayList() + Lists.newArrayList() var orderedCursorLookUp: Boolean = false var assignDefaultValuesFromCursor = true @@ -86,57 +91,45 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce var count = 0 if (!packagePrivateList.isEmpty()) { - val typeBuilder = TypeSpec.classBuilder(elementClassName?.simpleName() + - databaseDefinition?.classSeparator + "Helper") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + val classSeparator = databaseDefinition?.classSeparator + val typeBuilder = TypeSpec.classBuilder("${elementClassName?.simpleName()}${classSeparator}Helper") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) for (columnDefinition in packagePrivateList) { - var helperClassName = manager.elements.getPackageOf(columnDefinition.element).toString() + - "." + ClassName.get(columnDefinition.element.enclosingElement as TypeElement).simpleName() + - databaseDefinition?.classSeparator + "Helper" + var helperClassName = "${columnDefinition.element.getPackage()}.${columnDefinition.element.enclosingElement.toClassName().simpleName()}${classSeparator}Helper" if (columnDefinition is ForeignKeyColumnDefinition) { val tableDefinition: TableDefinition? = databaseDefinition?.objectHolder?.tableDefinitionMap?.get(columnDefinition.referencedTableClassName as TypeName) if (tableDefinition != null) { - helperClassName = manager.elements.getPackageOf(tableDefinition.element).toString() + - "." + ClassName.get(tableDefinition.element as TypeElement).simpleName() + - databaseDefinition?.classSeparator + "Helper" + helperClassName = "${tableDefinition.element.getPackage()}.${ClassName.get(tableDefinition.element as TypeElement).simpleName()}${classSeparator}Helper" } } val className = ElementUtility.getClassName(helperClassName, manager) if (className != null && PackagePrivateScopeColumnAccessor.containsColumn(className, columnDefinition.columnName)) { - - var method: MethodSpec.Builder = MethodSpec.methodBuilder("get" + columnDefinition.columnName.capitalizeFirstLetter()) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .addParameter(elementTypeName, ModelUtils.variable) - .returns(columnDefinition.elementTypeName) - val samePackage = ElementUtility.isInSamePackage(manager, columnDefinition.element, this.element) - - if (samePackage) { - method.addStatement("return \$L.\$L", ModelUtils.variable, columnDefinition.elementName) - } else { - method.addStatement("return \$T.get\$L(\$L)", className, - columnDefinition.columnName.capitalizeFirstLetter(), - ModelUtils.variable) + typeBuilder.apply { + val samePackage = ElementUtility.isInSamePackage(manager, columnDefinition.element, this@BaseTableDefinition.element) + val methodName = columnDefinition.columnName.capitalizeFirstLetter() + + `public static final`(columnDefinition.elementTypeName!!, "get$methodName", + param(elementTypeName!!, ModelUtils.variable)) { + if (samePackage) { + `return`("${ModelUtils.variable}.${columnDefinition.elementName}") + } else { + `return`("\$T.get$methodName(${ModelUtils.variable})", className) + } + } + + `public static final`(TypeName.VOID, "set$methodName", + param(elementTypeName!!, ModelUtils.variable), + param(columnDefinition.elementTypeName!!, "var")) { + if (samePackage) { + statement("${ModelUtils.variable}.${columnDefinition.elementName} = var") + } else { + statement("\$T.set$methodName(${ModelUtils.variable}, var") + } + } } - typeBuilder.addMethod(method.build()) - - method = MethodSpec.methodBuilder("set" + columnDefinition.columnName.capitalizeFirstLetter()) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .addParameter(elementTypeName, ModelUtils.variable) - .addParameter(columnDefinition.elementTypeName, "var") - - if (samePackage) { - method.addStatement("\$L.\$L = \$L", ModelUtils.variable, - columnDefinition.elementName, "var") - } else { - - method.addStatement("\$T.set\$L(\$L, \$L)", className, - columnDefinition.columnName.capitalizeFirstLetter(), - ModelUtils.variable, "var") - } - typeBuilder.addMethod(method.build()) count++ } else if (className == null) { manager.logError(BaseTableDefinition::class, "Could not find classname for:" + helperClassName) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index ccc3ee3a1..3886cbd6e 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -86,26 +86,20 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi private fun validateDefinitions() { elementClassName?.let { - // TODO: validate them here before preparing them val map = HashMap() val tableValidator = TableValidator() - for (tableDefinition in manager.getTableDefinitions(it)) { - if (tableValidator.validate(ProcessorManager.manager, tableDefinition)) { - tableDefinition.elementClassName?.let { className -> map.put(className, tableDefinition) } - } - } + manager.getTableDefinitions(it) + .filter { tableValidator.validate(ProcessorManager.manager, it) } + .forEach { it.elementClassName?.let { className -> map.put(className, it) } } manager.setTableDefinitions(map, it) val modelViewDefinitionMap = HashMap() val modelViewValidator = ModelViewValidator() - for (modelViewDefinition in manager.getModelViewDefinitions(it)) { - if (modelViewValidator.validate(ProcessorManager.manager, modelViewDefinition)) { - modelViewDefinition.elementClassName?.let { className -> modelViewDefinitionMap.put(className, modelViewDefinition) } - } - } + manager.getModelViewDefinitions(it) + .filter { modelViewValidator.validate(ProcessorManager.manager, it) } + .forEach { it.elementClassName?.let { className -> modelViewDefinitionMap.put(className, it) } } manager.setModelViewDefinitions(modelViewDefinitionMap, it) } - } private fun prepareDefinitions() { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt index 048dd8804..fb55ff3f8 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseHolderDefinition.kt @@ -1,11 +1,10 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.DatabaseHandler import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec -import javax.lang.model.element.Modifier /** * Description: Top-level writer that handles writing all [DatabaseDefinition] @@ -18,7 +17,6 @@ class DatabaseHolderDefinition(private val processorManager: ProcessorManager) : init { val options = this.processorManager.processingEnvironment.options - if (options.containsKey(OPTION_TARGET_MODULE_NAME)) { className = options[OPTION_TARGET_MODULE_NAME] ?: "" } @@ -26,45 +24,36 @@ class DatabaseHolderDefinition(private val processorManager: ProcessorManager) : className += ClassNames.DATABASE_HOLDER_STATIC_CLASS_NAME } - override val typeSpec: TypeSpec - get() { - val typeBuilder = TypeSpec.classBuilder(this.className) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .superclass(ClassNames.DATABASE_HOLDER) + override val typeSpec: TypeSpec = `public final class`(this.className) { + extends(ClassNames.DATABASE_HOLDER) - val constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC) + constructor { + modifiers(public) - processorManager.getTypeConverters().forEach { - constructor.addStatement("\$L.put(\$T.class, new \$T())", - DatabaseHandler.TYPE_CONVERTER_MAP_FIELD_NAME, it.modelTypeName, it.className) + processorManager.getTypeConverters().forEach { tc -> + statement("\$L.put(\$T.class, new \$T())", + DatabaseHandler.TYPE_CONVERTER_MAP_FIELD_NAME, tc.modelTypeName, tc.className) - if (it.allowedSubTypes?.isNotEmpty() ?: false) { - it.allowedSubTypes?.forEach { subType -> - constructor.addStatement("\$L.put(\$T.class, new \$T())", - DatabaseHandler.TYPE_CONVERTER_MAP_FIELD_NAME, subType, it.className) - } + tc.allowedSubTypes?.forEach { subType -> + statement("\$L.put(\$T.class, new \$T())", + DatabaseHandler.TYPE_CONVERTER_MAP_FIELD_NAME, subType, tc.className) } } processorManager.getDatabaseHolderDefinitionList() - .sortedBy { it.databaseDefinition?.outputClassName?.simpleName() } - .forEach { databaseDefinition -> - databaseDefinition.databaseDefinition?.let { - constructor.addStatement("new \$T(this)", it.outputClassName) - } - } - - typeBuilder.addMethod(constructor.build()) - - - return typeBuilder.build() + .mapNotNull { it.databaseDefinition?.outputClassName } + .sortedBy { it.simpleName() } + .forEach { statement("new \$T(this)", it) } + this } - - fun isGarbage(): Boolean { - val filter = processorManager.getDatabaseHolderDefinitionList().filter { it.databaseDefinition?.outputClassName != null } - return filter.isEmpty() } + /** + * If none of the database holder databases exist, don't generate a holder. + */ + fun isGarbage() = processorManager.getDatabaseHolderDefinitionList() + .filter { it.databaseDefinition?.outputClassName != null }.isEmpty() + companion object { @JvmField diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt index f8af6907d..903523516 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/IndexGroupsDefinition.kt @@ -23,8 +23,8 @@ class IndexGroupsDefinition(private val tableDefinition: TableDefinition, indexG "index_$indexName") { addModifiers(public, static, final) `=` { - add("new \$T<>(\$S, \$L, \$T.class", ClassNames.INDEX_PROPERTY, - indexName, isUnique, tableDefinition.elementTypeName) + add("new \$T<>(${indexName.S}, $isUnique, \$T.class", + ClassNames.INDEX_PROPERTY, tableDefinition.elementTypeName) if (columnDefinitionList.isNotEmpty()) { add(",") diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt index dd7a6ddc6..71d587294 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt @@ -74,14 +74,14 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce } override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { - typeBuilder.addAnnotation(AnnotationSpec.builder(Table::class.java) - .addMember("database", "\$T.class", databaseTypeName).build()) + typeBuilder.apply { + addAnnotation(AnnotationSpec.builder(Table::class.java) + .addMember("database", "\$T.class", databaseTypeName).build()) - val referencedDefinition = manager.getTableDefinition(databaseTypeName, referencedTable) - val selfDefinition = manager.getTableDefinition(databaseTypeName, elementTypeName) + val referencedDefinition = manager.getTableDefinition(databaseTypeName, referencedTable) + val selfDefinition = manager.getTableDefinition(databaseTypeName, elementTypeName) - if (generateAutoIncrement) { - typeBuilder.apply { + if (generateAutoIncrement) { addField(field(`@`(PrimaryKey::class) { this["autoincrement"] = "true" }, TypeName.LONG, "_id").build()) `fun`(TypeName.LONG, "getId") { @@ -89,10 +89,10 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce `return`("_id") } } - } - referencedDefinition?.let { appendColumnDefinitions(typeBuilder, it, 0, referencedColumnName) } - selfDefinition?.let { appendColumnDefinitions(typeBuilder, it, 1, thisColumnName) } + referencedDefinition?.let { appendColumnDefinitions(this, it, 0, referencedColumnName) } + selfDefinition?.let { appendColumnDefinitions(this, it, 1, thisColumnName) } + } } override val extendsClass: TypeName? diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index da5b5c712..e463f195b 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -10,9 +10,10 @@ import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.utils.* -import com.squareup.javapoet.* +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec import javax.lang.model.element.Element -import javax.lang.model.element.Modifier import javax.lang.model.element.TypeElement import javax.lang.model.type.MirroredTypeException @@ -126,7 +127,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } if (queryFieldName.isNullOrEmpty()) { - manager.logError("%1s is missing the @ModelViewQuery field.", elementClassName) + manager.logError("$elementClassName is missing the @ModelViewQuery field.") } } @@ -137,34 +138,29 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab get() = ParameterizedTypeName.get(ClassNames.MODEL_VIEW_ADAPTER, elementClassName) override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { + typeBuilder.apply { + `public static final field`(String::class, "VIEW_NAME") { `=`(name.S) } - typeBuilder.addField(FieldSpec.builder(ClassName.get(String::class.java), - "VIEW_NAME", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("\$S", name!!).build()) - elementClassName?.let { - columnDefinitions.forEach { - columnDefinition -> - columnDefinition.addPropertyDefinition(typeBuilder, it) + elementClassName?.let { elementClassName -> + columnDefinitions.forEach { + it.addPropertyDefinition(typeBuilder, elementClassName) + } } - } - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this) - customTypeConverterPropertyMethod.addToType(typeBuilder) + val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@ModelViewDefinition) + customTypeConverterPropertyMethod.addToType(this) - typeBuilder.constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) + constructor(param(ClassNames.DATABASE_HOLDER, "holder"), + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + modifiers(public) + statement("super(databaseDefinition)") + code { + customTypeConverterPropertyMethod.addCode(this) + } } - } - methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) - - typeBuilder.apply { `override fun`(String::class, "getCreationQuery") { modifiers(public, final) `return`("\$T.\$L.getQuery()", elementClassName, queryFieldName) @@ -177,7 +173,11 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab modifiers(public, final) `return`("new \$T()", elementClassName) } + } + + methods.mapNotNull { it.methodSpec } + .forEach { typeBuilder.addMethod(it) } } override fun compareTo(other: ModelViewDefinition): Int { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 7829edccd..8081cf981 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -9,11 +9,7 @@ import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.* -import com.raizlabs.android.dbflow.processor.utils.isSubclass -import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.processor.utils.addStatement -import com.raizlabs.android.dbflow.processor.utils.annotation -import com.raizlabs.android.dbflow.processor.utils.toTypeElement +import com.raizlabs.android.dbflow.processor.utils.* import com.squareup.javapoet.* import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement @@ -127,7 +123,7 @@ class OneToManyDefinition(typeElement: ExecutableElement, private fun writeLoopWithMethod(codeBuilder: CodeBlock.Builder, methodName: String, useWrapper: Boolean) { val oneToManyMethodName = this@OneToManyDefinition.methodName codeBuilder.apply { - codeBuilder.`if`("($oneToManyMethodName != null)") { + `if`("($oneToManyMethodName != null)") { val loopClass: ClassName? = if (extendsBaseModel) ClassNames.BASE_MODEL else ClassName.get(referencedType) // need to load adapter for non-model classes diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index 61c2513c5..0b21b6ffb 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -73,33 +73,31 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana get() = ParameterizedTypeName.get(ClassNames.QUERY_MODEL_ADAPTER, elementClassName) override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { - elementClassName?.let { className -> columnDefinitions.forEach { it.addPropertyDefinition(typeBuilder, className) } } - - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this) - customTypeConverterPropertyMethod.addToType(typeBuilder) - + typeBuilder.apply { + elementClassName?.let { className -> columnDefinitions.forEach { it.addPropertyDefinition(this, className) } } + val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@QueryModelDefinition) + customTypeConverterPropertyMethod.addToType(this) - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) + InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) - typeBuilder.constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) + constructor(param(ClassNames.DATABASE_HOLDER, "holder"), + param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + modifiers(public) + statement("super(databaseDefinition)") + code { + customTypeConverterPropertyMethod.addCode(this) + } } - } - - methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } - typeBuilder.apply { `override fun`(elementClassName!!, "newInstance") { modifiers(public, final) `return`("new \$T()", elementClassName) } } + + methods.mapNotNull { it.methodSpec } + .forEach { typeBuilder.addMethod(it) } } override fun createColumnDefinitions(typeElement: TypeElement) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index 59aca2eb9..65ba501c9 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -170,19 +170,22 @@ class SqliteStatementAccessCombiner(combiner: Combiner) if (subWrapperAccessor != null) { subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) } - code.beginControlFlow("if (\$L != null) ", storedFieldAccess) - .addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, subWrapperFieldAccess) - .nextControlFlow("else") if (!defaultValue.toString().isNullOrEmpty()) { - code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, defaultValue) - } else { - code.addStatement("statement.bindNull(\$L + \$L)", index, columnRepresentation) + code.beginControlFlow("if (\$L != null) ", storedFieldAccess) } - code.endControlFlow() + + code.addStatement("statement.bind\$LOrNull(\$L + \$L, \$L)", + SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqliteStatementWrapperMethod, + index, columnRepresentation, subWrapperFieldAccess) + + if (!defaultValue.toString().isNullOrEmpty()) { + code.nextControlFlow("else") + .addStatement("statement.bind\$L(\$L + \$L, \$L)", + SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, + index, columnRepresentation, defaultValue) + .endControlFlow() + } + } else { code.addStatement("statement.bind\$L(\$L + \$L, \$L)", SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, index, @@ -248,12 +251,8 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } if (assignDefaultValuesFromCursor) { code.nextControlFlow("else") - if (wrapperLevelAccessor != null) { - code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) - } else { - code.addStatement(fieldLevelAccessor.set(defaultValue, modelBlock)) - } + code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, + isDefault = true), modelBlock)) } code.endControlFlow() } else { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt index 6560ecdb3..e0c9151b2 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt @@ -1,6 +1,7 @@ package com.raizlabs.android.dbflow.processor.utils import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.squareup.javapoet.ClassName import com.squareup.javapoet.TypeName import javax.lang.model.element.Element import javax.lang.model.element.TypeElement @@ -27,4 +28,8 @@ fun TypeMirror?.erasure(manager: ProcessorManager = ProcessorManager.manager): T fun TypeName?.toTypeElement(manager: ProcessorManager = ProcessorManager.manager): TypeElement? = manager.elements.getTypeElement(toString()) -inline fun Element?.annotation() = this?.getAnnotation(T::class.java) \ No newline at end of file +inline fun Element?.annotation() = this?.getAnnotation(T::class.java) + +fun Element?.getPackage(manager: ProcessorManager = ProcessorManager.manager) = manager.elements.getPackageOf(this) + +fun Element?.toClassName() = ClassName.get(this as TypeElement) \ No newline at end of file diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java index 1f5f094f9..7bddd3d44 100644 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java +++ b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java @@ -1,5 +1,6 @@ package com.raizlabs.android.dbflow.sqlcipher; +import com.raizlabs.android.dbflow.structure.database.BaseDatabaseStatement; import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; import net.sqlcipher.database.SQLiteStatement; @@ -8,7 +9,7 @@ * Description: Implements the methods necessary for {@link DatabaseStatement}. Delegates calls to * the contained {@link SQLiteStatement}. */ -public class SQLCipherStatement implements DatabaseStatement { +public class SQLCipherStatement extends BaseDatabaseStatement { public static SQLCipherStatement from(SQLiteStatement statement) { return new SQLCipherStatement(statement); diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index 8267663a1..851963b41 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -20,7 +20,7 @@ class TwoColumnModel(@PrimaryKey var name: String? = "", @Column var id: Int = 0 @Table(database = TestDatabase::class, allFields = true) open class AllFieldsModel(@PrimaryKey var name: String? = null, - var count: Int = 0, + var count: Int? = 0, @Column(getterName = "getTruth") var truth: Boolean = false, internal val finalName: String = "", diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java index 4516e891f..18b0fe7be 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java @@ -9,7 +9,7 @@ /** * Description: */ -public class AndroidDatabaseStatement implements DatabaseStatement { +public class AndroidDatabaseStatement extends BaseDatabaseStatement { public static AndroidDatabaseStatement from(SQLiteStatement sqLiteStatement, SQLiteDatabase database) { return new AndroidDatabaseStatement(sqLiteStatement, database); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java new file mode 100644 index 000000000..4a88f672e --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java @@ -0,0 +1,37 @@ +package com.raizlabs.android.dbflow.structure.database; + +import android.support.annotation.Nullable; + +/** + * Description: Default implementation for some {@link DatabaseStatement} methods. + */ +public abstract class BaseDatabaseStatement implements DatabaseStatement { + + @Override + public void bindStringOrNull(int index, @Nullable String name) { + if (name != null) { + bindString(index, name); + } else { + bindNull(index); + } + } + + @Override + public void bindNumberOrNull(int index, @Nullable Number name) { + if (name != null) { + bindLong(index, name.longValue()); + } else { + bindNull(index); + } + } + + @Override + public void bindDoubleOrNull(int index, @Nullable Double name) { + if (name != null) { + bindDouble(index, name); + } else { + bindNull(index); + } + } + +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java index effbd7385..bd3f91f61 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java @@ -1,6 +1,7 @@ package com.raizlabs.android.dbflow.structure.database; import android.database.sqlite.SQLiteStatement; +import android.support.annotation.Nullable; /** * Description: Abstracts out a {@link SQLiteStatement}. @@ -21,11 +22,17 @@ public interface DatabaseStatement { void bindString(int index, String name); + void bindStringOrNull(int index, @Nullable String name); + void bindNull(int index); void bindLong(int index, long aLong); + void bindNumberOrNull(int index, @Nullable Number name); + void bindDouble(int index, double aDouble); + void bindDoubleOrNull(int index, @Nullable Double name); + void bindBlob(int index, byte[] bytes); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java index bbca9caba..0fed75d9c 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java @@ -9,7 +9,7 @@ * Description: Delegates all of its calls to the contained {@link DatabaseStatement}, while * providing notification methods for when operations occur. */ -public class DatabaseStatementWrapper implements DatabaseStatement { +public class DatabaseStatementWrapper extends BaseDatabaseStatement { @NonNull private final DatabaseStatement databaseStatement; From af262917293c0daff94563a9e7eb5e89ff63db9e Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 16:30:33 +1000 Subject: [PATCH 07/37] [statement] more code consolidation. add blob or null method. --- .../definition/column/ColumnAccessCombiner.kt | 22 ++----------------- .../android/dbflow/models/SimpleTestModels.kt | 22 ++++++++++++++++++- .../database/BaseDatabaseStatement.java | 8 +++++++ .../structure/database/DatabaseStatement.java | 3 +++ 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index 65ba501c9..e35cd8a8f 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -50,12 +50,6 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { open fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int = -1) { } - - protected fun useStoredFieldRef(): Boolean { - return combiner.wrapperLevelAccessor == null && - combiner.fieldLevelAccessor !is VisibleScopeColumnAccessor - } - } class SimpleAccessCombiner(combiner: Combiner) @@ -119,12 +113,7 @@ class ContentValuesCombiner(combiner: Combiner) code.addStatement("values.put(\$1S, \$2L)", QueryBuilder.quote(columnRepresentation), fieldAccess) } else { if (defaultValue != null) { - var storedFieldAccess = CodeBlock.of("ref\$L", fieldLevelAccessor.propertyName) - if (useStoredFieldRef()) { - code.addStatement("\$T \$L = \$L", fieldTypeName, storedFieldAccess, fieldAccess) - } else { - storedFieldAccess = fieldAccess - } + val storedFieldAccess = fieldAccess var subWrapperFieldAccess = storedFieldAccess if (subWrapperAccessor != null) { subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) @@ -158,14 +147,7 @@ class SqliteStatementAccessCombiner(combiner: Combiner) index, columnRepresentation, fieldAccess) } else { if (defaultValue != null) { - var storedFieldAccess = CodeBlock.of("ref\$L", fieldLevelAccessor.propertyName) - - if (useStoredFieldRef()) { - code.addStatement("\$T \$L = \$L", fieldTypeName, storedFieldAccess, fieldAccess) - } else { - storedFieldAccess = fieldAccess - } - + var storedFieldAccess = fieldAccess var subWrapperFieldAccess = storedFieldAccess if (subWrapperAccessor != null) { subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index 851963b41..3db447d89 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -2,6 +2,8 @@ package com.raizlabs.android.dbflow.models import com.raizlabs.android.dbflow.TestDatabase import com.raizlabs.android.dbflow.annotation.* +import com.raizlabs.android.dbflow.converter.TypeConverter +import com.raizlabs.android.dbflow.data.Blob /** * Description: @@ -43,4 +45,22 @@ class DontAssignDefaultModel(@PrimaryKey var name: String? = null, @Table(database = TestDatabase::class, orderedCursorLookUp = true) class OrderCursorModel(@PrimaryKey var id: Int = 0, @Column var name: String? = "", - @Column var age: Int = 0) \ No newline at end of file + @Column var age: Int = 0) + +@Table(database = TestDatabase::class) +class TypeConverterModel(@PrimaryKey var id: Int = 0, + @Column var blob: Blob? = null, + @Column(typeConverter = CustomTypeConverter::class) var customType: CustomType? = null) + +class CustomType(var name: String? = "") + +class CustomTypeConverter : TypeConverter() { + override fun getDBValue(model: CustomType?) = model?.name + + override fun getModelValue(data: String?) = if (data == null) { + null + } else { + CustomType(data) + } + +} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java index 4a88f672e..bb793360b 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java @@ -34,4 +34,12 @@ public void bindDoubleOrNull(int index, @Nullable Double name) { } } + @Override + public void bindBlobOrNull(int index, @Nullable byte[] bytes) { + if (bytes != null) { + bindBlob(index, bytes); + } else { + bindNull(index); + } + } } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java index bd3f91f61..4064f7f3c 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java @@ -35,4 +35,7 @@ public interface DatabaseStatement { void bindDoubleOrNull(int index, @Nullable Double name); void bindBlob(int index, byte[] bytes); + + void bindBlobOrNull(int index, @Nullable byte[] bytes); + } From 586375fdfc713c3069fd8fe1bef5d44dd23262d1 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sat, 15 Apr 2017 16:36:46 +1000 Subject: [PATCH 08/37] [statement] dont generate regular sqlite bind method if no autoincrementing ids or listeners. --- .../dbflow/processor/definition/Methods.kt | 114 +++++++++--------- .../dbflow/structure/ModelAdapter.java | 5 + 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 442e05c7b..13ef71988 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -32,11 +32,11 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini override val methodSpec: MethodSpec? get() { val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "bindToInsertValues" else "bindToContentValues") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.CONTENT_VALUES, PARAM_CONTENT_VALUES) - .addParameter(baseTableDefinition.parameterClassName, ModelUtils.variable) - .returns(TypeName.VOID) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.CONTENT_VALUES, PARAM_CONTENT_VALUES) + .addParameter(baseTableDefinition.parameterClassName, ModelUtils.variable) + .returns(TypeName.VOID) var retMethodBuilder: MethodSpec.Builder? = methodBuilder @@ -49,7 +49,7 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini if (implementsContentValuesListener) { methodBuilder.addStatement("\$L.onBindTo\$LValues(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) + ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) } } else { if (baseTableDefinition.hasAutoIncrement || baseTableDefinition.hasRowID) { @@ -64,7 +64,7 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini methodBuilder.addStatement("bindToInsertValues(\$L, \$L)", PARAM_CONTENT_VALUES, ModelUtils.variable) if (implementsContentValuesListener) { methodBuilder.addStatement("\$L.onBindTo\$LValues(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) + ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) } } @@ -81,14 +81,14 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini */ class BindToStatementMethod(private val tableDefinition: TableDefinition, private val isInsert: Boolean) : MethodDefinition { - override val methodSpec: MethodSpec + override val methodSpec: MethodSpec? get() { val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "bindToInsertStatement" else "bindToStatement") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.DATABASE_STATEMENT, PARAM_STATEMENT) - .addParameter(tableDefinition.parameterClassName, - ModelUtils.variable).returns(TypeName.VOID) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.DATABASE_STATEMENT, PARAM_STATEMENT) + .addParameter(tableDefinition.parameterClassName, + ModelUtils.variable).returns(TypeName.VOID) // write the reference method if (isInsert) { @@ -103,7 +103,7 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat if (tableDefinition.implementsSqlStatementListener) { methodBuilder.addStatement("\$L.onBindTo\$LStatement(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) + ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) } } else { var start = 0 @@ -113,12 +113,14 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat methodBuilder.addStatement("int start = 0") methodBuilder.addCode(it.getSQLiteStatementMethod(AtomicInteger(++start))) } - } - - methodBuilder.addStatement("bindToInsertStatement(\$L, \$L, \$L)", PARAM_STATEMENT, ModelUtils.variable, start) - if (tableDefinition.implementsSqlStatementListener) { + methodBuilder.addStatement("bindToInsertStatement(\$L, \$L, \$L)", PARAM_STATEMENT, ModelUtils.variable, start) + } else if (tableDefinition.implementsSqlStatementListener) { + methodBuilder.addStatement("bindToInsertStatement(\$L, \$L, \$L)", PARAM_STATEMENT, ModelUtils.variable, start) methodBuilder.addStatement("\$L.onBindTo\$LStatement(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) + ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) + } else { + // don't generate method + return null } } @@ -140,12 +142,12 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method override val methodSpec: MethodSpec get() { val methodBuilder = MethodSpec.methodBuilder("getCreationQuery") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassName.get(String::class.java)) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .returns(ClassName.get(String::class.java)) val creationBuilder = CodeBlock.builder().add("CREATE TABLE IF NOT EXISTS ") - .add(QueryBuilder.quote(tableDefinition.tableName)).add("(") + .add(QueryBuilder.quote(tableDefinition.tableName)).add("(") (0..tableDefinition.columnDefinitions.size - 1).forEach { i -> if (i > 0) { @@ -208,7 +210,7 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method foreignKeyBlocks.add(foreignKeyBuilder.build()) tableNameBlocks.add(CodeBlock.builder().add("\$T.getTableName(\$T.class)", - ClassNames.FLOW_MANAGER, foreignKeyColumnDefinition.referencedTableClassName).build()) + ClassNames.FLOW_MANAGER, foreignKeyColumnDefinition.referencedTableClassName).build()) referenceBuilder.add("(") for (j in 0..foreignKeyColumnDefinition._foreignKeyReferenceDefinitionList.size - 1) { @@ -219,7 +221,7 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method referenceBuilder.add("\$L", QueryBuilder.quote(referenceDefinition.foreignColumnName)) } referenceBuilder.add(") ON UPDATE \$L ON DELETE \$L", foreignKeyColumnDefinition.onUpdate.name.replace("_", " "), - foreignKeyColumnDefinition.onDelete.name.replace("_", " ")) + foreignKeyColumnDefinition.onDelete.name.replace("_", " ")) referenceKeyBlocks.add(referenceBuilder.build()) } @@ -250,13 +252,13 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab val customTypeConverters = baseTableDefinition.associatedTypeConverters.keys customTypeConverters.forEach { typeBuilder.addField(FieldSpec.builder(it, "typeConverter" + it.simpleName(), - Modifier.PRIVATE, Modifier.FINAL).initializer("new \$T()", it).build()) + Modifier.PRIVATE, Modifier.FINAL).initializer("new \$T()", it).build()) } val globalTypeConverters = baseTableDefinition.globalTypeConverters.keys globalTypeConverters.forEach { typeBuilder.addField(FieldSpec.builder(it, "global_typeConverter" + it.simpleName(), - Modifier.PRIVATE, Modifier.FINAL).build()) + Modifier.PRIVATE, Modifier.FINAL).build()) } @@ -270,7 +272,7 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab val firstDef = def?.get(0) firstDef?.typeConverterElementNames?.forEach { elementName -> code.addStatement("global_typeConverter\$L = (\$T) \$L.getTypeConverterForClass(\$T.class)", - it.simpleName(), it, "holder", elementName).build() + it.simpleName(), it, "holder", elementName).build() } } return code @@ -285,10 +287,10 @@ class ExistenceMethod(private val tableDefinition: BaseTableDefinition) : Method override val methodSpec: MethodSpec get() { val methodBuilder = MethodSpec.methodBuilder("exists") - .addAnnotation(Override::class.java) - .addParameter(tableDefinition.parameterClassName, ModelUtils.variable) - .addParameter(ClassNames.DATABASE_WRAPPER, "wrapper") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(TypeName.BOOLEAN) + .addAnnotation(Override::class.java) + .addParameter(tableDefinition.parameterClassName, ModelUtils.variable) + .addParameter(ClassNames.DATABASE_WRAPPER, "wrapper") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(TypeName.BOOLEAN) // only quick check if enabled. var primaryColumn = tableDefinition.autoIncrementColumn if (primaryColumn == null) { @@ -313,8 +315,8 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p return null // dont write method here because we reuse the compiled statement query method } val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "getInsertStatementQuery" else "getCompiledStatementQuery") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassName.get(String::class.java)) + .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .returns(ClassName.get(String::class.java)) val codeBuilder = CodeBlock.builder().add("INSERT ") if (!tableDefinition.insertConflictActionName.isEmpty()) { @@ -323,7 +325,7 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p codeBuilder.add("INTO ").add(QueryBuilder.quote(tableDefinition.tableName)) val isSingleAutoincrement = tableDefinition.hasAutoIncrement && tableDefinition.columnDefinitions.size == 1 - && isInsert + && isInsert codeBuilder.add("(") @@ -375,10 +377,10 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) get() { val methodBuilder = MethodSpec.methodBuilder("loadFromCursor") .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.FLOW_CURSOR, PARAM_CURSOR) - .addParameter(baseTableDefinition.parameterClassName, - ModelUtils.variable).returns(TypeName.VOID) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.FLOW_CURSOR, PARAM_CURSOR) + .addParameter(baseTableDefinition.parameterClassName, + ModelUtils.variable).returns(TypeName.VOID) val index = AtomicInteger(0) baseTableDefinition.columnDefinitions.forEach { @@ -433,18 +435,18 @@ class OneToManyDeleteMethod(private val tableDefinition: TableDefinition, } builder.addStatement("boolean successful = super.delete(\$L\$L)", ModelUtils.variable, - if (useWrapper) ", " + ModelUtils.wrapper else "") + if (useWrapper) ", " + ModelUtils.wrapper else "") tableDefinition.oneToManyDefinitions.forEach { it.writeDelete(builder, useWrapper) } builder.addStatement("return successful") val delete = MethodSpec.methodBuilder("delete") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.elementClassName, ModelUtils.variable) - .addCode(builder.build()) - .returns(TypeName.BOOLEAN) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(tableDefinition.elementClassName, ModelUtils.variable) + .addCode(builder.build()) + .returns(TypeName.BOOLEAN) if (useWrapper) { delete.addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) } @@ -473,12 +475,12 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, } code.addStatement("super.\$L(\$L\$L)", methodName, - ModelUtils.variable, - if (useWrapper) ", " + ModelUtils.wrapper else "") + ModelUtils.variable, + if (useWrapper) ", " + ModelUtils.wrapper else "") if (tableDefinition.cachingEnabled) { code.addStatement("getModelCache().addModel(getCachingId(\$L), \$L)", ModelUtils.variable, - ModelUtils.variable) + ModelUtils.variable) } for (oneToManyDefinition in tableDefinition.oneToManyDefinitions) { @@ -490,10 +492,10 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, } val builder = MethodSpec.methodBuilder(methodName) - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.elementClassName, ModelUtils.variable) - .addCode(code.build()) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(tableDefinition.elementClassName, ModelUtils.variable) + .addCode(code.build()) if (methodName == METHOD_INSERT) { builder.returns(ClassName.LONG) builder.addStatement("return rowId") @@ -527,10 +529,10 @@ class PrimaryConditionMethod(private val tableDefinition: BaseTableDefinition) : override val methodSpec: MethodSpec? get() { val methodBuilder = MethodSpec.methodBuilder("getPrimaryConditionClause") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.parameterClassName, - ModelUtils.variable).returns(ClassNames.OPERATOR_GROUP) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(tableDefinition.parameterClassName, + ModelUtils.variable).returns(ClassNames.OPERATOR_GROUP) val code = CodeBlock.builder() code.addStatement("\$T clause = \$T.clause()", ClassNames.OPERATOR_GROUP, ClassNames.OPERATOR_GROUP) tableDefinition.primaryColumnDefinitions.forEach { diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java index 401d7b8be..36bbc616b 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java @@ -217,6 +217,11 @@ public void bindToContentValues(ContentValues contentValues, TModel tModel) { bindToInsertValues(contentValues, tModel); } + @Override + public void bindToStatement(DatabaseStatement sqLiteStatement, TModel tModel) { + bindToInsertStatement(sqLiteStatement, tModel, 0); + } + /** * If a {@link Model} has an auto-incrementing primary key, then * this method will be overridden. From c55edb3de4ffa302e9a8679d4ba6ae4788d494f9 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sun, 16 Apr 2017 18:13:29 +1000 Subject: [PATCH 09/37] [definition] dont add holder to table def constructor if not used. --- .../definition/BaseTableDefinition.kt | 13 +++-- .../definition/DatabaseDefinition.kt | 9 +++- .../definition/ManyToManyDefinition.kt | 13 +++-- .../dbflow/processor/definition/Methods.kt | 15 +++--- .../processor/definition/TableDefinition.kt | 7 ++- .../definition/column/ColumnAccessCombiner.kt | 49 ++++++++----------- .../definition/column/ColumnAccessor.kt | 6 +-- .../dbflow/processor/utils/StringUtils.kt | 8 +-- .../dbflow/processor/utils/WriterUtils.kt | 7 +-- .../dbflow/sqlcipher/SQLCipherStatement.java | 4 +- .../android/dbflow/models/SimpleTestModels.kt | 7 ++- .../database/AndroidDatabaseStatement.java | 4 +- .../database/BaseDatabaseStatement.java | 23 +++++---- .../structure/database/DatabaseStatement.java | 10 ++-- .../database/DatabaseStatementWrapper.java | 4 +- 15 files changed, 99 insertions(+), 80 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt index b7acd1c7e..1bf79bf5c 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt @@ -9,7 +9,10 @@ import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.PackagePrivateScopeColumnAccessor -import com.raizlabs.android.dbflow.processor.utils.* +import com.raizlabs.android.dbflow.processor.utils.ElementUtility +import com.raizlabs.android.dbflow.processor.utils.ModelUtils +import com.raizlabs.android.dbflow.processor.utils.getPackage +import com.raizlabs.android.dbflow.processor.utils.toClassName import com.squareup.javapoet.ClassName import com.squareup.javapoet.JavaFile import com.squareup.javapoet.TypeName @@ -46,11 +49,13 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce var classElementLookUpMap: MutableMap = mutableMapOf() - val modelClassName: String + val modelClassName = typeElement.simpleName.toString() var databaseDefinition: DatabaseDefinition? = null + val hasGlobalTypeConverters + get() = globalTypeConverters.isNotEmpty() + init { - this.modelClassName = typeElement.simpleName.toString() columnDefinitions = ArrayList() } @@ -108,7 +113,7 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce if (className != null && PackagePrivateScopeColumnAccessor.containsColumn(className, columnDefinition.columnName)) { typeBuilder.apply { val samePackage = ElementUtility.isInSamePackage(manager, columnDefinition.element, this@BaseTableDefinition.element) - val methodName = columnDefinition.columnName.capitalizeFirstLetter() + val methodName = columnDefinition.columnName.capitalize() `public static final`(columnDefinition.elementTypeName!!, "get$methodName", param(elementTypeName!!, ModelUtils.variable)) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index 3886cbd6e..e8d8390ad 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -127,8 +127,13 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi for (tableDefinition in manager.getTableDefinitions(elementClassName)) { statement("holder.putDatabaseForTable(\$T.class, this)", tableDefinition.elementClassName) statement("\$L.put(\$S, \$T.class)", DatabaseHandler.MODEL_NAME_MAP, tableDefinition.tableName, tableDefinition.elementClassName) - statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, - tableDefinition.elementClassName, tableDefinition.outputClassName) + if (tableDefinition.hasGlobalTypeConverters) { + statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, + tableDefinition.elementClassName, tableDefinition.outputClassName) + } else { + statement("\$L.put(\$T.class, new \$T(this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, + tableDefinition.elementClassName, tableDefinition.outputClassName) + } } for (modelViewDefinition in manager.getModelViewDefinitions(elementClassName)) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt index 71d587294..40db6c4a5 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt @@ -7,7 +7,10 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager -import com.raizlabs.android.dbflow.processor.utils.* +import com.raizlabs.android.dbflow.processor.utils.annotation +import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty +import com.raizlabs.android.dbflow.processor.utils.lower +import com.raizlabs.android.dbflow.processor.utils.toTypeElement import com.squareup.javapoet.AnnotationSpec import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec @@ -18,8 +21,8 @@ import javax.lang.model.type.TypeMirror /** * Description: Generates the Model class that is used in a many to many. */ -class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, processorManager: ProcessorManager, - manyToMany: ManyToMany = element.annotation()!!) +class ManyToManyDefinition(element: TypeElement, processorManager: ProcessorManager, + manyToMany: ManyToMany = element.annotation()!!) : BaseDefinition(element, processorManager) { internal var referencedTable: TypeName @@ -116,11 +119,11 @@ class ManyToManyDefinition @JvmOverloads constructor(element: TypeElement, proce } `@`(ForeignKey::class) { member("saveForeignKeyModel", saveForeignKeyModels.toString()) } } - `fun`(referencedDefinition.elementClassName!!, "get${fieldName.capitalizeFirstLetter()}") { + `fun`(referencedDefinition.elementClassName!!, "get${fieldName.capitalize()}") { modifiers(public, final) `return`(fieldName.L) } - `fun`(TypeName.VOID, "set${fieldName.capitalizeFirstLetter()}", + `fun`(TypeName.VOID, "set${fieldName.capitalize()}", param(referencedDefinition.elementClassName!!, "param")) { modifiers(public, final) statement("$fieldName = param") diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 13ef71988..7024bf5b1 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -1,5 +1,7 @@ package com.raizlabs.android.dbflow.processor.definition +import com.grosner.kpoet.S +import com.grosner.kpoet.statement import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.utils.ModelUtils import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty @@ -225,15 +227,15 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method referenceKeyBlocks.add(referenceBuilder.build()) } - val codeBuilder = CodeBlock.builder().add("return \$S", creationBuilder.build().toString()) + val codeBuilder = CodeBlock.builder() + .add("return \$S", creationBuilder.build().toString()) if (foreignSize > 0) { for (i in 0..foreignSize - 1) { codeBuilder.add("+ \$S + \$L + \$S", foreignKeyBlocks[i], tableNameBlocks[i], referenceKeyBlocks[i]) } } - codeBuilder.add(" + \$S", ");").add(";\n") - + codeBuilder.add(" + ${");".S};\n") methodBuilder.addCode(codeBuilder.build()) @@ -247,7 +249,6 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTableDefinition) : TypeAdder, CodeAdder { - override fun addToType(typeBuilder: TypeSpec.Builder) { val customTypeConverters = baseTableDefinition.associatedTypeConverters.keys customTypeConverters.forEach { @@ -260,8 +261,6 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab typeBuilder.addField(FieldSpec.builder(it, "global_typeConverter" + it.simpleName(), Modifier.PRIVATE, Modifier.FINAL).build()) } - - } override fun addCode(code: CodeBlock.Builder): CodeBlock.Builder { @@ -271,8 +270,8 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab val def = baseTableDefinition.globalTypeConverters[it] val firstDef = def?.get(0) firstDef?.typeConverterElementNames?.forEach { elementName -> - code.addStatement("global_typeConverter\$L = (\$T) \$L.getTypeConverterForClass(\$T.class)", - it.simpleName(), it, "holder", elementName).build() + code.statement("global_typeConverter${it.simpleName()} " + + "= (\$T) holder.getTypeConverterForClass(\$T.class)", it, elementName) } } return code diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index 96d8ecb16..d745f68d1 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -350,8 +350,11 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@TableDefinition) customTypeConverterPropertyMethod.addToType(this) - constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { + constructor { + if (hasGlobalTypeConverters) { + addParameter(param(ClassNames.DATABASE_HOLDER, "holder").build()) + } + addParameter(param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition").build()) modifiers(public) statement("super(databaseDefinition)") code { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index e35cd8a8f..6722fe0f7 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -1,6 +1,9 @@ package com.raizlabs.android.dbflow.processor.definition.column import com.grosner.kpoet.S +import com.grosner.kpoet.`else` +import com.grosner.kpoet.`if` +import com.grosner.kpoet.statement import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper import com.raizlabs.android.dbflow.processor.utils.ModelUtils @@ -140,38 +143,26 @@ class SqliteStatementAccessCombiner(combiner: Combiner) modelBlock: CodeBlock) { combiner.apply { val fieldAccess: CodeBlock = getFieldAccessBlock(code, modelBlock) + val wrapperMethod = SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqliteStatementWrapperMethod + val statementMethod = SQLiteHelper[fieldTypeName].sqLiteStatementMethod - if (fieldTypeName.isPrimitive) { - code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, fieldAccess) - } else { - if (defaultValue != null) { - var storedFieldAccess = fieldAccess - var subWrapperFieldAccess = storedFieldAccess - if (subWrapperAccessor != null) { - subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) - } - if (!defaultValue.toString().isNullOrEmpty()) { - code.beginControlFlow("if (\$L != null) ", storedFieldAccess) - } - - code.addStatement("statement.bind\$LOrNull(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqliteStatementWrapperMethod, - index, columnRepresentation, subWrapperFieldAccess) - + code.apply { + if (fieldTypeName.isPrimitive) { + statement("statement.bind$statementMethod($index + $columnRepresentation, $fieldAccess)") + } else { + val subWrapperFieldAccess = subWrapperAccessor?.get(fieldAccess) ?: fieldAccess if (!defaultValue.toString().isNullOrEmpty()) { - code.nextControlFlow("else") - .addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, - index, columnRepresentation, defaultValue) - .endControlFlow() + `if`("$fieldAccess != null") { + statement("statement.bind$wrapperMethod($index + $columnRepresentation," + + " $subWrapperFieldAccess)") + }.`else` { + statement("statement.bind$statementMethod($index + $columnRepresentation," + + " $defaultValue)") + } + } else { + statement("statement.bind${wrapperMethod}OrNull($index + $columnRepresentation," + + " $subWrapperFieldAccess)") } - - } else { - code.addStatement("statement.bind\$L(\$L + \$L, \$L)", - SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqLiteStatementMethod, index, - columnRepresentation, fieldAccess) } } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt index af042df31..05659e775 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt @@ -95,9 +95,9 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette get() = if (getterName.isNullOrEmpty()) { if (propertyName != null) { if (useIsForPrivateBooleans && !propertyName.startsWith("is", ignoreCase = true)) { - "is" + propertyName.capitalizeFirstLetter() + "is" + propertyName.capitalize() } else if (!useIsForPrivateBooleans && !propertyName.startsWith("get", ignoreCase = true)) { - "get" + propertyName.capitalizeFirstLetter() + "get" + propertyName.capitalize() } else propertyName.lower() } else { "" @@ -114,7 +114,7 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette } else if (useIsForPrivateBooleans && setElementName.startsWith("Is")) { setElementName = setElementName.replaceFirst("Is".toRegex(), "") } - "set" + setElementName.capitalizeFirstLetter() + "set" + setElementName.capitalize() } else setElementName.lower() } else setterName } else "" diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/StringUtils.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/StringUtils.kt index eae853ddc..f03f7fa61 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/StringUtils.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/StringUtils.kt @@ -4,19 +4,19 @@ package com.raizlabs.android.dbflow.processor.utils * Description: */ fun String?.isNullOrEmpty(): Boolean { - return this == null || this.trim { it <= ' ' }.length == 0 || this == "null" + return this == null || this.trim { it <= ' ' }.isEmpty() || this == "null" } fun String?.capitalizeFirstLetter(): String { - if (this == null || this.trim { it <= ' ' }.length == 0) { + if (this == null || this.trim { it <= ' ' }.isEmpty()) { return this ?: "" } - return this.substring(0, 1).toUpperCase() + this.substring(1) + return this.capitalize() } fun String?.lower(): String { - if (this == null || this.trim { it <= ' ' }.length == 0) { + if (this == null || this.trim { it <= ' ' }.isEmpty()) { return this ?: "" } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/WriterUtils.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/WriterUtils.kt index f32633539..281dedeca 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/WriterUtils.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/WriterUtils.kt @@ -1,8 +1,8 @@ package com.raizlabs.android.dbflow.processor.utils +import com.grosner.kpoet.javaFile import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.BaseDefinition -import com.squareup.javapoet.JavaFile import java.io.IOException /** @@ -13,13 +13,14 @@ object WriterUtils { fun writeBaseDefinition(baseDefinition: BaseDefinition, processorManager: ProcessorManager): Boolean { var success = false try { - val javaFile = JavaFile.builder(baseDefinition.packageName, baseDefinition.typeSpec).build() - javaFile.writeTo(processorManager.processingEnvironment.filer) + javaFile(baseDefinition.packageName) { baseDefinition.typeSpec } + .writeTo(processorManager.processingEnvironment.filer) success = true } catch (e: IOException) { // ignored } catch (i: IllegalStateException) { processorManager.logError(WriterUtils::class, "Found error for class:" + baseDefinition.elementName) + processorManager.logError(WriterUtils::class, i.message) } return success diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java index 7bddd3d44..b2d72267a 100644 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java +++ b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java @@ -56,8 +56,8 @@ public long executeInsert() { } @Override - public void bindString(int index, String name) { - statement.bindString(index, name); + public void bindString(int index, String s) { + statement.bindString(index, s); } @Override diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index 3db447d89..b0ea4b9ad 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -63,4 +63,9 @@ class CustomTypeConverter : TypeConverter() { CustomType(data) } -} \ No newline at end of file +} + +@Table(database = TestDatabase::class) +class DefaultModel(@PrimaryKey @Column(defaultValue = "5") var id: Int? = 0, + @Column(defaultValue = "5.0") var location: Double? = 0.0, + @Column(defaultValue = "\"String\"") var name: String? = "") \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java index 18b0fe7be..739b8a187 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java @@ -78,8 +78,8 @@ public long executeInsert() { } @Override - public void bindString(int index, String name) { - statement.bindString(index, name); + public void bindString(int index, String s) { + statement.bindString(index, s); } @Override diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java index bb793360b..14d91c735 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java @@ -8,27 +8,32 @@ public abstract class BaseDatabaseStatement implements DatabaseStatement { @Override - public void bindStringOrNull(int index, @Nullable String name) { - if (name != null) { - bindString(index, name); + public void bindStringOrNull(int index, @Nullable String s) { + if (s != null) { + bindString(index, s); } else { bindNull(index); } } @Override - public void bindNumberOrNull(int index, @Nullable Number name) { - if (name != null) { - bindLong(index, name.longValue()); + public void bindNumber(int index, @Nullable Number number) { + bindNumberOrNull(index, number); + } + + @Override + public void bindNumberOrNull(int index, @Nullable Number number) { + if (number != null) { + bindLong(index, number.longValue()); } else { bindNull(index); } } @Override - public void bindDoubleOrNull(int index, @Nullable Double name) { - if (name != null) { - bindDouble(index, name); + public void bindDoubleOrNull(int index, @Nullable Double aDouble) { + if (aDouble != null) { + bindDouble(index, aDouble); } else { bindNull(index); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java index 4064f7f3c..5af8fdad0 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java @@ -20,19 +20,21 @@ public interface DatabaseStatement { long executeInsert(); - void bindString(int index, String name); + void bindString(int index, String s); - void bindStringOrNull(int index, @Nullable String name); + void bindStringOrNull(int index, @Nullable String s); void bindNull(int index); void bindLong(int index, long aLong); - void bindNumberOrNull(int index, @Nullable Number name); + void bindNumber(int index, @Nullable Number number); + + void bindNumberOrNull(int index, @Nullable Number number); void bindDouble(int index, double aDouble); - void bindDoubleOrNull(int index, @Nullable Double name); + void bindDoubleOrNull(int index, @Nullable Double aDouble); void bindBlob(int index, byte[] bytes); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java index 0fed75d9c..8a03d7f50 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java @@ -62,8 +62,8 @@ public long executeInsert() { } @Override - public void bindString(int index, String name) { - databaseStatement.bindString(index, name); + public void bindString(int index, String s) { + databaseStatement.bindString(index, s); } @Override From e37c43ee719e3a59107040cee6f2d75ce16a4f87 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sun, 16 Apr 2017 18:54:05 +1000 Subject: [PATCH 10/37] [definition] considerable cut down code gen line count in the database files. --- .../definition/BaseTableDefinition.kt | 25 ++++++++++++--- .../definition/DatabaseDefinition.kt | 32 +++++++++---------- .../definition/ModelViewDefinition.kt | 12 +------ .../definition/QueryModelDefinition.kt | 12 +------ .../processor/definition/TableDefinition.kt | 15 +-------- .../dbflow/config/DatabaseDefinition.java | 16 ++++++++++ 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt index 1bf79bf5c..961f30e4c 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt @@ -1,10 +1,8 @@ package com.raizlabs.android.dbflow.processor.definition import com.google.common.collect.Lists -import com.grosner.kpoet.`public static final` -import com.grosner.kpoet.`return` -import com.grosner.kpoet.param -import com.grosner.kpoet.statement +import com.grosner.kpoet.* +import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition @@ -90,6 +88,25 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce return "global_typeConverter" + typeConverterName.simpleName() } + fun writeConstructor(typeBuilder: TypeSpec.Builder) { + typeBuilder.apply { + val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@BaseTableDefinition) + customTypeConverterPropertyMethod.addToType(this) + + constructor { + if (hasGlobalTypeConverters) { + addParameter(param(ClassNames.DATABASE_HOLDER, "holder").build()) + } + addParameter(param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition").build()) + modifiers(public) + statement("super(databaseDefinition)") + code { + customTypeConverterPropertyMethod.addCode(this) + } + } + } + } + @Throws(IOException::class) fun writePackageHelper(processingEnvironment: ProcessingEnvironment) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index e8d8390ad..38cdd2260 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -124,28 +124,28 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi modifiers(public) val elementClassName = this@DatabaseDefinition.elementClassName if (elementClassName != null) { - for (tableDefinition in manager.getTableDefinitions(elementClassName)) { - statement("holder.putDatabaseForTable(\$T.class, this)", tableDefinition.elementClassName) - statement("\$L.put(\$S, \$T.class)", DatabaseHandler.MODEL_NAME_MAP, tableDefinition.tableName, tableDefinition.elementClassName) - if (tableDefinition.hasGlobalTypeConverters) { - statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, - tableDefinition.elementClassName, tableDefinition.outputClassName) + for (definition in manager.getTableDefinitions(elementClassName)) { + if (definition.hasGlobalTypeConverters) { + statement("addModelAdapter(new \$T(holder, this), holder)", definition.outputClassName) } else { - statement("\$L.put(\$T.class, new \$T(this))", DatabaseHandler.MODEL_ADAPTER_MAP_FIELD_NAME, - tableDefinition.elementClassName, tableDefinition.outputClassName) + statement("addModelAdapter(new \$T(this), holder)", definition.outputClassName) } } - for (modelViewDefinition in manager.getModelViewDefinitions(elementClassName)) { - statement("holder.putDatabaseForTable(\$T.class, this)", modelViewDefinition.elementClassName) - statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.MODEL_VIEW_ADAPTER_MAP_FIELD_NAME, - modelViewDefinition.elementClassName, modelViewDefinition.outputClassName) + for (definition in manager.getModelViewDefinitions(elementClassName)) { + if (definition.hasGlobalTypeConverters) { + statement("addModelViewAdapter(new \$T(holder, this), holder)", definition.outputClassName) + } else { + statement("addModelViewAdapter(new \$T(this), holder)", definition.outputClassName) + } } - for (queryModelDefinition in manager.getQueryModelDefinitions(elementClassName)) { - statement("holder.putDatabaseForTable(\$T.class, this)", queryModelDefinition.elementClassName) - statement("\$L.put(\$T.class, new \$T(holder, this))", DatabaseHandler.QUERY_MODEL_ADAPTER_MAP_FIELD_NAME, - queryModelDefinition.elementClassName, queryModelDefinition.outputClassName) + for (definition in manager.getQueryModelDefinitions(elementClassName)) { + if (definition.hasGlobalTypeConverters) { + statement("addQueryModelAdapter(new \$T(holder, this), holder)", definition.outputClassName) + } else { + statement("addQueryModelAdapter(new \$T(this), holder)", definition.outputClassName) + } } val migrationDefinitionMap = manager.getMigrationsForDatabase(elementClassName) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index e463f195b..f7ecb9ae4 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -147,17 +147,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } } - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@ModelViewDefinition) - customTypeConverterPropertyMethod.addToType(this) - - constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) - } - } + writeConstructor(this) InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index 0b21b6ffb..dfc035c12 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -76,19 +76,9 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana typeBuilder.apply { elementClassName?.let { className -> columnDefinitions.forEach { it.addPropertyDefinition(this, className) } } - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@QueryModelDefinition) - customTypeConverterPropertyMethod.addToType(this) - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) - constructor(param(ClassNames.DATABASE_HOLDER, "holder"), - param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition")) { - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) - } - } + writeConstructor(this) `override fun`(elementClassName!!, "newInstance") { modifiers(public, final) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index d745f68d1..d4589fca6 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -347,20 +347,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab InternalAdapterHelper.writeGetModelClass(this, elementClassName) InternalAdapterHelper.writeGetTableName(this, tableName) - val customTypeConverterPropertyMethod = CustomTypeConverterPropertyMethod(this@TableDefinition) - customTypeConverterPropertyMethod.addToType(this) - - constructor { - if (hasGlobalTypeConverters) { - addParameter(param(ClassNames.DATABASE_HOLDER, "holder").build()) - } - addParameter(param(ClassNames.BASE_DATABASE_DEFINITION_CLASSNAME, "databaseDefinition").build()) - modifiers(public) - statement("super(databaseDefinition)") - code { - customTypeConverterPropertyMethod.addCode(this) - } - } + writeConstructor(this) `override fun`(elementClassName!!, "newInstance") { modifiers(public, final) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java index 8c5570460..a45264220 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java @@ -103,6 +103,22 @@ public DatabaseDefinition() { } } + protected void addModelAdapter(ModelAdapter modelAdapter, DatabaseHolder holder) { + holder.putDatabaseForTable(modelAdapter.getModelClass(), this); + modelTableNames.put(modelAdapter.getTableName(), modelAdapter.getModelClass()); + modelAdapters.put(modelAdapter.getModelClass(), modelAdapter); + } + + protected void addModelViewAdapter(ModelViewAdapter modelViewAdapter, DatabaseHolder holder) { + holder.putDatabaseForTable(modelViewAdapter.getModelClass(), this); + modelViewAdapterMap.put(modelViewAdapter.getModelClass(), modelViewAdapter); + } + + protected void addQueryModelAdapter(QueryModelAdapter queryModelAdapter, DatabaseHolder holder) { + holder.putDatabaseForTable(queryModelAdapter.getModelClass(), this); + queryModelAdapterMap.put(queryModelAdapter.getModelClass(), queryModelAdapter); + } + /** * @return a list of all model classes in this database. */ From 25981e1efecc35ec45bac1f5d766d44aaf5df152 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Sun, 16 Apr 2017 19:46:08 +1000 Subject: [PATCH 11/37] [definition] simplify more spaghetti methods. consolidate construction for base tables. dont include holder in constructor if not used. also generate even less code. --- .../definition/BaseTableDefinition.kt | 16 +- .../definition/DatabaseDefinition.kt | 23 +-- .../definition/InternalAdapterHelper.kt | 94 ---------- .../definition/ManyToManyDefinition.kt | 4 +- .../definition/MigrationDefinition.kt | 2 +- .../definition/ModelViewDefinition.kt | 8 +- .../definition/QueryModelDefinition.kt | 2 +- .../processor/definition/TableDefinition.kt | 166 ++++++++++-------- 8 files changed, 116 insertions(+), 199 deletions(-) delete mode 100644 dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt index 961f30e4c..5c62aaede 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/BaseTableDefinition.kt @@ -7,14 +7,8 @@ import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.definition.column.PackagePrivateScopeColumnAccessor -import com.raizlabs.android.dbflow.processor.utils.ElementUtility -import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.processor.utils.getPackage -import com.raizlabs.android.dbflow.processor.utils.toClassName -import com.squareup.javapoet.ClassName -import com.squareup.javapoet.JavaFile -import com.squareup.javapoet.TypeName -import com.squareup.javapoet.TypeSpec +import com.raizlabs.android.dbflow.processor.utils.* +import com.squareup.javapoet.* import java.io.IOException import java.util.* import javax.annotation.processing.ProcessingEnvironment @@ -107,6 +101,12 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce } } + fun writeGetModelClass(typeBuilder: TypeSpec.Builder, modelClassName: ClassName?) = typeBuilder.apply { + `override fun`(ParameterizedTypeName.get(ClassName.get(Class::class.java), modelClassName), "getModelClass") { + modifiers(public, final) + `return`("\$T.class", modelClassName) + } + } @Throws(IOException::class) fun writePackageHelper(processingEnvironment: ProcessingEnvironment) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index 38cdd2260..c1bb9d61b 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -104,17 +104,9 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi private fun prepareDefinitions() { elementClassName?.let { - for (tableDefinition in manager.getTableDefinitions(it)) { - tableDefinition.prepareForWrite() - } - - for (modelViewDefinition in manager.getModelViewDefinitions(it)) { - modelViewDefinition.prepareForWrite() - } - - for (queryModelDefinition in manager.getQueryModelDefinitions(it)) { - queryModelDefinition.prepareForWrite() - } + manager.getTableDefinitions(it).forEach(TableDefinition::prepareForWrite) + manager.getModelViewDefinitions(it).forEach(ModelViewDefinition::prepareForWrite) + manager.getQueryModelDefinitions(it).forEach(QueryModelDefinition::prepareForWrite) } } @@ -122,8 +114,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi builder.constructor(param(ClassNames.DATABASE_HOLDER, "holder")) { modifiers(public) - val elementClassName = this@DatabaseDefinition.elementClassName - if (elementClassName != null) { + this@DatabaseDefinition.elementClassName?.let { elementClassName -> for (definition in manager.getTableDefinitions(elementClassName)) { if (definition.hasGlobalTypeConverters) { statement("addModelAdapter(new \$T(holder, this), holder)", definition.outputClassName) @@ -158,11 +149,9 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi Collections.sort(migrationDefinitions, { o1, o2 -> Integer.valueOf(o2.priority)!!.compareTo(o1.priority) }) statement("\$T migrations\$L = new \$T()", ParameterizedTypeName.get(ClassName.get(List::class.java), ClassNames.MIGRATION), version, ParameterizedTypeName.get(ClassName.get(ArrayList::class.java), ClassNames.MIGRATION)) - statement("\$L.put(\$L, migrations\$L)", DatabaseHandler.MIGRATION_FIELD_NAME, - version, version) + statement("${DatabaseHandler.MIGRATION_FIELD_NAME}.put($version, migrations$version)") for (migrationDefinition in migrationDefinitions) { - statement("migrations\$L.add(new \$T\$L)", version, migrationDefinition.elementClassName, - migrationDefinition.constructorName) + statement("migrations$version.add(new \$T${migrationDefinition.constructorName})", migrationDefinition.elementClassName) } } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt deleted file mode 100644 index 95af0c54c..000000000 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.raizlabs.android.dbflow.processor.definition - -import com.grosner.kpoet.* -import com.raizlabs.android.dbflow.processor.ClassNames -import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.raizlabs.android.dbflow.processor.definition.column.DefinitionUtils -import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.processor.utils.`override fun` -import com.raizlabs.android.dbflow.sql.QueryBuilder -import com.squareup.javapoet.* -import javax.lang.model.element.Modifier - -/** - * Description: Assists in writing methods for adapters - */ -object InternalAdapterHelper { - - fun writeGetModelClass(typeBuilder: TypeSpec.Builder, modelClassName: ClassName?) = typeBuilder.apply { - `override fun`(ParameterizedTypeName.get(ClassName.get(Class::class.java), modelClassName), "getModelClass") { - modifiers(public, final) - `return`("\$T.class", modelClassName) - } - } - - fun writeGetTableName(typeBuilder: TypeSpec.Builder, tableName: String?) = typeBuilder.apply { - `override fun`(String::class, "getTableName") { - modifiers(public, final) - `return`(QueryBuilder.quote(tableName).S) - } - } - - fun writeUpdateAutoIncrement(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, - autoIncrementDefinition: ColumnDefinition) = typeBuilder.apply { - `override fun`(TypeName.VOID, "updateAutoIncrement", param(modelClassName!!, ModelUtils.variable), - param(Number::class, "id")) { - modifiers(public, final) - addCode(autoIncrementDefinition.updateAutoIncrementMethod) - } - } - - fun writeGetCachingId(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, - primaryColumns: List) { - if (primaryColumns.size > 1) { - var methodBuilder: MethodSpec.Builder = MethodSpec.methodBuilder("getCachingColumnValuesFromModel") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ArrayTypeName.of(Any::class.java), "inValues") - .addParameter(modelClassName, ModelUtils.variable) - for (i in primaryColumns.indices) { - val column = primaryColumns[i] - methodBuilder.addCode(column.getColumnAccessString(i)) - } - methodBuilder.addStatement("return \$L", "inValues").returns(ArrayTypeName.of(Any::class.java)) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingColumnValuesFromCursor") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ArrayTypeName.of(Any::class.java), "inValues") - .addParameter(ClassNames.FLOW_CURSOR, "cursor") - for (i in primaryColumns.indices) { - val column = primaryColumns[i] - val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) - methodBuilder.addStatement("inValues[\$L] = \$L.\$L(\$L.getColumnIndex(\$S))", i, LoadFromCursorMethod.PARAM_CURSOR, - method, LoadFromCursorMethod.PARAM_CURSOR, column.columnName) - } - methodBuilder.addStatement("return \$L", "inValues").returns(ArrayTypeName.of(Any::class.java)) - typeBuilder.addMethod(methodBuilder.build()) - } else { - // single primary key - var methodBuilder: MethodSpec.Builder = MethodSpec.methodBuilder("getCachingColumnValueFromModel") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - methodBuilder.addCode(primaryColumns[0].getSimpleAccessString()) - .returns(Any::class.java) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingColumnValueFromCursor") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.FLOW_CURSOR, "cursor") - val column = primaryColumns[0] - val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) - methodBuilder.addStatement("return \$L.\$L(\$L.getColumnIndex(\$S))", LoadFromCursorMethod.PARAM_CURSOR, - method, LoadFromCursorMethod.PARAM_CURSOR, column.columnName).returns(Any::class.java) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingId") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - .addStatement("return getCachingColumnValueFromModel(\$L)", - ModelUtils.variable).returns(TypeName.OBJECT) - typeBuilder.addMethod(methodBuilder.build()) - } - } - -} diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt index 40db6c4a5..256c1d112 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ManyToManyDefinition.kt @@ -58,14 +58,14 @@ class ManyToManyDefinition(element: TypeElement, processorManager: ProcessorMana if (!thisColumnName.isNullOrEmpty() && !referencedColumnName.isNullOrEmpty() && thisColumnName == referencedColumnName) { - manager.logError(ManyToManyDefinition::class, "The thisTableColumnName and referenceTableColumnName" + "cannot be the same") + manager.logError(ManyToManyDefinition::class, "The thisTableColumnName and referenceTableColumnName cannot be the same") } } fun prepareForWrite() { val databaseDefinition = manager.getDatabaseHolderDefinition(databaseTypeName)?.databaseDefinition if (databaseDefinition == null) { - manager.logError("DatabaseDefinition was null for : " + elementName) + manager.logError("DatabaseDefinition was null for : $elementName") } else { if (generatedTableClassName.isNullOrEmpty()) { val referencedOutput = getElementClassName(referencedTable.toTypeElement(manager)) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt index 3a8fcc75a..14d521abf 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/MigrationDefinition.kt @@ -45,7 +45,7 @@ class MigrationDefinition(processorManager: ProcessorManager, typeElement: TypeE priority = migration.priority val elements = typeElement.enclosedElements - for (element in elements) { + elements.forEach { element -> if (element is ExecutableElement && element.simpleName.toString() == "") { if (!constructorName.isNullOrEmpty()) { manager.logError(MigrationDefinition::class, "Migrations cannot have more than one constructor. " + diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index f7ecb9ae4..152e5ff62 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -113,7 +113,7 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab } } else if (variableElement.annotation() != null) { if (!queryFieldName.isNullOrEmpty()) { - manager.logError("Found duplicate ") + manager.logError("Found duplicate queryField name: $queryFieldName for $elementClassName") } ensureVisibleStatic(variableElement, typeElement, "ModelViewQuery") @@ -142,14 +142,12 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab `public static final field`(String::class, "VIEW_NAME") { `=`(name.S) } elementClassName?.let { elementClassName -> - columnDefinitions.forEach { - it.addPropertyDefinition(typeBuilder, elementClassName) - } + columnDefinitions.forEach { it.addPropertyDefinition(typeBuilder, elementClassName) } } writeConstructor(this) - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) + writeGetModelClass(typeBuilder, elementClassName) `override fun`(String::class, "getCreationQuery") { modifiers(public, final) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index dfc035c12..cd4dc7383 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -76,7 +76,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana typeBuilder.apply { elementClassName?.let { className -> columnDefinitions.forEach { it.addPropertyDefinition(this, className) } } - InternalAdapterHelper.writeGetModelClass(typeBuilder, elementClassName) + writeGetModelClass(typeBuilder, elementClassName) writeConstructor(this) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index d4589fca6..00ee59258 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -1,7 +1,6 @@ package com.raizlabs.android.dbflow.processor.definition import com.google.common.collect.Lists -import com.google.common.collect.Maps import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.processor.ClassNames @@ -9,6 +8,7 @@ import com.raizlabs.android.dbflow.processor.ColumnValidator import com.raizlabs.android.dbflow.processor.OneToManyValidator import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition +import com.raizlabs.android.dbflow.processor.definition.column.DefinitionUtils import com.raizlabs.android.dbflow.processor.definition.column.ForeignKeyColumnDefinition import com.raizlabs.android.dbflow.processor.utils.* import com.raizlabs.android.dbflow.sql.QueryBuilder @@ -35,10 +35,10 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab var primaryKeyConflictActionName: String = "" - var _primaryColumnDefinitions: MutableList - var foreignKeyDefinitions: MutableList - var uniqueGroupsDefinitions: MutableList - var indexGroupsDefinitions: MutableList + val _primaryColumnDefinitions = mutableListOf() + val foreignKeyDefinitions = mutableListOf() + val uniqueGroupsDefinitions = mutableListOf() + val indexGroupsDefinitions = mutableListOf() var implementsContentValuesListener = false @@ -56,26 +56,20 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab var allFields = false var useIsForPrivateBooleans: Boolean = false - val columnMap: MutableMap = Maps.newHashMap() + val columnMap = mutableMapOf() - var columnUniqueMap: MutableMap> - = Maps.newHashMap>() + var columnUniqueMap = mutableMapOf>() - var oneToManyDefinitions: MutableList = ArrayList() + var oneToManyDefinitions = mutableListOf() - var inheritedColumnMap: MutableMap = HashMap() - var inheritedFieldNameList: MutableList = ArrayList() - var inheritedPrimaryKeyMap: MutableMap = HashMap() + var inheritedColumnMap = hashMapOf() + var inheritedFieldNameList = mutableListOf() + var inheritedPrimaryKeyMap = hashMapOf() var hasPrimaryConstructor = false init { - _primaryColumnDefinitions = ArrayList() - foreignKeyDefinitions = ArrayList() - uniqueGroupsDefinitions = ArrayList() - indexGroupsDefinitions = ArrayList() - element.annotation
()?.let { table -> this.tableName = table.name @@ -170,21 +164,20 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab setOutputClassName("${it.classSeparator}Table") // globular default - var insertConflict: ConflictAction? = table.insertConflict + var insertConflict = table.insertConflict if (insertConflict == ConflictAction.NONE && it.insertConflict != ConflictAction.NONE) { - insertConflict = it.insertConflict + insertConflict = it.insertConflict ?: ConflictAction.NONE } - var updateConflict: ConflictAction? = table.updateConflict - if (updateConflict == ConflictAction.NONE - && it.updateConflict != ConflictAction.NONE) { - updateConflict = it.updateConflict + var updateConflict = table.updateConflict + if (updateConflict == ConflictAction.NONE && it.updateConflict != ConflictAction.NONE) { + updateConflict = it.updateConflict ?: ConflictAction.NONE } val primaryKeyConflict = table.primaryKeyConflict - insertConflictActionName = if (insertConflict == ConflictAction.NONE) "" else insertConflict?.name ?: "" - updateConflictActionName = if (updateConflict == ConflictAction.NONE) "" else updateConflict?.name ?: "" + insertConflictActionName = if (insertConflict == ConflictAction.NONE) "" else insertConflict.name + updateConflictActionName = if (updateConflict == ConflictAction.NONE) "" else updateConflict.name primaryKeyConflictActionName = if (primaryKeyConflict == ConflictAction.NONE) "" else primaryKeyConflict.name } @@ -197,11 +190,8 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab manager.logError("A duplicate unique group with number %1s was found for %1s", uniqueGroup.groupNumber, tableName) } val definition = UniqueGroupsDefinition(uniqueGroup) - for (columnDefinition in columnDefinitions) { - if (columnDefinition.uniqueGroups.contains(definition.number)) { - definition.addColumnDefinition(columnDefinition) - } - } + columnDefinitions.filter { it.uniqueGroups.contains(definition.number) } + .forEach { definition.addColumnDefinition(it) } uniqueGroupsDefinitions.add(definition) uniqueNumbersSet.add(uniqueGroup.groupNumber) } @@ -213,11 +203,8 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab manager.logError(TableDefinition::class, "A duplicate unique index number %1s was found for %1s", indexGroup.number, elementName) } val definition = IndexGroupsDefinition(this, indexGroup) - columnDefinitions.forEach { - if (it.indexGroups.contains(definition.indexNumber)) { - definition.columnDefinitionList.add(it) - } - } + columnDefinitions.filter { it.indexGroups.contains(definition.indexNumber) } + .forEach { definition.columnDefinitionList.add(it) } indexGroupsDefinitions.add(definition) uniqueNumbersSet.add(indexGroup.number) } @@ -292,14 +279,12 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (!columnDefinition.uniqueGroups.isEmpty()) { val groups = columnDefinition.uniqueGroups for (group in groups) { - var groupList: MutableList? = columnUniqueMap[group] + var groupList = columnUniqueMap[group] if (groupList == null) { - groupList = ArrayList() + groupList = mutableSetOf() columnUniqueMap.put(group, groupList) } - if (!groupList.contains(columnDefinition)) { - groupList.add(columnDefinition) - } + groupList.add(columnDefinition) } } @@ -328,7 +313,6 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } } } - } override val primaryColumnDefinitions: List @@ -344,11 +328,14 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab override fun onWriteDefinition(typeBuilder: TypeSpec.Builder) { typeBuilder.apply { - InternalAdapterHelper.writeGetModelClass(this, elementClassName) - InternalAdapterHelper.writeGetTableName(this, tableName) - + writeGetModelClass(this, elementClassName) writeConstructor(this) + `override fun`(String::class, "getTableName") { + modifiers(public, final) + `return`(QueryBuilder.quote(tableName).S) + } + `override fun`(elementClassName!!, "newInstance") { modifiers(public, final) `return`("new \$T()", elementClassName) @@ -357,14 +344,14 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (updateConflictActionName.isNotEmpty()) { `override fun`(ClassNames.CONFLICT_ACTION, "getUpdateOnConflictAction") { modifiers(public, final) - `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, updateConflictActionName) + `return`("\$T.$updateConflictActionName", ClassNames.CONFLICT_ACTION) } } if (insertConflictActionName.isNotEmpty()) { `override fun`(ClassNames.CONFLICT_ACTION, "getInsertOnConflictAction") { modifiers(public, final) - `return`("\$T.\$L", ClassNames.CONFLICT_ACTION, insertConflictActionName) + `return`("\$T.$insertConflictActionName", ClassNames.CONFLICT_ACTION) } } @@ -405,7 +392,11 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (hasAutoIncrement || hasRowID) { val autoIncrement = autoIncrementColumn autoIncrement?.let { - InternalAdapterHelper.writeUpdateAutoIncrement(typeBuilder, elementClassName, autoIncrement) + `override fun`(TypeName.VOID, "updateAutoIncrement", param(elementClassName!!, ModelUtils.variable), + param(Number::class, "id")) { + modifiers(public, final) + addCode(autoIncrement.updateAutoIncrementMethod) + } `override fun`(Number::class, "getAutoIncrementingId", param(elementClassName!!, ModelUtils.variable)) { modifiers(public, final) @@ -481,21 +472,55 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `return`(true.L) } - val primaries = primaryColumnDefinitions - InternalAdapterHelper.writeGetCachingId(this, elementClassName, primaries) + val primaryColumns = primaryColumnDefinitions + if (primaryColumns.size > 1) { + `override fun`(ArrayTypeName.of(Any::class.java), "getCachingColumnValuesFromModel", + param(ArrayTypeName.of(Any::class.java), "inValues"), + param(elementClassName!!, ModelUtils.variable)) { + modifiers(public, final) + for (i in primaryColumns.indices) { + val column = primaryColumns[i] + addCode(column.getColumnAccessString(i)) + } - `override fun`(ArrayTypeName.of(ClassName.get(String::class.java)), "createCachingColumns") { - modifiers(public, final) - var columns = "return new String[]{" - primaries.indices.forEach { i -> - val column = primaries[i] - if (i > 0) { - columns += "," + `return`("inValues") + } + + `override fun`(ArrayTypeName.of(Any::class.java), "getCachingColumnValuesFromCursor", + param(ArrayTypeName.of(Any::class.java), "inValues"), + param(ClassNames.FLOW_CURSOR, "cursor")) { + modifiers(public, final) + for (i in primaryColumns.indices) { + val column = primaryColumns[i] + val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) + statement("inValues[$i] = ${LoadFromCursorMethod.PARAM_CURSOR}" + + ".$method(${LoadFromCursorMethod.PARAM_CURSOR}.getColumnIndex(${column.columnName.S}))") } - columns += "\"" + QueryBuilder.quoteIfNeeded(column.columnName) + "\"" + `return`("inValues") + } + } else { + // single primary key + `override fun`(Any::class, "getCachingColumnValueFromModel", + param(elementClassName!!, ModelUtils.variable)) { + modifiers(public, final) + addCode(primaryColumns[0].getSimpleAccessString()) + } + + `override fun`(Any::class, "getCachingColumnValueFromCursor", param(ClassNames.FLOW_CURSOR, "cursor")) { + modifiers(public, final) + val column = primaryColumns[0] + val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) + `return`("${LoadFromCursorMethod.PARAM_CURSOR}.$method(${LoadFromCursorMethod.PARAM_CURSOR}.getColumnIndex(${column.columnName.S}))") + } + `override fun`(Any::class, "getCachingId", param(elementClassName!!, ModelUtils.variable)) { + modifiers(public, final) + `return`("getCachingColumnValueFromModel(${ModelUtils.variable})") } - columns += "}" - statement(columns) + } + + `override fun`(ArrayTypeName.of(ClassName.get(String::class.java)), "createCachingColumns") { + modifiers(public, final) + `return`("new String[]{${primaryColumns.joinToString { QueryBuilder.quoteIfNeeded(it.columnName).S }}}") } if (cacheSize != Table.DEFAULT_CACHE_SIZE) { @@ -509,7 +534,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `override fun`(ParameterizedTypeName.get(ClassNames.MODEL_CACHE, elementClassName, WildcardTypeName.subtypeOf(Any::class.java)), "createModelCache") { modifiers(public, final) - `return`("\$T.\$L", elementClassName, customCacheFieldName) + `return`("\$T.$customCacheFieldName", elementClassName) } } @@ -517,21 +542,20 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `override fun`(ParameterizedTypeName.get(ClassNames.MULTI_KEY_CACHE_CONVERTER, WildcardTypeName.subtypeOf(Any::class.java)), "getCacheConverter") { modifiers(public, final) - `return`("\$T.\$L", elementClassName, customMultiCacheFieldName) + `return`("\$T.$customMultiCacheFieldName", elementClassName) } } - `override fun`(TypeName.VOID, "reloadRelationships", - param(elementClassName!!, ModelUtils.variable), - param(ClassNames.FLOW_CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { - modifiers(public, final) - code { - val noIndex = AtomicInteger(-1) - - foreignKeyDefinitions.forEach { - add(it.getLoadFromCursorMethod(false, noIndex)) + if (foreignKeyDefinitions.isNotEmpty()) { + `override fun`(TypeName.VOID, "reloadRelationships", + param(elementClassName!!, ModelUtils.variable), + param(ClassNames.FLOW_CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { + modifiers(public, final) + code { + val noIndex = AtomicInteger(-1) + foreignKeyDefinitions.forEach { add(it.getLoadFromCursorMethod(false, noIndex)) } + this } - this } } } From 3b82742c126e52d03ee8251f7c63b9728c95f985 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Wed, 19 Apr 2017 15:38:40 +1000 Subject: [PATCH 12/37] [processor] convert more methods into KPoet definitions. --- .../dbflow/processor/definition/Methods.kt | 307 +++++++----------- .../definition/OneToManyDefinition.kt | 16 +- .../definition/column/ColumnAccessCombiner.kt | 221 +++++++------ .../definition/column/ColumnDefinition.kt | 75 ++--- .../column/ForeignKeyAccessCombiner.kt | 17 +- .../dbflow/processor/utils/CodeExtensions.kt | 17 +- .../processor/utils/JavaPoetExtensions.kt | 24 +- .../android/dbflow/config/FlowManager.java | 10 +- .../dbflow/structure/ModelAdapter.java | 19 +- 9 files changed, 332 insertions(+), 374 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 7024bf5b1..2dba57058 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -1,9 +1,11 @@ package com.raizlabs.android.dbflow.processor.definition -import com.grosner.kpoet.S -import com.grosner.kpoet.statement +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.processor.ClassNames +import com.raizlabs.android.dbflow.processor.definition.column.wrapperCommaIfBaseModel import com.raizlabs.android.dbflow.processor.utils.ModelUtils +import com.raizlabs.android.dbflow.processor.utils.`override fun` +import com.raizlabs.android.dbflow.processor.utils.codeBlock import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.* @@ -142,50 +144,35 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat class CreationQueryMethod(private val tableDefinition: TableDefinition) : MethodDefinition { override val methodSpec: MethodSpec - get() { - val methodBuilder = MethodSpec.methodBuilder("getCreationQuery") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassName.get(String::class.java)) + get() = `override fun`(String::class, "getCreationQuery") { + modifiers(public, final) - val creationBuilder = CodeBlock.builder().add("CREATE TABLE IF NOT EXISTS ") - .add(QueryBuilder.quote(tableDefinition.tableName)).add("(") + val foreignSize = tableDefinition.foreignKeyDefinitions.size - (0..tableDefinition.columnDefinitions.size - 1).forEach { i -> - if (i > 0) { - creationBuilder.add(",") + val creationBuilder = codeBlock { + add("CREATE TABLE IF NOT EXISTS ${QueryBuilder.quote(tableDefinition.tableName)}(") + add(tableDefinition.columnDefinitions.joinToString { it.creationName.toString() }) + tableDefinition.uniqueGroupsDefinitions.forEach { + if (!it.columnDefinitionList.isEmpty()) add(it.creationName) } - creationBuilder.add(tableDefinition.columnDefinitions[i].creationName) - } - - tableDefinition.uniqueGroupsDefinitions.forEach { - if (!it.columnDefinitionList.isEmpty()) creationBuilder.add(it.creationName) - } - - if (!tableDefinition.hasAutoIncrement) { - val primarySize = tableDefinition.primaryColumnDefinitions.size - for (i in 0..primarySize - 1) { - if (i == 0) { - creationBuilder.add(", PRIMARY KEY(") - } - - if (i > 0) { - creationBuilder.add(",") - } - val primaryDefinition = tableDefinition.primaryColumnDefinitions[i] - creationBuilder.add(primaryDefinition.primaryKeyName) - - if (i == primarySize - 1) { - creationBuilder.add(")") + if (!tableDefinition.hasAutoIncrement) { + val primarySize = tableDefinition.primaryColumnDefinitions.size + if (primarySize > 0) { + add(", PRIMARY KEY(${tableDefinition.primaryColumnDefinitions.joinToString { it.primaryKeyName.toString() }})") if (!tableDefinition.primaryKeyConflictActionName.isNullOrEmpty()) { - creationBuilder.add(" ON CONFLICT " + tableDefinition.primaryKeyConflictActionName) + add(" ON CONFLICT ${tableDefinition.primaryKeyConflictActionName}") } } } + if (foreignSize == 0) { + add(")") + } + this } - val foreignSize = tableDefinition.foreignKeyDefinitions.size + val codeBuilder = CodeBlock.builder() + .add("return ${creationBuilder.toString().S}") val foreignKeyBlocks = ArrayList() val tableNameBlocks = ArrayList() @@ -194,52 +181,33 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method for (i in 0..foreignSize - 1) { val foreignKeyBuilder = CodeBlock.builder() val referenceBuilder = CodeBlock.builder() - val foreignKeyColumnDefinition = tableDefinition.foreignKeyDefinitions[i] - - foreignKeyBuilder.add(", FOREIGN KEY(") - - (0..foreignKeyColumnDefinition._foreignKeyReferenceDefinitionList.size - 1).forEach { j -> - if (j > 0) { - foreignKeyBuilder.add(",") - } - val referenceDefinition = foreignKeyColumnDefinition._foreignKeyReferenceDefinitionList[j] - foreignKeyBuilder.add("\$L", QueryBuilder.quote(referenceDefinition.columnName)) - } - + val fk = tableDefinition.foreignKeyDefinitions[i] - foreignKeyBuilder.add(") REFERENCES ") + foreignKeyBlocks.add(foreignKeyBuilder.apply { + add(", FOREIGN KEY(") + add(fk._foreignKeyReferenceDefinitionList.joinToString { QueryBuilder.quote(it.columnName) }) + add(") REFERENCES ") + }.build()) - foreignKeyBlocks.add(foreignKeyBuilder.build()) + tableNameBlocks.add(codeBlock { add("\$T.getTableName(\$T.class)", ClassNames.FLOW_MANAGER, fk.referencedTableClassName) }) - tableNameBlocks.add(CodeBlock.builder().add("\$T.getTableName(\$T.class)", - ClassNames.FLOW_MANAGER, foreignKeyColumnDefinition.referencedTableClassName).build()) - - referenceBuilder.add("(") - for (j in 0..foreignKeyColumnDefinition._foreignKeyReferenceDefinitionList.size - 1) { - if (j > 0) { - referenceBuilder.add(", ") - } - val referenceDefinition = foreignKeyColumnDefinition._foreignKeyReferenceDefinitionList[j] - referenceBuilder.add("\$L", QueryBuilder.quote(referenceDefinition.foreignColumnName)) - } - referenceBuilder.add(") ON UPDATE \$L ON DELETE \$L", foreignKeyColumnDefinition.onUpdate.name.replace("_", " "), - foreignKeyColumnDefinition.onDelete.name.replace("_", " ")) - referenceKeyBlocks.add(referenceBuilder.build()) + referenceKeyBlocks.add(referenceBuilder.apply { + add("(") + add(fk._foreignKeyReferenceDefinitionList.joinToString { QueryBuilder.quote(it.foreignColumnName) }) + add(") ON UPDATE ${fk.onUpdate.name.replace("_", " ")} ON DELETE ${fk.onDelete.name.replace("_", " ")}") + }.build()) } - val codeBuilder = CodeBlock.builder() - .add("return \$S", creationBuilder.build().toString()) - if (foreignSize > 0) { for (i in 0..foreignSize - 1) { - codeBuilder.add("+ \$S + \$L + \$S", foreignKeyBlocks[i], tableNameBlocks[i], referenceKeyBlocks[i]) + codeBuilder.add("+ ${foreignKeyBlocks[i].S} + ${tableNameBlocks[i]} + ${referenceKeyBlocks[i].S}") } + codeBuilder.add(" + ${");".S};\n") + } else { + codeBuilder.add(";\n") } - codeBuilder.add(" + ${");".S};\n") - methodBuilder.addCode(codeBuilder.build()) - - return methodBuilder.build() + addCode(codeBuilder.build()) } } @@ -283,23 +251,20 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab */ class ExistenceMethod(private val tableDefinition: BaseTableDefinition) : MethodDefinition { - override val methodSpec: MethodSpec - get() { - val methodBuilder = MethodSpec.methodBuilder("exists") - .addAnnotation(Override::class.java) - .addParameter(tableDefinition.parameterClassName, ModelUtils.variable) - .addParameter(ClassNames.DATABASE_WRAPPER, "wrapper") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL).returns(TypeName.BOOLEAN) - // only quick check if enabled. - var primaryColumn = tableDefinition.autoIncrementColumn - if (primaryColumn == null) { - primaryColumn = tableDefinition.primaryColumnDefinitions[0] + override val methodSpec + get() = `override fun`(TypeName.BOOLEAN, "exists", + param(tableDefinition.parameterClassName!!, ModelUtils.variable), + param(ClassNames.DATABASE_WRAPPER, "wrapper")) { + modifiers(public, final) + code { + // only quick check if enabled. + var primaryColumn = tableDefinition.autoIncrementColumn + if (primaryColumn == null) { + primaryColumn = tableDefinition.primaryColumnDefinitions[0] + } + primaryColumn.appendExistenceMethod(this) + this } - - val code = CodeBlock.builder() - primaryColumn.appendExistenceMethod(code) - methodBuilder.addCode(code.build()) - return methodBuilder.build() } } @@ -373,36 +338,32 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) : MethodDefinition { override val methodSpec: MethodSpec - get() { - val methodBuilder = MethodSpec.methodBuilder("loadFromCursor") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.FLOW_CURSOR, PARAM_CURSOR) - .addParameter(baseTableDefinition.parameterClassName, - ModelUtils.variable).returns(TypeName.VOID) - + get() = `override fun`(TypeName.VOID, "loadFromCursor", + param(ClassNames.FLOW_CURSOR, PARAM_CURSOR), + param(baseTableDefinition.parameterClassName!!, ModelUtils.variable)) { val index = AtomicInteger(0) baseTableDefinition.columnDefinitions.forEach { - methodBuilder.addCode(it.getLoadFromCursorMethod(true, index)) + addCode(it.getLoadFromCursorMethod(true, index)) index.incrementAndGet() } if (baseTableDefinition is TableDefinition) { - val codeBuilder = CodeBlock.builder() - baseTableDefinition.oneToManyDefinitions - .filter { it.isLoad } - .forEach { it.writeLoad(codeBuilder) } - methodBuilder.addCode(codeBuilder.build()) + code { + baseTableDefinition.oneToManyDefinitions + .filter { it.isLoad } + .forEach { it.writeLoad(this) } + this + } } if (baseTableDefinition is TableDefinition && baseTableDefinition.implementsLoadFromCursorListener) { - methodBuilder.addStatement("\$L.onLoadFromCursor(\$L)", ModelUtils.variable, PARAM_CURSOR) + statement("${ModelUtils.variable}.onLoadFromCursor($PARAM_CURSOR)") } - - return methodBuilder.build() + this } + companion object { val PARAM_CURSOR = "cursor" @@ -417,39 +378,24 @@ class OneToManyDeleteMethod(private val tableDefinition: TableDefinition, override val methodSpec: MethodSpec? get() { - var shouldWrite = false - for (oneToManyDefinition in tableDefinition.oneToManyDefinitions) { - if (oneToManyDefinition.isDelete) { - shouldWrite = true - break - } - } - + val shouldWrite = tableDefinition.oneToManyDefinitions.any { it.isDelete } if (shouldWrite || tableDefinition.cachingEnabled) { + return `override fun`(TypeName.BOOLEAN, "delete", + param(tableDefinition.elementClassName!!, ModelUtils.variable)) { + modifiers(public, final) + if (useWrapper) { + addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) + } + if (tableDefinition.cachingEnabled) { + statement("getModelCache().removeModel(getCachingId(${ModelUtils.variable}))") + } - val builder = CodeBlock.builder() - - if (tableDefinition.cachingEnabled) { - builder.addStatement("getModelCache().removeModel(getCachingId(\$L))", ModelUtils.variable) - } - - builder.addStatement("boolean successful = super.delete(\$L\$L)", ModelUtils.variable, - if (useWrapper) ", " + ModelUtils.wrapper else "") - - tableDefinition.oneToManyDefinitions.forEach { it.writeDelete(builder, useWrapper) } + statement("boolean successful = super.delete(${ModelUtils.variable}${wrapperCommaIfBaseModel(useWrapper)})") - builder.addStatement("return successful") + tableDefinition.oneToManyDefinitions.forEach { it.writeDelete(this, useWrapper) } - val delete = MethodSpec.methodBuilder("delete") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.elementClassName, ModelUtils.variable) - .addCode(builder.build()) - .returns(TypeName.BOOLEAN) - if (useWrapper) { - delete.addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) + `return`("successful") } - return delete.build() } return null } @@ -465,48 +411,46 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, override val methodSpec: MethodSpec? get() { if (!tableDefinition.oneToManyDefinitions.isEmpty() || tableDefinition.cachingEnabled) { - val code = CodeBlock.builder() - - if (methodName == METHOD_INSERT) { - code.add("long rowId = ") - } else if (methodName == METHOD_UPDATE || methodName == METHOD_SAVE) { - code.add("boolean successful = ") + var retType = TypeName.BOOLEAN + var retStatement = "rowId" + when (methodName) { + METHOD_INSERT -> { + retType = ClassName.LONG + retStatement = "successful" + } } - code.addStatement("super.\$L(\$L\$L)", methodName, - ModelUtils.variable, - if (useWrapper) ", " + ModelUtils.wrapper else "") + return `override fun`(retType, methodName, + param(tableDefinition.elementClassName!!, ModelUtils.variable)) { + modifiers(public, final) - if (tableDefinition.cachingEnabled) { - code.addStatement("getModelCache().addModel(getCachingId(\$L), \$L)", ModelUtils.variable, - ModelUtils.variable) - } + if (useWrapper) { + addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) + } + code { + if (methodName == METHOD_INSERT) { + add("long rowId = ") + } else if (methodName == METHOD_UPDATE || methodName == METHOD_SAVE) { + add("boolean successful = ") + } + statement("super.$methodName(${ModelUtils.variable}${wrapperCommaIfBaseModel(useWrapper)})") - for (oneToManyDefinition in tableDefinition.oneToManyDefinitions) { - when (methodName) { - METHOD_SAVE -> oneToManyDefinition.writeSave(code, useWrapper) - METHOD_UPDATE -> oneToManyDefinition.writeUpdate(code, useWrapper) - METHOD_INSERT -> oneToManyDefinition.writeInsert(code, useWrapper) + if (tableDefinition.cachingEnabled) { + statement("getModelCache().addModel(getCachingId(${ModelUtils.variable}), ${ModelUtils.variable})") + } + this } - } - val builder = MethodSpec.methodBuilder(methodName) - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.elementClassName, ModelUtils.variable) - .addCode(code.build()) - if (methodName == METHOD_INSERT) { - builder.returns(ClassName.LONG) - builder.addStatement("return rowId") - } else if (methodName == METHOD_UPDATE || methodName == METHOD_SAVE) { - builder.returns(TypeName.BOOLEAN) - builder.addStatement("return successful") - } - if (useWrapper) { - builder.addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) - } + for (oneToManyDefinition in tableDefinition.oneToManyDefinitions) { + when (methodName) { + METHOD_SAVE -> oneToManyDefinition.writeSave(this, useWrapper) + METHOD_UPDATE -> oneToManyDefinition.writeUpdate(this, useWrapper) + METHOD_INSERT -> oneToManyDefinition.writeInsert(this, useWrapper) + } + } - return builder.build() + `return`(retStatement) + } } else { return null } @@ -526,21 +470,18 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, class PrimaryConditionMethod(private val tableDefinition: BaseTableDefinition) : MethodDefinition { override val methodSpec: MethodSpec? - get() { - val methodBuilder = MethodSpec.methodBuilder("getPrimaryConditionClause") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(tableDefinition.parameterClassName, - ModelUtils.variable).returns(ClassNames.OPERATOR_GROUP) - val code = CodeBlock.builder() - code.addStatement("\$T clause = \$T.clause()", ClassNames.OPERATOR_GROUP, ClassNames.OPERATOR_GROUP) - tableDefinition.primaryColumnDefinitions.forEach { - val codeBuilder = CodeBlock.builder() - it.appendPropertyComparisonAccessStatement(codeBuilder) - code.add(codeBuilder.build()) + get() = `override fun`(ClassNames.OPERATOR_GROUP, "getPrimaryConditionClause", + param(tableDefinition.parameterClassName!!, ModelUtils.variable)) { + modifiers(public, final) + code { + statement("\$T clause = \$T.clause()", ClassNames.OPERATOR_GROUP, ClassNames.OPERATOR_GROUP) + tableDefinition.primaryColumnDefinitions.forEach { + val codeBuilder = CodeBlock.builder() + it.appendPropertyComparisonAccessStatement(codeBuilder) + add(codeBuilder.build()) + } + this } - methodBuilder.addCode(code.build()) - methodBuilder.addStatement("return clause") - return methodBuilder.build() + `return`("clause") } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 8081cf981..dfbbcd13f 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -98,29 +98,27 @@ class OneToManyDefinition(typeElement: ExecutableElement, /** * Writes a delete method that will delete all related objects. - - * @param codeBuilder */ - fun writeDelete(codeBuilder: CodeBlock.Builder, useWrapper: Boolean) { + fun writeDelete(method: MethodSpec.Builder, useWrapper: Boolean) { if (isDelete) { - writeLoopWithMethod(codeBuilder, "delete", useWrapper && extendsBaseModel) - codeBuilder.addStatement(columnAccessor.set(CodeBlock.of("null"), modelBlock)) + writeLoopWithMethod(method, "delete", useWrapper && extendsBaseModel) + method.statement(columnAccessor.set(CodeBlock.of("null"), modelBlock)) } } - fun writeSave(codeBuilder: CodeBlock.Builder, useWrapper: Boolean) { + fun writeSave(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { if (isSave) writeLoopWithMethod(codeBuilder, "save", useWrapper && extendsBaseModel) } - fun writeUpdate(codeBuilder: CodeBlock.Builder, useWrapper: Boolean) { + fun writeUpdate(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { if (isSave) writeLoopWithMethod(codeBuilder, "update", useWrapper && extendsBaseModel) } - fun writeInsert(codeBuilder: CodeBlock.Builder, useWrapper: Boolean) { + fun writeInsert(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { if (isSave) writeLoopWithMethod(codeBuilder, "insert", useWrapper && (extendsBaseModel || !extendsModel)) } - private fun writeLoopWithMethod(codeBuilder: CodeBlock.Builder, methodName: String, useWrapper: Boolean) { + private fun writeLoopWithMethod(codeBuilder: MethodSpec.Builder, methodName: String, useWrapper: Boolean) { val oneToManyMethodName = this@OneToManyDefinition.methodName codeBuilder.apply { `if`("($oneToManyMethodName != null)") { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index 6722fe0f7..776ca0e9b 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -1,14 +1,12 @@ package com.raizlabs.android.dbflow.processor.definition.column -import com.grosner.kpoet.S -import com.grosner.kpoet.`else` -import com.grosner.kpoet.`if` -import com.grosner.kpoet.statement +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.processor.utils.addStatement +import com.raizlabs.android.dbflow.processor.utils.catch import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty +import com.raizlabs.android.dbflow.processor.utils.statement import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock @@ -45,10 +43,9 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { return fieldAccess } - abstract fun addCode(code: CodeBlock.Builder, - columnRepresentation: String, defaultValue: CodeBlock? = null, - index: Int = -1, - modelBlock: CodeBlock = CodeBlock.of("model")) + abstract fun CodeBlock.Builder.addCode(columnRepresentation: String, defaultValue: CodeBlock? = null, + index: Int = -1, + modelBlock: CodeBlock = CodeBlock.of("model")) open fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int = -1) { @@ -57,9 +54,9 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { class SimpleAccessCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { - code.addStatement("return \$L", getFieldAccessBlock(code, modelBlock)) + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + statement("return \$L", getFieldAccessBlock(this, modelBlock)) } } @@ -69,36 +66,38 @@ class ExistenceAccessCombiner(combiner: Combiner, val quickCheckPrimaryKey: Boolean, val tableClassName: ClassName) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { combiner.apply { if (autoRowId) { - val access = getFieldAccessBlock(code, modelBlock) + val access = getFieldAccessBlock(this@addCode, modelBlock) - code.add("return ") + add("return ") if (!fieldTypeName.isPrimitive) { - code.add("(\$L != null && ", access) + add("(\$L != null && ", access) } - code.add("\$L > 0", access) + add("\$L > 0", access) if (!fieldTypeName.isPrimitive) { - code.add(" || \$L == null)", access) + add(" || \$L == null)", access) } } if (!autoRowId || !quickCheckPrimaryKey) { if (autoRowId) { - code.add("\n&& ") + add("\n&& ") } else { - code.add("return ") + add("return ") } - code.add("\$T.selectCountOf()\n.from(\$T.class)\n.where(getPrimaryConditionClause(\$L))\n.hasData(wrapper)", + add("\$T.selectCountOf()\n.from(\$T.class)\n" + + ".where(getPrimaryConditionClause(\$L))\n" + + ".hasData(wrapper)", ClassNames.SQLITE, tableClassName, modelBlock) } - code.add(";\n") + add(";\n") } } @@ -107,13 +106,13 @@ class ExistenceAccessCombiner(combiner: Combiner, class ContentValuesCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, - modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, + modelBlock: CodeBlock) { combiner.apply { - val fieldAccess: CodeBlock = getFieldAccessBlock(code, modelBlock) + val fieldAccess: CodeBlock = getFieldAccessBlock(this@addCode, modelBlock) if (fieldTypeName.isPrimitive) { - code.addStatement("values.put(\$1S, \$2L)", QueryBuilder.quote(columnRepresentation), fieldAccess) + statement("values.put(\$1S, \$2L)", QueryBuilder.quote(columnRepresentation), fieldAccess) } else { if (defaultValue != null) { val storedFieldAccess = fieldAccess @@ -121,10 +120,10 @@ class ContentValuesCombiner(combiner: Combiner) if (subWrapperAccessor != null) { subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) } - code.addStatement("values.put(\$S, \$L != null ? \$L : \$L)", + statement("values.put(\$S, \$L != null ? \$L : \$L)", QueryBuilder.quote(columnRepresentation), storedFieldAccess, subWrapperFieldAccess, defaultValue) } else { - code.addStatement("values.put(\$S, \$L)", + statement("values.put(\$S, \$L)", QueryBuilder.quote(columnRepresentation), fieldAccess) } } @@ -138,38 +137,36 @@ class ContentValuesCombiner(combiner: Combiner) class SqliteStatementAccessCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, - modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, + modelBlock: CodeBlock) { combiner.apply { - val fieldAccess: CodeBlock = getFieldAccessBlock(code, modelBlock) + val fieldAccess: CodeBlock = getFieldAccessBlock(this@addCode, modelBlock) val wrapperMethod = SQLiteHelper[wrapperFieldTypeName ?: fieldTypeName].sqliteStatementWrapperMethod val statementMethod = SQLiteHelper[fieldTypeName].sqLiteStatementMethod - code.apply { - if (fieldTypeName.isPrimitive) { - statement("statement.bind$statementMethod($index + $columnRepresentation, $fieldAccess)") - } else { - val subWrapperFieldAccess = subWrapperAccessor?.get(fieldAccess) ?: fieldAccess - if (!defaultValue.toString().isNullOrEmpty()) { - `if`("$fieldAccess != null") { - statement("statement.bind$wrapperMethod($index + $columnRepresentation," + - " $subWrapperFieldAccess)") - }.`else` { - statement("statement.bind$statementMethod($index + $columnRepresentation," + - " $defaultValue)") - } - } else { - statement("statement.bind${wrapperMethod}OrNull($index + $columnRepresentation," + + if (fieldTypeName.isPrimitive) { + statement("statement.bind$statementMethod($index + $columnRepresentation, $fieldAccess)") + } else { + val subWrapperFieldAccess = subWrapperAccessor?.get(fieldAccess) ?: fieldAccess + if (!defaultValue.toString().isNullOrEmpty()) { + `if`("$fieldAccess != null") { + statement("statement.bind$wrapperMethod($index + $columnRepresentation," + " $subWrapperFieldAccess)") + }.`else` { + statement("statement.bind$statementMethod($index + $columnRepresentation," + + " $defaultValue)") } + } else { + statement("statement.bind${wrapperMethod}OrNull($index + $columnRepresentation," + + " $subWrapperFieldAccess)") } } } } override fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int) { - code.addStatement("statement.bindNull(\$L + \$L)", index, columnRepresentation) + code.addStatement("statement.bindNull($index + $columnRepresentation)") } } @@ -178,56 +175,55 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, val assignDefaultValuesFromCursor: Boolean = true) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, - modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, + modelBlock: CodeBlock) { combiner.apply { - var indexName: CodeBlock - if (!orderedCursorLookup) { - indexName = CodeBlock.of(columnRepresentation.S) + var indexName = if (!orderedCursorLookup) { + CodeBlock.of(columnRepresentation.S) } else { - indexName = CodeBlock.of(index.toString()) - } + CodeBlock.of(index.toString()) + }!! if (wrapperLevelAccessor != null) { if (!orderedCursorLookup) { indexName = CodeBlock.of("index_\$L", columnRepresentation) - code.addStatement("\$T \$L = cursor.getColumnIndex(\$S)", Int::class.java, indexName, + statement("\$T \$L = cursor.getColumnIndex(\$S)", Int::class.java, indexName, columnRepresentation) - code.beginControlFlow("if (\$1L != -1 && !cursor.isNull(\$1L))", indexName) + beginControlFlow("if (\$1L != -1 && !cursor.isNull(\$1L))", indexName) } else { - code.beginControlFlow("if (!cursor.isNull(\$1L))", index) + beginControlFlow("if (!cursor.isNull(\$1L))", index) } val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) // special case where we need to append try catch hack val isEnum = wrapperLevelAccessor is EnumColumnAccessor if (isEnum) { - code.beginControlFlow("try") + beginControlFlow("try") } if (subWrapperAccessor != null) { - code.addStatement(fieldLevelAccessor.set( + statement(fieldLevelAccessor.set( wrapperLevelAccessor.set(subWrapperAccessor.set(cursorAccess)), modelBlock)) } else { - code.addStatement(fieldLevelAccessor.set( + statement(fieldLevelAccessor.set( wrapperLevelAccessor.set(cursorAccess), modelBlock)) } if (isEnum) { - code.nextControlFlow("catch (\$T i)", IllegalArgumentException::class.java) - if (assignDefaultValuesFromCursor) { - code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) - } else { - code.addStatement(fieldLevelAccessor.set(defaultValue, modelBlock)) + catch(IllegalArgumentException::class) { + if (assignDefaultValuesFromCursor) { + statement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, + isDefault = true), modelBlock)) + } else { + statement(fieldLevelAccessor.set(defaultValue, modelBlock)) + } } - code.endControlFlow() } if (assignDefaultValuesFromCursor) { - code.nextControlFlow("else") - code.addStatement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, + nextControlFlow("else") + statement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, isDefault = true), modelBlock)) } - code.endControlFlow() + endControlFlow() } else { var defaultValueBlock = defaultValue @@ -236,7 +232,7 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L, $defaultValueBlock)", SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) - code.addStatement(fieldLevelAccessor.set(cursorAccess, modelBlock)) + statement(fieldLevelAccessor.set(cursorAccess, modelBlock)) } } } @@ -244,13 +240,13 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, class PrimaryReferenceAccessCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, - modelBlock: CodeBlock) { - val wrapperLevelAccessor = this.combiner.wrapperLevelAccessor - code.addStatement("clause.and(\$L.\$Leq(\$L))", columnRepresentation, + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, + modelBlock: CodeBlock) { + val wrapperLevelAccessor = this@PrimaryReferenceAccessCombiner.combiner.wrapperLevelAccessor + statement("clause.and(\$L.\$Leq(\$L))", columnRepresentation, if (!wrapperLevelAccessor.isPrimitiveTarget()) "invertProperty()." else "", - getFieldAccessBlock(code, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) + getFieldAccessBlock(this, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) } override fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int) { @@ -261,15 +257,15 @@ class PrimaryReferenceAccessCombiner(combiner: Combiner) class UpdateAutoIncrementAccessCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, defaultValue: CodeBlock?, + index: Int, modelBlock: CodeBlock) { combiner.apply { var method = "" if (SQLiteHelper.containsNumberMethod(fieldTypeName.unbox())) { method = fieldTypeName.unbox().toString() } - code.addStatement(fieldLevelAccessor.set(CodeBlock.of("id.\$LValue()", method), modelBlock)) + statement(fieldLevelAccessor.set(CodeBlock.of("id.\$LValue()", method), modelBlock)) } } @@ -277,9 +273,9 @@ class UpdateAutoIncrementAccessCombiner(combiner: Combiner) class CachingIdAccessCombiner(combiner: Combiner) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { - code.addStatement("inValues[\$L] = \$L", index, getFieldAccessBlock(code, modelBlock)) + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + statement("inValues[\$L] = \$L", index, getFieldAccessBlock(this, modelBlock)) } } @@ -288,19 +284,18 @@ class SaveModelAccessCombiner(combiner: Combiner, val implementsModel: Boolean, val extendsBaseModel: Boolean) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { combiner.apply { - val access = getFieldAccessBlock(code, modelBlock) - code.beginControlFlow("if (\$L != null)", access) - if (implementsModel) { - code.addStatement("\$L.save(\$L)", access, - if (extendsBaseModel) ModelUtils.wrapper else "") - } else { - code.addStatement("\$T.getModelAdapter(\$T.class).save(\$L, \$L)", - ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) - } - code.endControlFlow() + val access = getFieldAccessBlock(this@addCode, modelBlock) + `if`("$access != null") { + if (implementsModel) { + statement("$access.save(${wrapperIfBaseModel(extendsBaseModel)})") + } else { + statement("\$T.getModelAdapter(\$T.class).save($access, ${ModelUtils.wrapper})", + ClassNames.FLOW_MANAGER, fieldTypeName) + } + }.end() } } @@ -310,20 +305,22 @@ class DeleteModelAccessCombiner(combiner: Combiner, val implementsModel: Boolean, val extendsBaseModel: Boolean) : ColumnAccessCombiner(combiner) { - override fun addCode(code: CodeBlock.Builder, columnRepresentation: String, - defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { + override fun CodeBlock.Builder.addCode(columnRepresentation: String, + defaultValue: CodeBlock?, index: Int, modelBlock: CodeBlock) { combiner.apply { - val access = getFieldAccessBlock(code, modelBlock) - code.beginControlFlow("if (\$L != null)", access) - if (implementsModel) { - code.addStatement("\$L.delete(\$L)", access, - if (extendsBaseModel) ModelUtils.wrapper else "") - } else { - code.addStatement("\$T.getModelAdapter(\$T.class).delete(\$L, \$L)", - ClassNames.FLOW_MANAGER, fieldTypeName, access, ModelUtils.wrapper) - } - code.endControlFlow() + val access = getFieldAccessBlock(this@addCode, modelBlock) + `if`("$access != null") { + if (implementsModel) { + statement("$access.delete(${wrapperIfBaseModel(extendsBaseModel)})") + } else { + statement("\$T.getModelAdapter(\$T.class).delete($access, ${ModelUtils.wrapper})", + ClassNames.FLOW_MANAGER, fieldTypeName) + } + }.end() } } -} \ No newline at end of file +} + +fun wrapperIfBaseModel(extendsBaseModel: Boolean) = if (extendsBaseModel) ModelUtils.wrapper else "" +fun wrapperCommaIfBaseModel(extendsBaseModel: Boolean) = if (extendsBaseModel) ", " + ModelUtils.wrapper else "" \ No newline at end of file diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index dfe73f9ec..951f32adc 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt @@ -1,5 +1,6 @@ package com.raizlabs.android.dbflow.processor.definition.column +import com.grosner.kpoet.code import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.data.Blob import com.raizlabs.android.dbflow.processor.ClassNames @@ -8,9 +9,9 @@ import com.raizlabs.android.dbflow.processor.definition.BaseDefinition import com.raizlabs.android.dbflow.processor.definition.BaseTableDefinition import com.raizlabs.android.dbflow.processor.definition.TableDefinition import com.raizlabs.android.dbflow.processor.definition.TypeConverterDefinition +import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.fromTypeMirror import com.raizlabs.android.dbflow.processor.utils.getTypeElement -import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.* @@ -302,8 +303,9 @@ constructor(processorManager: ProcessorManager, element: Element, get() { val code = CodeBlock.builder() - ContentValuesCombiner(combiner) - .addCode(code, columnName, getDefaultValueBlock(), 0, modelBlock) + ContentValuesCombiner(combiner).apply { + code.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } return code.build() } @@ -316,22 +318,20 @@ constructor(processorManager: ProcessorManager, element: Element, index.incrementAndGet() } - open fun getSQLiteStatementMethod(index: AtomicInteger): CodeBlock { - - val builder = CodeBlock.builder() - SqliteStatementAccessCombiner(combiner) - .addCode(builder, "start", getDefaultValueBlock(), index.get(), modelBlock) - return builder.build() + open fun getSQLiteStatementMethod(index: AtomicInteger) = code { + SqliteStatementAccessCombiner(combiner).apply { + addCode("start", getDefaultValueBlock(), index.get(), modelBlock) + } + this } - open fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger): CodeBlock { + open fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger) = code { - val builder = CodeBlock.builder() - LoadFromCursorAccessCombiner(combiner, - baseTableDefinition.orderedCursorLookUp, - baseTableDefinition.assignDefaultValuesFromCursor) - .addCode(builder, columnName, getDefaultValueBlock(), index.get(), modelBlock) - return builder.build() + LoadFromCursorAccessCombiner(combiner, baseTableDefinition.orderedCursorLookUp, + baseTableDefinition.assignDefaultValuesFromCursor).apply { + addCode(columnName, getDefaultValueBlock(), index.get(), modelBlock) + } + this } /** @@ -339,39 +339,40 @@ constructor(processorManager: ProcessorManager, element: Element, * @return The statement to use. */ - val updateAutoIncrementMethod: CodeBlock - get() { - val code = CodeBlock.builder() - UpdateAutoIncrementAccessCombiner(combiner) - .addCode(code, columnName, getDefaultValueBlock(), - 0, modelBlock) - return code.build() + val updateAutoIncrementMethod + get() = code { + UpdateAutoIncrementAccessCombiner(combiner).apply { + addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } + this } - fun getColumnAccessString(index: Int): CodeBlock { - val codeBlock = CodeBlock.builder() - CachingIdAccessCombiner(combiner) - .addCode(codeBlock, columnName, getDefaultValueBlock(), index, modelBlock) - return codeBlock.build() + fun getColumnAccessString(index: Int) = code { + CachingIdAccessCombiner(combiner).apply { + addCode(columnName, getDefaultValueBlock(), index, modelBlock) + } + this } - fun getSimpleAccessString(): CodeBlock { - val codeBlock = CodeBlock.builder() - SimpleAccessCombiner(combiner) - .addCode(codeBlock, columnName, getDefaultValueBlock(), 0, modelBlock) - return codeBlock.build() + fun getSimpleAccessString() = code { + SimpleAccessCombiner(combiner).apply { + addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } + this } open fun appendExistenceMethod(codeBuilder: CodeBlock.Builder) { ExistenceAccessCombiner(combiner, isRowId || isPrimaryKeyAutoIncrement, isQuickCheckPrimaryKeyAutoIncrement, baseTableDefinition.elementClassName!!) - .addCode(codeBuilder, columnName, getDefaultValueBlock(), 0, modelBlock) + .apply { + codeBuilder.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } } open fun appendPropertyComparisonAccessStatement(codeBuilder: CodeBlock.Builder) { - PrimaryReferenceAccessCombiner(combiner) - .addCode(codeBuilder, columnName, getDefaultValueBlock(), - 0, modelBlock) + PrimaryReferenceAccessCombiner(combiner).apply { + codeBuilder.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } } open val creationName: CodeBlock diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt index 4860b96a3..aaf42c876 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt @@ -2,7 +2,7 @@ package com.raizlabs.android.dbflow.processor.definition.column import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper -import com.raizlabs.android.dbflow.processor.utils.addStatement +import com.raizlabs.android.dbflow.processor.utils.statement import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.TypeName import java.util.concurrent.atomic.AtomicInteger @@ -42,8 +42,9 @@ class ForeignKeyAccessField(val columnRepresentation: String, fun addCode(code: CodeBlock.Builder, index: Int, modelAccessBlock: CodeBlock) { - columnAccessCombiner.addCode(code, columnRepresentation, defaultValue, index, - modelAccessBlock) + columnAccessCombiner.apply { + code.addCode(columnRepresentation, defaultValue, index, modelAccessBlock) + } } fun addNull(code: CodeBlock.Builder, index: Int) { @@ -65,7 +66,7 @@ class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, setterBlock.add("\$T.select().from(\$T.class).where()", ClassNames.SQLITE, referencedTypeName) } else { - setterBlock.addStatement( + setterBlock.statement( fieldAccessor.set(CodeBlock.of("new \$T()", referencedTypeName), modelBlock)) } for ((i, it) in fieldAccesses.withIndex()) { @@ -82,12 +83,12 @@ class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, code.beginControlFlow("if (\$L)", ifChecker.build()) if (!isStubbed) { - code.addStatement(fieldAccessor.set(setterBlock.build(), modelBlock)) + code.statement(fieldAccessor.set(setterBlock.build(), modelBlock)) } else { code.add(setterBlock.build()) } code.nextControlFlow("else") - .addStatement(fieldAccessor.set(CodeBlock.of("null"), modelBlock)) + .statement(fieldAccessor.set(CodeBlock.of("null"), modelBlock)) .endControlFlow() } } @@ -120,14 +121,14 @@ class PartialLoadFromCursorAccessCombiner( code.add(CodeBlock.of("\n.and(\$T.\$L.eq(\$L))", referencedTableTypeName, propertyRepresentation, fieldAccessBlock)) } else if (fieldLevelAccessor != null) { - code.addStatement(fieldLevelAccessor.set(cursorAccess, parentAccessor.get(modelBlock))) + code.statement(fieldLevelAccessor.set(cursorAccess, parentAccessor.get(modelBlock))) } } fun addColumnIndex(code: CodeBlock.Builder, index: Int) { if (!orderedCursorLookup) { - code.addStatement(CodeBlock.of("int \$L = cursor.getColumnIndex(\$S)", + code.statement(CodeBlock.of("int \$L = cursor.getColumnIndex(\$S)", getIndexName(index), columnRepresentation)) } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/CodeExtensions.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/CodeExtensions.kt index 4d4ad9e92..669aaf242 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/CodeExtensions.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/CodeExtensions.kt @@ -1,7 +1,10 @@ package com.raizlabs.android.dbflow.processor.utils +import com.grosner.kpoet.end +import com.grosner.kpoet.nextControl import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.MethodSpec +import kotlin.reflect.KClass /** * Description: Set of utility methods to save code @@ -25,10 +28,16 @@ fun MethodSpec.Builder.controlFlow(statement: String, vararg args: Any?, * * @author Andrew Grosner (fuzz) */ -fun CodeBlock.Builder.addStatement(codeBlock: CodeBlock?): CodeBlock.Builder - = this.addStatement("\$L", codeBlock) +fun CodeBlock.Builder.statement(codeBlock: CodeBlock?): CodeBlock.Builder + = this.addStatement("\$L", codeBlock) -fun MethodSpec.Builder.addStatement(codeBlock: CodeBlock?): MethodSpec.Builder +fun MethodSpec.Builder.statement(codeBlock: CodeBlock?): MethodSpec.Builder - = this.addStatement("\$L", codeBlock) + = this.addStatement("\$L", codeBlock) + +inline fun CodeBlock.Builder.catch(exception: KClass, + function: CodeBlock.Builder.() -> CodeBlock.Builder) + = nextControl("catch", statement = "\$T e", args = arrayOf(exception), function = function).end() + +fun codeBlock(function: CodeBlock.Builder.() -> CodeBlock.Builder) = CodeBlock.builder().function().build() diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/JavaPoetExtensions.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/JavaPoetExtensions.kt index fcef35b2c..aacfa2dbd 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/JavaPoetExtensions.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/JavaPoetExtensions.kt @@ -9,12 +9,24 @@ import kotlin.reflect.KClass fun TypeSpec.Builder.`override fun`(type: TypeName, name: String, vararg params: ParameterSpec.Builder, codeMethod: (MethodSpec.Builder.() -> MethodSpec.Builder) = { this }) - = addMethod(MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) - .addAnnotation(Override::class.java) - .codeMethod().build())!! + = addMethod(MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) + .addAnnotation(Override::class.java) + .codeMethod().build())!! fun TypeSpec.Builder.`override fun`(type: KClass<*>, name: String, vararg params: ParameterSpec.Builder, codeMethod: (MethodSpec.Builder.() -> MethodSpec.Builder) = { this }) - = addMethod(MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) - .addAnnotation(Override::class.java) - .codeMethod().build())!! \ No newline at end of file + = addMethod(MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) + .addAnnotation(Override::class.java) + .codeMethod().build())!! + +fun `override fun`(type: TypeName, name: String, vararg params: ParameterSpec.Builder, + codeMethod: (MethodSpec.Builder.() -> MethodSpec.Builder) = { this }) + = MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) + .addAnnotation(Override::class.java) + .codeMethod().build()!! + +fun `override fun`(type: KClass<*>, name: String, vararg params: ParameterSpec.Builder, + codeMethod: (MethodSpec.Builder.() -> MethodSpec.Builder) = { this }) + = MethodSpec.methodBuilder(name).returns(type).addParameters(params.map { it.build() }.toList()) + .addAnnotation(Override::class.java) + .codeMethod().build()!! \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java index 34712b3fc..605cabe67 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java @@ -6,6 +6,7 @@ import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.converter.TypeConverter; +import com.raizlabs.android.dbflow.sql.QueryBuilder; import com.raizlabs.android.dbflow.sql.migration.Migration; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.BaseModelView; @@ -95,9 +96,12 @@ public static Class getTableClassForName(String databaseName, String tableNam DatabaseDefinition databaseDefinition = getDatabase(databaseName); Class modelClass = databaseDefinition.getModelClassForName(tableName); if (modelClass == null) { - throw new IllegalArgumentException(String.format("The specified table %1s was not found. " + - "Did you forget to add the @Table annotation and point it to %1s?", - tableName, databaseName)); + modelClass = databaseDefinition.getModelClassForName(QueryBuilder.quote(tableName)); + if (modelClass == null) { + throw new IllegalArgumentException(String.format("The specified table %1s was not found. " + + "Did you forget to add the @Table annotation and point it to %1s?", + tableName, databaseName)); + } } return modelClass; } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java index 36bbc616b..2a129391f 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java @@ -9,7 +9,6 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey; import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.sql.language.SQLite; import com.raizlabs.android.dbflow.sql.language.property.IProperty; import com.raizlabs.android.dbflow.sql.language.property.Property; @@ -24,6 +23,8 @@ import java.util.Collection; +import static com.raizlabs.android.dbflow.config.FlowManager.getWritableDatabaseForTable; + /** * Description: Used for generated classes from the combination of {@link Table} and {@link Model}. */ @@ -50,8 +51,7 @@ public ModelAdapter(DatabaseDefinition databaseDefinition) { */ public DatabaseStatement getInsertStatement() { if (insertStatement == null) { - insertStatement = getInsertStatement( - FlowManager.getDatabaseForTable(getModelClass()).getWritableDatabase()); + insertStatement = getInsertStatement(getWritableDatabaseForTable(getModelClass())); } return insertStatement; @@ -85,8 +85,7 @@ public DatabaseStatement getDeleteStatement(TModel model, DatabaseWrapper databa */ public DatabaseStatement getCompiledStatement() { if (compiledStatement == null) { - compiledStatement = getCompiledStatement( - FlowManager.getDatabaseForTable(getModelClass()).getWritableDatabase()); + compiledStatement = getCompiledStatement(getWritableDatabaseForTable(getModelClass())); } return compiledStatement; @@ -134,12 +133,6 @@ public boolean save(TModel model, DatabaseWrapper databaseWrapper) { @Override public void saveAll(Collection models) { getListModelSaver().saveAll(models); - - if (cachingEnabled()) { - for (TModel model : models) { - getModelCache().addModel(getCachingId(model), model); - } - } } @Override @@ -395,7 +388,9 @@ public void setModelSaver(ModelSaver modelSaver) { * @param cursor The cursor to reload from. */ public void reloadRelationships(@NonNull TModel model, FlowCursor cursor) { - throwCachingError(); + if (!cachingEnabled()) { + throwCachingError(); + } } @Override From b780fc1ac8a842f8d4cf63878aeb1d8a5a7355e9 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Wed, 19 Apr 2017 15:44:19 +1000 Subject: [PATCH 13/37] [processor] fix compile issues. --- .../raizlabs/android/dbflow/processor/definition/Methods.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 2dba57058..ae24b6b9c 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -341,6 +341,7 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) get() = `override fun`(TypeName.VOID, "loadFromCursor", param(ClassNames.FLOW_CURSOR, PARAM_CURSOR), param(baseTableDefinition.parameterClassName!!, ModelUtils.variable)) { + modifiers(public, final) val index = AtomicInteger(0) baseTableDefinition.columnDefinitions.forEach { addCode(it.getLoadFromCursorMethod(true, index)) @@ -412,11 +413,11 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, get() { if (!tableDefinition.oneToManyDefinitions.isEmpty() || tableDefinition.cachingEnabled) { var retType = TypeName.BOOLEAN - var retStatement = "rowId" + var retStatement = "successful" when (methodName) { METHOD_INSERT -> { retType = ClassName.LONG - retStatement = "successful" + retStatement = "rowId" } } From 3351929d2dba847f620c912d74c91e4b6b160464 Mon Sep 17 00:00:00 2001 From: Andrew Grosner Date: Mon, 24 Apr 2017 10:51:17 -0400 Subject: [PATCH 14/37] [methods] more code improvments. --- .../dbflow/processor/definition/Methods.kt | 89 +++++++------------ 1 file changed, 34 insertions(+), 55 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index ae24b6b9c..e178f688f 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -106,8 +106,8 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat } if (tableDefinition.implementsSqlStatementListener) { - methodBuilder.addStatement("\$L.onBindTo\$LStatement(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) + methodBuilder.addStatement("${ModelUtils.variable}.onBindTo\$LStatement($PARAM_STATEMENT)", + if (isInsert) "Insert" else "") } } else { var start = 0 @@ -117,11 +117,11 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat methodBuilder.addStatement("int start = 0") methodBuilder.addCode(it.getSQLiteStatementMethod(AtomicInteger(++start))) } - methodBuilder.addStatement("bindToInsertStatement(\$L, \$L, \$L)", PARAM_STATEMENT, ModelUtils.variable, start) + methodBuilder.addStatement("bindToInsertStatement($PARAM_STATEMENT, ${ModelUtils.variable}, $start)") } else if (tableDefinition.implementsSqlStatementListener) { - methodBuilder.addStatement("bindToInsertStatement(\$L, \$L, \$L)", PARAM_STATEMENT, ModelUtils.variable, start) - methodBuilder.addStatement("\$L.onBindTo\$LStatement(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "", PARAM_STATEMENT) + methodBuilder.addStatement("bindToInsertStatement($PARAM_STATEMENT, ${ModelUtils.variable}, $start)") + methodBuilder.addStatement("${ModelUtils.variable}.onBindTo\$LStatement($PARAM_STATEMENT)", + if (isInsert) "Insert" else "") } else { // don't generate method return null @@ -220,14 +220,12 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab override fun addToType(typeBuilder: TypeSpec.Builder) { val customTypeConverters = baseTableDefinition.associatedTypeConverters.keys customTypeConverters.forEach { - typeBuilder.addField(FieldSpec.builder(it, "typeConverter" + it.simpleName(), - Modifier.PRIVATE, Modifier.FINAL).initializer("new \$T()", it).build()) + typeBuilder.`private final field`(it, "typeConverter${it.simpleName()}") { `=`("new \$T()", it) } } val globalTypeConverters = baseTableDefinition.globalTypeConverters.keys globalTypeConverters.forEach { - typeBuilder.addField(FieldSpec.builder(it, "global_typeConverter" + it.simpleName(), - Modifier.PRIVATE, Modifier.FINAL).build()) + typeBuilder.`private final field`(it, "global_typeConverter${it.simpleName()}") } } @@ -278,57 +276,38 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p if (isInsert && !tableDefinition.hasAutoIncrement) { return null // dont write method here because we reuse the compiled statement query method } - val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "getInsertStatementQuery" else "getCompiledStatementQuery") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ClassName.get(String::class.java)) - - val codeBuilder = CodeBlock.builder().add("INSERT ") - if (!tableDefinition.insertConflictActionName.isEmpty()) { - codeBuilder.add("OR \$L ", tableDefinition.insertConflictActionName) - } - codeBuilder.add("INTO ").add(QueryBuilder.quote(tableDefinition.tableName)) - - val isSingleAutoincrement = tableDefinition.hasAutoIncrement && tableDefinition.columnDefinitions.size == 1 - && isInsert - - codeBuilder.add("(") - - val columnSize = tableDefinition.columnDefinitions.size - var columnCount = 0 - tableDefinition.columnDefinitions.forEach { - if (!it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert || isSingleAutoincrement) { - if (columnCount > 0) codeBuilder.add(",") - - codeBuilder.add(it.insertStatementColumnName) - columnCount++ - } - } - codeBuilder.add(")") - - codeBuilder.add(" VALUES (") - - columnCount = 0 - for (i in 0..columnSize - 1) { - val definition = tableDefinition.columnDefinitions[i] - if (!definition.isPrimaryKeyAutoIncrement && !definition.isRowId || !isInsert) { - if (columnCount > 0) { - codeBuilder.add(",") + return `override fun`(String::class, + if (isInsert) "getInsertStatementQuery" else "getCompiledStatementQuery") { + modifiers(public, final) + val isSingleAutoincrement = tableDefinition.hasAutoIncrement + && tableDefinition.columnDefinitions.size == 1 && isInsert + `return`(codeBlock { + add("INSERT ") + if (!tableDefinition.insertConflictActionName.isEmpty()) { + add("OR ${tableDefinition.insertConflictActionName} ") } + add("INTO ${QueryBuilder.quote(tableDefinition.tableName)}(") - codeBuilder.add(definition.insertStatementValuesString) - columnCount++ - } - } + tableDefinition.columnDefinitions.filter { + !it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert || isSingleAutoincrement + }.forEachIndexed { index, columnDefinition -> + if (index > 0) add(",") + add(columnDefinition.insertStatementColumnName) - if (isSingleAutoincrement) { - codeBuilder.add("NULL") - } + } + add(") VALUES (") - codeBuilder.add(")") + tableDefinition.columnDefinitions.filter { it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert } + .forEachIndexed { index, columnDefinition -> + if (index > 0) add(",") + add(columnDefinition.insertStatementValuesString) + } - methodBuilder.addStatement("return \$S", codeBuilder.build().toString()) + if (isSingleAutoincrement) add("NULL") - return methodBuilder.build() + add(")") + }.S) + } } } From e97d3a53c182e80f84239f8a72edf2d28255b8e6 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 10:59:55 -0400 Subject: [PATCH 15/37] [queriable] update doc on count. --- .../raizlabs/android/dbflow/sql/language/BaseQueriable.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java index e453e874b..e356774b9 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java @@ -40,6 +40,8 @@ public Class getTable() { * Execute a statement that returns a 1 by 1 table with a numeric value. * For example, SELECT COUNT(*) FROM table. * Please see {@link SQLiteStatement#simpleQueryForLong()}. + *

+ * catches a {@link SQLiteDoneException} if result is not found and returns 0. The error can safely be ignored. */ @Override public long count(DatabaseWrapper databaseWrapper) { @@ -49,7 +51,7 @@ public long count(DatabaseWrapper databaseWrapper) { return SqlUtils.longForQuery(databaseWrapper, query); } catch (SQLiteDoneException sde) { // catch exception here, log it but return 0; - FlowLog.log(FlowLog.Level.E, sde); + FlowLog.log(FlowLog.Level.W, sde); } return 0; } From 34dd84438a28be4569c7b1fa24214252459366bd Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 11:04:07 -0400 Subject: [PATCH 16/37] [cursor] add compatible getwrapped cursor method. --- .../android/dbflow/structure/database/FlowCursor.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java index c12317a47..0cb23f258 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -14,8 +14,16 @@ public static FlowCursor from(Cursor cursor) { } } + private Cursor cursor; // compatibility reasons + private FlowCursor(@NonNull Cursor cursor) { super(cursor); + this.cursor = cursor; + } + + // compatibility + public Cursor getWrappedCursor() { + return cursor; } public String getStringOrDefault(int index, String defValue) { From 45e630cc9a4b4b95c82f845a33fa051602e1ed21 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 11:52:10 -0400 Subject: [PATCH 17/37] [cursor] add default methods which dont pass in empty values, rather delegate to condition before returning default. reference cursor directly to cut down method refs. If default value not specified, leave out param. --- .../dbflow/processor/definition/Methods.kt | 5 +- .../processor/definition/TableDefinition.kt | 113 ++++++------ .../definition/column/ColumnAccessCombiner.kt | 53 +++--- .../definition/column/ColumnDefinition.kt | 66 +++---- .../column/ForeignKeyAccessCombiner.kt | 70 ++++---- .../column/ForeignKeyColumnDefinition.kt | 47 ++--- .../dbflow/structure/database/FlowCursor.java | 164 +++++++++++++----- 7 files changed, 310 insertions(+), 208 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index e178f688f..2f2e28968 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -297,7 +297,7 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p } add(") VALUES (") - tableDefinition.columnDefinitions.filter { it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert } + tableDefinition.columnDefinitions.filter { !it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert } .forEachIndexed { index, columnDefinition -> if (index > 0) add(",") add(columnDefinition.insertStatementValuesString) @@ -322,8 +322,9 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) param(baseTableDefinition.parameterClassName!!, ModelUtils.variable)) { modifiers(public, final) val index = AtomicInteger(0) + val nameAllocator = NameAllocator() // unique names baseTableDefinition.columnDefinitions.forEach { - addCode(it.getLoadFromCursorMethod(true, index)) + addCode(it.getLoadFromCursorMethod(true, index, nameAllocator)) index.incrementAndGet() } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt index 00ee59258..883b41a28 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/TableDefinition.kt @@ -99,7 +99,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab inheritedColumns.forEach { if (inheritedFieldNameList.contains(it.fieldName)) { manager.logError("A duplicate inherited column with name %1s was found for %1s", - it.fieldName, tableName) + it.fieldName, tableName) } inheritedFieldNameList.add(it.fieldName) inheritedColumnMap.put(it.fieldName, it) @@ -109,35 +109,35 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab inheritedPrimaryKeys.forEach { if (inheritedFieldNameList.contains(it.fieldName)) { manager.logError("A duplicate inherited column with name %1s was found for %1s", - it.fieldName, tableName) + it.fieldName, tableName) } inheritedFieldNameList.add(it.fieldName) inheritedPrimaryKeyMap.put(it.fieldName, it) } implementsLoadFromCursorListener = element.implementsClass(manager.processingEnvironment, - ClassNames.LOAD_FROM_CURSOR_LISTENER) + ClassNames.LOAD_FROM_CURSOR_LISTENER) implementsContentValuesListener = element.implementsClass(manager.processingEnvironment, - ClassNames.CONTENT_VALUES_LISTENER) + ClassNames.CONTENT_VALUES_LISTENER) implementsSqlStatementListener = element.implementsClass(manager.processingEnvironment, - ClassNames.SQLITE_STATEMENT_LISTENER) + ClassNames.SQLITE_STATEMENT_LISTENER) } methods = arrayOf(BindToContentValuesMethod(this, true, implementsContentValuesListener), - BindToContentValuesMethod(this, false, implementsContentValuesListener), - BindToStatementMethod(this, true), BindToStatementMethod(this, false), - InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), - CreationQueryMethod(this), LoadFromCursorMethod(this), ExistenceMethod(this), - PrimaryConditionMethod(this), OneToManyDeleteMethod(this, false), - OneToManyDeleteMethod(this, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, false), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, true), - OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, true)) + BindToContentValuesMethod(this, false, implementsContentValuesListener), + BindToStatementMethod(this, true), BindToStatementMethod(this, false), + InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), + CreationQueryMethod(this), LoadFromCursorMethod(this), ExistenceMethod(this), + PrimaryConditionMethod(this), OneToManyDeleteMethod(this, false), + OneToManyDeleteMethod(this, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, false), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_SAVE, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_INSERT, true), + OneToManySaveMethod(this, OneToManySaveMethod.METHOD_UPDATE, true)) } override fun prepareForWrite() { @@ -191,7 +191,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val definition = UniqueGroupsDefinition(uniqueGroup) columnDefinitions.filter { it.uniqueGroups.contains(definition.number) } - .forEach { definition.addColumnDefinition(it) } + .forEach { definition.addColumnDefinition(it) } uniqueGroupsDefinitions.add(definition) uniqueNumbersSet.add(uniqueGroup.groupNumber) } @@ -204,7 +204,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val definition = IndexGroupsDefinition(this, indexGroup) columnDefinitions.filter { it.indexGroups.contains(definition.indexNumber) } - .forEach { definition.columnDefinitionList.add(it) } + .forEach { definition.columnDefinitionList.add(it) } indexGroupsDefinitions.add(definition) uniqueNumbersSet.add(indexGroup.number) } @@ -218,9 +218,9 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab for (element in elements) { classElementLookUpMap.put(element.simpleName.toString(), element) if (element is ExecutableElement && element.parameters.isEmpty() - && element.simpleName.toString() == "" - && element.enclosingElement == typeElement - && !element.modifiers.contains(Modifier.PRIVATE)) { + && element.simpleName.toString() == "" + && element.enclosingElement == typeElement + && !element.modifiers.contains(Modifier.PRIVATE)) { hasPrimaryConstructor = true } } @@ -240,23 +240,23 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val isInherited = inheritedColumnMap.containsKey(element.simpleName.toString()) val isInheritedPrimaryKey = inheritedPrimaryKeyMap.containsKey(element.simpleName.toString()) if (element.annotation() != null || isForeign || isPrimary - || isAllFields || isInherited || isInheritedPrimaryKey) { + || isAllFields || isInherited || isInheritedPrimaryKey) { val columnDefinition: ColumnDefinition if (isInheritedPrimaryKey) { val inherited = inheritedPrimaryKeyMap[element.simpleName.toString()] columnDefinition = ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, inherited?.primaryKey) + inherited?.column, inherited?.primaryKey) } else if (isInherited) { val inherited = inheritedColumnMap[element.simpleName.toString()] columnDefinition = ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, null) + inherited?.column, null) } else if (isForeign) { columnDefinition = ForeignKeyColumnDefinition(manager, this, - element, isPackagePrivateNotInSamePackage) + element, isPackagePrivateNotInSamePackage) } else { columnDefinition = ColumnDefinition(manager, element, - this, isPackagePrivateNotInSamePackage) + this, isPackagePrivateNotInSamePackage) } if (columnValidator.validate(manager, columnDefinition)) { @@ -359,7 +359,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val getPropertiesBuilder = CodeBlock.builder() `override fun`(ClassNames.PROPERTY, "getProperty", - param(String::class, paramColumnName)) { + param(String::class, paramColumnName)) { modifiers(public, final) statement("$paramColumnName = \$T.quoteIfNeeded($paramColumnName)", ClassName.get(QueryBuilder::class.java)) @@ -393,7 +393,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val autoIncrement = autoIncrementColumn autoIncrement?.let { `override fun`(TypeName.VOID, "updateAutoIncrement", param(elementClassName!!, ModelUtils.variable), - param(Number::class, "id")) { + param(Number::class, "id")) { modifiers(public, final) addCode(autoIncrement.updateAutoIncrementMethod) } @@ -410,28 +410,28 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val saveForeignKeyFields = columnDefinitions - .filter { (it is ForeignKeyColumnDefinition) && it.saveForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } + .filter { (it is ForeignKeyColumnDefinition) && it.saveForeignKeyModel } + .map { it as ForeignKeyColumnDefinition } if (saveForeignKeyFields.isNotEmpty()) { val code = CodeBlock.builder() saveForeignKeyFields.forEach { it.appendSaveMethod(code) } `override fun`(TypeName.VOID, "saveForeignKeys", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { modifiers(public, final) addCode(code.build()) } } val deleteForeignKeyFields = columnDefinitions - .filter { (it is ForeignKeyColumnDefinition) && it.deleteForeignKeyModel } - .map { it as ForeignKeyColumnDefinition } + .filter { (it is ForeignKeyColumnDefinition) && it.deleteForeignKeyModel } + .map { it as ForeignKeyColumnDefinition } if (deleteForeignKeyFields.isNotEmpty()) { val code = CodeBlock.builder() deleteForeignKeyFields.forEach { it.appendDeleteMethod(code) } `override fun`(TypeName.VOID, "deleteForeignKeys", param(elementClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { + param(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper)) { modifiers(public, final) addCode(code.build()) } @@ -449,21 +449,21 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab `override fun`(ClassNames.SINGLE_MODEL_LOADER, "createSingleModelLoader") { modifiers(public, final) addStatement("return new \$T<>(getModelClass())", - if (singlePrimaryKey) - ClassNames.SINGLE_KEY_CACHEABLE_MODEL_LOADER - else - ClassNames.CACHEABLE_MODEL_LOADER) + if (singlePrimaryKey) + ClassNames.SINGLE_KEY_CACHEABLE_MODEL_LOADER + else + ClassNames.CACHEABLE_MODEL_LOADER) } `override fun`(ClassNames.LIST_MODEL_LOADER, "createListModelLoader") { modifiers(public, final) `return`("new \$T<>(getModelClass())", - if (singlePrimaryKey) - ClassNames.SINGLE_KEY_CACHEABLE_LIST_MODEL_LOADER - else - ClassNames.CACHEABLE_LIST_MODEL_LOADER) + if (singlePrimaryKey) + ClassNames.SINGLE_KEY_CACHEABLE_LIST_MODEL_LOADER + else + ClassNames.CACHEABLE_LIST_MODEL_LOADER) } `override fun`(ParameterizedTypeName.get(ClassNames.CACHEABLE_LIST_MODEL_SAVER, elementClassName), - "createListModelSaver") { + "createListModelSaver") { modifiers(protected) `return`("new \$T<>(getModelSaver())", ClassNames.CACHEABLE_LIST_MODEL_SAVER) } @@ -475,8 +475,8 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val primaryColumns = primaryColumnDefinitions if (primaryColumns.size > 1) { `override fun`(ArrayTypeName.of(Any::class.java), "getCachingColumnValuesFromModel", - param(ArrayTypeName.of(Any::class.java), "inValues"), - param(elementClassName!!, ModelUtils.variable)) { + param(ArrayTypeName.of(Any::class.java), "inValues"), + param(elementClassName!!, ModelUtils.variable)) { modifiers(public, final) for (i in primaryColumns.indices) { val column = primaryColumns[i] @@ -487,21 +487,21 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } `override fun`(ArrayTypeName.of(Any::class.java), "getCachingColumnValuesFromCursor", - param(ArrayTypeName.of(Any::class.java), "inValues"), - param(ClassNames.FLOW_CURSOR, "cursor")) { + param(ArrayTypeName.of(Any::class.java), "inValues"), + param(ClassNames.FLOW_CURSOR, "cursor")) { modifiers(public, final) for (i in primaryColumns.indices) { val column = primaryColumns[i] val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) statement("inValues[$i] = ${LoadFromCursorMethod.PARAM_CURSOR}" + - ".$method(${LoadFromCursorMethod.PARAM_CURSOR}.getColumnIndex(${column.columnName.S}))") + ".$method(${LoadFromCursorMethod.PARAM_CURSOR}.getColumnIndex(${column.columnName.S}))") } `return`("inValues") } } else { // single primary key `override fun`(Any::class, "getCachingColumnValueFromModel", - param(elementClassName!!, ModelUtils.variable)) { + param(elementClassName!!, ModelUtils.variable)) { modifiers(public, final) addCode(primaryColumns[0].getSimpleAccessString()) } @@ -532,7 +532,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (!customCacheFieldName.isNullOrEmpty()) { `override fun`(ParameterizedTypeName.get(ClassNames.MODEL_CACHE, elementClassName, - WildcardTypeName.subtypeOf(Any::class.java)), "createModelCache") { + WildcardTypeName.subtypeOf(Any::class.java)), "createModelCache") { modifiers(public, final) `return`("\$T.$customCacheFieldName", elementClassName) } @@ -540,7 +540,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (!customMultiCacheFieldName.isNullOrEmpty()) { `override fun`(ParameterizedTypeName.get(ClassNames.MULTI_KEY_CACHE_CONVERTER, - WildcardTypeName.subtypeOf(Any::class.java)), "getCacheConverter") { + WildcardTypeName.subtypeOf(Any::class.java)), "getCacheConverter") { modifiers(public, final) `return`("\$T.$customMultiCacheFieldName", elementClassName) } @@ -548,12 +548,13 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab if (foreignKeyDefinitions.isNotEmpty()) { `override fun`(TypeName.VOID, "reloadRelationships", - param(elementClassName!!, ModelUtils.variable), - param(ClassNames.FLOW_CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { + param(elementClassName!!, ModelUtils.variable), + param(ClassNames.FLOW_CURSOR, LoadFromCursorMethod.PARAM_CURSOR)) { modifiers(public, final) code { val noIndex = AtomicInteger(-1) - foreignKeyDefinitions.forEach { add(it.getLoadFromCursorMethod(false, noIndex)) } + val nameAllocator = NameAllocator() + foreignKeyDefinitions.forEach { add(it.getLoadFromCursorMethod(false, noIndex, nameAllocator)) } this } } @@ -562,6 +563,6 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt index 776ca0e9b..9e6dd9688 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessCombiner.kt @@ -10,6 +10,7 @@ import com.raizlabs.android.dbflow.processor.utils.statement import com.raizlabs.android.dbflow.sql.QueryBuilder import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.NameAllocator import com.squareup.javapoet.TypeName data class Combiner(val fieldLevelAccessor: ColumnAccessor, @@ -29,9 +30,9 @@ abstract class ColumnAccessCombiner(val combiner: Combiner) { fieldAccess = CodeBlock.of("ref" + fieldLevelAccessor.propertyName) existingBuilder.addStatement("\$T \$L = \$L != null ? \$L : null", - wrapperFieldTypeName, fieldAccess, - fieldLevelAccessor.get(modelBlock), - wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock))) + wrapperFieldTypeName, fieldAccess, + fieldLevelAccessor.get(modelBlock), + wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock))) } else { if (useWrapper && wrapperLevelAccessor != null) { fieldAccess = wrapperLevelAccessor.get(fieldLevelAccessor.get(modelBlock)) @@ -93,9 +94,9 @@ class ExistenceAccessCombiner(combiner: Combiner, } add("\$T.selectCountOf()\n.from(\$T.class)\n" + - ".where(getPrimaryConditionClause(\$L))\n" + - ".hasData(wrapper)", - ClassNames.SQLITE, tableClassName, modelBlock) + ".where(getPrimaryConditionClause(\$L))\n" + + ".hasData(wrapper)", + ClassNames.SQLITE, tableClassName, modelBlock) } add(";\n") } @@ -121,10 +122,10 @@ class ContentValuesCombiner(combiner: Combiner) subWrapperFieldAccess = subWrapperAccessor.get(storedFieldAccess) } statement("values.put(\$S, \$L != null ? \$L : \$L)", - QueryBuilder.quote(columnRepresentation), storedFieldAccess, subWrapperFieldAccess, defaultValue) + QueryBuilder.quote(columnRepresentation), storedFieldAccess, subWrapperFieldAccess, defaultValue) } else { statement("values.put(\$S, \$L)", - QueryBuilder.quote(columnRepresentation), fieldAccess) + QueryBuilder.quote(columnRepresentation), fieldAccess) } } } @@ -152,14 +153,14 @@ class SqliteStatementAccessCombiner(combiner: Combiner) if (!defaultValue.toString().isNullOrEmpty()) { `if`("$fieldAccess != null") { statement("statement.bind$wrapperMethod($index + $columnRepresentation," + - " $subWrapperFieldAccess)") + " $subWrapperFieldAccess)") }.`else` { statement("statement.bind$statementMethod($index + $columnRepresentation," + - " $defaultValue)") + " $defaultValue)") } } else { statement("statement.bind${wrapperMethod}OrNull($index + $columnRepresentation," + - " $subWrapperFieldAccess)") + " $subWrapperFieldAccess)") } } } @@ -171,6 +172,8 @@ class SqliteStatementAccessCombiner(combiner: Combiner) } class LoadFromCursorAccessCombiner(combiner: Combiner, + val hasDefaultValue: Boolean, + val nameAllocator: NameAllocator, val orderedCursorLookup: Boolean = false, val assignDefaultValuesFromCursor: Boolean = true) : ColumnAccessCombiner(combiner) { @@ -187,15 +190,15 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, if (wrapperLevelAccessor != null) { if (!orderedCursorLookup) { - indexName = CodeBlock.of("index_\$L", columnRepresentation) + indexName = CodeBlock.of(nameAllocator.newName("index_$columnRepresentation", columnRepresentation)) statement("\$T \$L = cursor.getColumnIndex(\$S)", Int::class.java, indexName, - columnRepresentation) + columnRepresentation) beginControlFlow("if (\$1L != -1 && !cursor.isNull(\$1L))", indexName) } else { beginControlFlow("if (!cursor.isNull(\$1L))", index) } val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", - SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) + SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) // special case where we need to append try catch hack val isEnum = wrapperLevelAccessor is EnumColumnAccessor if (isEnum) { @@ -203,16 +206,16 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, } if (subWrapperAccessor != null) { statement(fieldLevelAccessor.set( - wrapperLevelAccessor.set(subWrapperAccessor.set(cursorAccess)), modelBlock)) + wrapperLevelAccessor.set(subWrapperAccessor.set(cursorAccess)), modelBlock)) } else { statement(fieldLevelAccessor.set( - wrapperLevelAccessor.set(cursorAccess), modelBlock)) + wrapperLevelAccessor.set(cursorAccess), modelBlock)) } if (isEnum) { catch(IllegalArgumentException::class) { if (assignDefaultValuesFromCursor) { statement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) + isDefault = true), modelBlock)) } else { statement(fieldLevelAccessor.set(defaultValue, modelBlock)) } @@ -221,7 +224,7 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, if (assignDefaultValuesFromCursor) { nextControlFlow("else") statement(fieldLevelAccessor.set(wrapperLevelAccessor.set(defaultValue, - isDefault = true), modelBlock)) + isDefault = true), modelBlock)) } endControlFlow() } else { @@ -230,8 +233,8 @@ class LoadFromCursorAccessCombiner(combiner: Combiner, if (!assignDefaultValuesFromCursor) { defaultValueBlock = fieldLevelAccessor.get(modelBlock) } - val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L, $defaultValueBlock)", - SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) + val cursorAccess = CodeBlock.of("cursor.\$LOrDefault(\$L${if (hasDefaultValue) ", $defaultValueBlock" else ""})", + SQLiteHelper.getMethod(wrapperFieldTypeName ?: fieldTypeName), indexName) statement(fieldLevelAccessor.set(cursorAccess, modelBlock)) } } @@ -245,13 +248,13 @@ class PrimaryReferenceAccessCombiner(combiner: Combiner) modelBlock: CodeBlock) { val wrapperLevelAccessor = this@PrimaryReferenceAccessCombiner.combiner.wrapperLevelAccessor statement("clause.and(\$L.\$Leq(\$L))", columnRepresentation, - if (!wrapperLevelAccessor.isPrimitiveTarget()) "invertProperty()." else "", - getFieldAccessBlock(this, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) + if (!wrapperLevelAccessor.isPrimitiveTarget()) "invertProperty()." else "", + getFieldAccessBlock(this, modelBlock, wrapperLevelAccessor !is BooleanColumnAccessor)) } override fun addNull(code: CodeBlock.Builder, columnRepresentation: String, index: Int) { code.addStatement("clause.and(\$L.eq((\$T) \$L))", columnRepresentation, - ClassNames.ICONDITIONAL, "null") + ClassNames.ICONDITIONAL, "null") } } @@ -293,7 +296,7 @@ class SaveModelAccessCombiner(combiner: Combiner, statement("$access.save(${wrapperIfBaseModel(extendsBaseModel)})") } else { statement("\$T.getModelAdapter(\$T.class).save($access, ${ModelUtils.wrapper})", - ClassNames.FLOW_MANAGER, fieldTypeName) + ClassNames.FLOW_MANAGER, fieldTypeName) } }.end() } @@ -314,7 +317,7 @@ class DeleteModelAccessCombiner(combiner: Combiner, statement("$access.delete(${wrapperIfBaseModel(extendsBaseModel)})") } else { statement("\$T.getModelAdapter(\$T.class).delete($access, ${ModelUtils.wrapper})", - ClassNames.FLOW_MANAGER, fieldTypeName) + ClassNames.FLOW_MANAGER, fieldTypeName) } }.end() } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index 951f32adc..fc5c68f9b 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt @@ -100,8 +100,8 @@ constructor(processorManager: ProcessorManager, element: Element, } if (defaultValue != null - && elementClassName == ClassName.get(String::class.java) - && !QUOTE_PATTERN.matcher(defaultValue).find()) { + && elementClassName == ClassName.get(String::class.java) + && !QUOTE_PATTERN.matcher(defaultValue).find()) { defaultValue = "\"" + defaultValue + "\"" } } @@ -111,19 +111,19 @@ constructor(processorManager: ProcessorManager, element: Element, if (isPackagePrivate) { columnAccessor = PackagePrivateScopeColumnAccessor(elementName, packageName, - baseTableDefinition.databaseDefinition?.classSeparator, - ClassName.get(element.enclosingElement as TypeElement).simpleName()) + baseTableDefinition.databaseDefinition?.classSeparator, + ClassName.get(element.enclosingElement as TypeElement).simpleName()) PackagePrivateScopeColumnAccessor.putElement( - (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, - columnName) + (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, + columnName) } else { val isPrivate = element.modifiers.contains(Modifier.PRIVATE) if (isPrivate) { val isBoolean = elementTypeName?.box() == TypeName.BOOLEAN.box() val useIs = isBoolean - && baseTableDefinition is TableDefinition && (baseTableDefinition as TableDefinition).useIsForPrivateBooleans + && baseTableDefinition is TableDefinition && (baseTableDefinition as TableDefinition).useIsForPrivateBooleans columnAccessor = PrivateScopeColumnAccessor(elementName, object : GetterSetter { override val getterName: String = column?.getterName ?: "" override val setterName: String = column?.setterName ?: "" @@ -172,7 +172,7 @@ constructor(processorManager: ProcessorManager, element: Element, hasCustomConverter = false if (typeConverterClassName != null && typeMirror != null && - typeConverterClassName != ClassNames.TYPE_CONVERTER) { + typeConverterClassName != ClassNames.TYPE_CONVERTER) { typeConverterDefinition = TypeConverterDefinition(typeConverterClassName, typeMirror, manager) evaluateTypeConverter(typeConverterDefinition, true) } @@ -190,7 +190,7 @@ constructor(processorManager: ProcessorManager, element: Element, // do nothing, for now. } else if (elementTypeName is ArrayTypeName) { processorManager.messager.printMessage(Diagnostic.Kind.ERROR, - "Columns cannot be of array type.") + "Columns cannot be of array type.") } else { if (elementTypeName == TypeName.BOOLEAN) { wrapperAccessor = BooleanColumnAccessor() @@ -210,7 +210,7 @@ constructor(processorManager: ProcessorManager, element: Element, } combiner = Combiner(columnAccessor, elementTypeName!!, wrapperAccessor, wrapperTypeName, - subWrapperAccessor) + subWrapperAccessor) } private fun evaluateTypeConverter(typeConverterDefinition: TypeConverterDefinition?, @@ -220,7 +220,7 @@ constructor(processorManager: ProcessorManager, element: Element, if (it.modelTypeName != elementTypeName) { manager.logError("The specified custom TypeConverter's Model Value ${it.modelTypeName}" + - " from ${it.className} must match the type of the column $elementTypeName. ") + " from ${it.className} must match the type of the column $elementTypeName. ") } else { hasTypeConverter = true hasCustomConverter = isCustom @@ -259,21 +259,21 @@ constructor(processorManager: ProcessorManager, element: Element, } val fieldBuilder = FieldSpec.builder(propParam, - columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) if (isNonPrimitiveTypeConverter) { val codeBlock = CodeBlock.builder() codeBlock.add("new \$T(\$T.class, \$S, true,", propParam, tableClass, columnName) codeBlock.add("\nnew \$T() {" + - "\n@Override" + - "\npublic \$T getTypeConverter(Class modelClass) {" + - "\n \$T adapter = (\$T) \$T.getInstanceAdapter(modelClass);" + - "\nreturn adapter.\$L;" + - "\n}" + - "\n})", ClassNames.TYPE_CONVERTER_GETTER, ClassNames.TYPE_CONVERTER, - baseTableDefinition.outputClassName, baseTableDefinition.outputClassName, - ClassNames.FLOW_MANAGER, - (wrapperAccessor as TypeConverterScopeColumnAccessor).typeConverterFieldName) + "\n@Override" + + "\npublic \$T getTypeConverter(Class modelClass) {" + + "\n \$T adapter = (\$T) \$T.getInstanceAdapter(modelClass);" + + "\nreturn adapter.\$L;" + + "\n}" + + "\n})", ClassNames.TYPE_CONVERTER_GETTER, ClassNames.TYPE_CONVERTER, + baseTableDefinition.outputClassName, baseTableDefinition.outputClassName, + ClassNames.FLOW_MANAGER, + (wrapperAccessor as TypeConverterScopeColumnAccessor).typeConverterFieldName) fieldBuilder.initializer(codeBlock.build()) } else { fieldBuilder.initializer("new \$T(\$T.class, \$S)", propParam, tableClass, columnName) @@ -325,10 +325,12 @@ constructor(processorManager: ProcessorManager, element: Element, this } - open fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger) = code { + open fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger, + nameAllocator: NameAllocator) = code { - LoadFromCursorAccessCombiner(combiner, baseTableDefinition.orderedCursorLookUp, - baseTableDefinition.assignDefaultValuesFromCursor).apply { + LoadFromCursorAccessCombiner(combiner, defaultValue != null, + nameAllocator, baseTableDefinition.orderedCursorLookUp, + baseTableDefinition.assignDefaultValuesFromCursor).apply { addCode(columnName, getDefaultValueBlock(), index.get(), modelBlock) } this @@ -363,10 +365,10 @@ constructor(processorManager: ProcessorManager, element: Element, open fun appendExistenceMethod(codeBuilder: CodeBlock.Builder) { ExistenceAccessCombiner(combiner, isRowId || isPrimaryKeyAutoIncrement, - isQuickCheckPrimaryKeyAutoIncrement, baseTableDefinition.elementClassName!!) - .apply { - codeBuilder.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) - } + isQuickCheckPrimaryKeyAutoIncrement, baseTableDefinition.elementClassName!!) + .apply { + codeBuilder.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + } } open fun appendPropertyComparisonAccessStatement(codeBuilder: CodeBlock.Builder) { @@ -383,9 +385,9 @@ constructor(processorManager: ProcessorManager, element: Element, codeBlockBuilder.add(" PRIMARY KEY ") if (baseTableDefinition is TableDefinition && - !(baseTableDefinition as TableDefinition).primaryKeyConflictActionName.isNullOrEmpty()) { + !(baseTableDefinition as TableDefinition).primaryKeyConflictActionName.isNullOrEmpty()) { codeBlockBuilder.add("ON CONFLICT \$L ", - (baseTableDefinition as TableDefinition).primaryKeyConflictActionName) + (baseTableDefinition as TableDefinition).primaryKeyConflictActionName) } codeBlockBuilder.add("AUTOINCREMENT") @@ -422,8 +424,8 @@ constructor(processorManager: ProcessorManager, element: Element, if (elementTypeName == TypeName.BOOLEAN) { defaultValue = "false" } else if (elementTypeName == TypeName.BYTE || elementTypeName == TypeName.INT - || elementTypeName == TypeName.DOUBLE || elementTypeName == TypeName.FLOAT - || elementTypeName == TypeName.LONG || elementTypeName == TypeName.SHORT) { + || elementTypeName == TypeName.DOUBLE || elementTypeName == TypeName.FLOAT + || elementTypeName == TypeName.LONG || elementTypeName == TypeName.SHORT) { defaultValue = "($elementTypeName) 0" } else if (elementTypeName == TypeName.CHAR) { defaultValue = "'\\u0000'" diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt index aaf42c876..346373ed4 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt @@ -4,6 +4,7 @@ import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper import com.raizlabs.android.dbflow.processor.utils.statement import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.NameAllocator import com.squareup.javapoet.TypeName import java.util.concurrent.atomic.AtomicInteger @@ -31,8 +32,8 @@ class ForeignKeyAccessCombiner(val fieldAccessor: ColumnAccessor) { } } code.nextControlFlow("else") - .add(nullAccessBlock.build().toString()) - .endControlFlow() + .add(nullAccessBlock.build().toString()) + .endControlFlow() } } @@ -55,7 +56,8 @@ class ForeignKeyAccessField(val columnRepresentation: String, class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, val referencedTypeName: TypeName, val referencedTableTypeName: TypeName, - val isStubbed: Boolean) { + val isStubbed: Boolean, + val nameAllocator: NameAllocator) { var fieldAccesses: List = arrayListOf() fun addCode(code: CodeBlock.Builder, index: AtomicInteger) { @@ -64,15 +66,15 @@ class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, if (!isStubbed) { setterBlock.add("\$T.select().from(\$T.class).where()", - ClassNames.SQLITE, referencedTypeName) + ClassNames.SQLITE, referencedTypeName) } else { setterBlock.statement( - fieldAccessor.set(CodeBlock.of("new \$T()", referencedTypeName), modelBlock)) + fieldAccessor.set(CodeBlock.of("new \$T()", referencedTypeName), modelBlock)) } for ((i, it) in fieldAccesses.withIndex()) { - it.addRetrieval(setterBlock, index.get(), referencedTableTypeName, isStubbed, fieldAccessor) - it.addColumnIndex(code, index.get()) - it.addIndexCheckStatement(ifChecker, index.get(), i == fieldAccesses.size - 1) + it.addRetrieval(setterBlock, index.get(), referencedTableTypeName, isStubbed, fieldAccessor, nameAllocator) + it.addColumnIndex(code, index.get(), nameAllocator) + it.addIndexCheckStatement(ifChecker, index.get(), i == fieldAccesses.size - 1, nameAllocator) if (i < fieldAccesses.size - 1) { index.incrementAndGet() @@ -88,56 +90,62 @@ class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, code.add(setterBlock.build()) } code.nextControlFlow("else") - .statement(fieldAccessor.set(CodeBlock.of("null"), modelBlock)) - .endControlFlow() + .statement(fieldAccessor.set(CodeBlock.of("null"), modelBlock)) + .endControlFlow() } } class PartialLoadFromCursorAccessCombiner( - val columnRepresentation: String, - val propertyRepresentation: String, - val fieldTypeName: TypeName, - val orderedCursorLookup: Boolean = false, - val fieldLevelAccessor: ColumnAccessor? = null, - val subWrapperAccessor: ColumnAccessor? = null, - val subWrapperTypeName: TypeName? = null) { - - fun getIndexName(index: Int): CodeBlock { - return if (!orderedCursorLookup) { - CodeBlock.of("index_\$L", columnRepresentation) - } else { - CodeBlock.of(index.toString()) + val columnRepresentation: String, + val propertyRepresentation: String, + val fieldTypeName: TypeName, + val orderedCursorLookup: Boolean = false, + val fieldLevelAccessor: ColumnAccessor? = null, + val subWrapperAccessor: ColumnAccessor? = null, + val subWrapperTypeName: TypeName? = null) { + + var indexName: CodeBlock? = null + + fun getIndexName(index: Int, nameAllocator: NameAllocator): CodeBlock { + if (indexName == null) { + indexName = if (!orderedCursorLookup) { + CodeBlock.of(nameAllocator.newName("index_$columnRepresentation", columnRepresentation)) + } else { + CodeBlock.of(index.toString()) + } } + return indexName!! } fun addRetrieval(code: CodeBlock.Builder, index: Int, referencedTableTypeName: TypeName, - isStubbed: Boolean, parentAccessor: ColumnAccessor) { + isStubbed: Boolean, parentAccessor: ColumnAccessor, + nameAllocator: NameAllocator) { val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", - SQLiteHelper.getMethod(subWrapperTypeName ?: fieldTypeName), getIndexName(index)) + SQLiteHelper.getMethod(subWrapperTypeName ?: fieldTypeName), getIndexName(index, nameAllocator)) val fieldAccessBlock = subWrapperAccessor?.set(cursorAccess) ?: cursorAccess if (!isStubbed) { code.add(CodeBlock.of("\n.and(\$T.\$L.eq(\$L))", - referencedTableTypeName, propertyRepresentation, fieldAccessBlock)) + referencedTableTypeName, propertyRepresentation, fieldAccessBlock)) } else if (fieldLevelAccessor != null) { code.statement(fieldLevelAccessor.set(cursorAccess, parentAccessor.get(modelBlock))) } } - fun addColumnIndex(code: CodeBlock.Builder, index: Int) { + fun addColumnIndex(code: CodeBlock.Builder, index: Int, nameAllocator: NameAllocator) { if (!orderedCursorLookup) { code.statement(CodeBlock.of("int \$L = cursor.getColumnIndex(\$S)", - getIndexName(index), columnRepresentation)) + getIndexName(index, nameAllocator), columnRepresentation)) } } fun addIndexCheckStatement(code: CodeBlock.Builder, index: Int, - isLast: Boolean) { - if (!orderedCursorLookup) code.add("\$L != -1 && ", getIndexName(index)) + isLast: Boolean, nameAllocator: NameAllocator) { + if (!orderedCursorLookup) code.add("\$L != -1 && ", getIndexName(index, nameAllocator)) - code.add("!cursor.isNull(\$L)", getIndexName(index)) + code.add("!cursor.isNull(\$L)", getIndexName(index, nameAllocator)) if (!isLast) code.add(" && ") } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt index ac4f07b55..76b0c84aa 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt @@ -7,7 +7,8 @@ import com.raizlabs.android.dbflow.annotation.ForeignKey import com.raizlabs.android.dbflow.annotation.ForeignKeyAction import com.raizlabs.android.dbflow.annotation.ForeignKeyReference import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.processor.* +import com.raizlabs.android.dbflow.processor.ClassNames +import com.raizlabs.android.dbflow.processor.ProcessorManager import com.raizlabs.android.dbflow.processor.definition.TableDefinition import com.raizlabs.android.dbflow.processor.utils.* import com.raizlabs.android.dbflow.sql.QueryBuilder @@ -107,12 +108,12 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab } if (it.columnName.isNullOrEmpty()) { manager.logError("Found empty reference name at ${it.foreignColumnName}" + - " from table ${baseTableDefinition.elementName}") + " from table ${baseTableDefinition.elementName}") } typeBuilder.addField(FieldSpec.builder(propParam, it.columnName, - Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("new \$T(\$T.class, \$S)", propParam, tableClass, it.columnName) - .addJavadoc("Foreign Key" + if (isPrimaryKey) " / Primary Key" else "").build()) + Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("new \$T(\$T.class, \$S)", propParam, tableClass, it.columnName) + .addJavadoc("Foreign Key" + if (isPrimaryKey) " / Primary Key" else "").build()) } } @@ -239,9 +240,9 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab } } - override fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger): CodeBlock { + override fun getLoadFromCursorMethod(endNonPrimitiveIf: Boolean, index: AtomicInteger, nameAllocator: NameAllocator): CodeBlock { if (nonModelColumn) { - return super.getLoadFromCursorMethod(endNonPrimitiveIf, index) + return super.getLoadFromCursorMethod(endNonPrimitiveIf, index, nameAllocator) } else { checkNeedsReferences() @@ -249,11 +250,11 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab referencedTableClassName?.let { referencedTableClassName -> val tableDefinition = manager.getTableDefinition(baseTableDefinition.databaseDefinition?.elementTypeName, - referencedTableClassName) + referencedTableClassName) val outputClassName = tableDefinition?.outputClassName outputClassName?.let { val foreignKeyCombiner = ForeignKeyLoadFromCursorCombiner(columnAccessor, - referencedTableClassName, outputClassName, isStubbedRelationship) + referencedTableClassName, outputClassName, isStubbedRelationship, nameAllocator) _foreignKeyReferenceDefinitionList.forEach { foreignKeyCombiner.fieldAccesses += it.partialAccessor } @@ -283,8 +284,8 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab if (!nonModelColumn && columnAccessor !is TypeConverterScopeColumnAccessor) { referencedTableClassName?.let { referencedTableClassName -> val saveAccessor = ForeignKeyAccessField(columnName, - SaveModelAccessCombiner(Combiner(columnAccessor, referencedTableClassName, wrapperAccessor, - wrapperTypeName, subWrapperAccessor), implementsModel, extendsBaseModel)) + SaveModelAccessCombiner(Combiner(columnAccessor, referencedTableClassName, wrapperAccessor, + wrapperTypeName, subWrapperAccessor), implementsModel, extendsBaseModel)) saveAccessor.addCode(codeBuilder, 0, modelBlock) } } @@ -294,8 +295,8 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab if (!nonModelColumn && columnAccessor !is TypeConverterScopeColumnAccessor) { referencedTableClassName?.let { referencedTableClassName -> val deleteAccessor = ForeignKeyAccessField(columnName, - DeleteModelAccessCombiner(Combiner(columnAccessor, referencedTableClassName, wrapperAccessor, - wrapperTypeName, subWrapperAccessor), implementsModel, extendsBaseModel)) + DeleteModelAccessCombiner(Combiner(columnAccessor, referencedTableClassName, wrapperAccessor, + wrapperTypeName, subWrapperAccessor), implementsModel, extendsBaseModel)) deleteAccessor.addCode(codeBuilder, 0, modelBlock) } } @@ -308,18 +309,18 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab private fun checkNeedsReferences() { val tableDefinition = (baseTableDefinition as TableDefinition) val referencedTableDefinition = manager.getTableDefinition( - tableDefinition.databaseTypeName, referencedTableClassName) + tableDefinition.databaseTypeName, referencedTableClassName) if (referencedTableDefinition == null) { manager.logError(ForeignKeyColumnDefinition::class, - "Could not find the referenced table definition $referencedTableClassName" + - " from ${tableDefinition.tableName}. " + - "Ensure it exists in the samedatabase ${tableDefinition.databaseTypeName}") + "Could not find the referenced table definition $referencedTableClassName" + + " from ${tableDefinition.tableName}. " + + "Ensure it exists in the samedatabase ${tableDefinition.databaseTypeName}") } else if (needsReferences) { val primaryColumns = referencedTableDefinition.primaryColumnDefinitions if (references?.isEmpty() ?: true) { primaryColumns.forEach { val foreignKeyReferenceDefinition = ForeignKeyReferenceDefinition(manager, - elementName, it.elementName, it, this, primaryColumns.size) + elementName, it.elementName, it, this, primaryColumns.size) _foreignKeyReferenceDefinitionList.add(foreignKeyReferenceDefinition) } needsReferences = false @@ -328,13 +329,13 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab val foundDefinition = primaryColumns.find { it.columnName == reference.foreignKeyColumnName } if (foundDefinition == null) { manager.logError(ForeignKeyColumnDefinition::class, - "Could not find referenced column ${reference.foreignKeyColumnName} " + - "from reference named ${reference.columnName}") + "Could not find referenced column ${reference.foreignKeyColumnName} " + + "from reference named ${reference.columnName}") } else { _foreignKeyReferenceDefinitionList.add( - ForeignKeyReferenceDefinition(manager, elementName, - foundDefinition.elementName, foundDefinition, this, - primaryColumns.size, reference.columnName)) + ForeignKeyReferenceDefinition(manager, elementName, + foundDefinition.elementName, foundDefinition, this, + primaryColumns.size, reference.columnName)) } } needsReferences = false diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java index 0cb23f258..e55cfc112 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -3,6 +3,7 @@ import android.database.Cursor; import android.database.CursorWrapper; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; public class FlowCursor extends CursorWrapper { @@ -27,163 +28,248 @@ public Cursor getWrappedCursor() { } public String getStringOrDefault(int index, String defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getString(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getString(index); } else { return defValue; } } + public String getStringOrDefault(String columnName) { + return getStringOrDefault(cursor.getColumnIndex(columnName)); + } + + @Nullable + public String getStringOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getString(index); + } else { + return null; + } + } + public String getStringOrDefault(String columnName, String defValue) { - return getStringOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getStringOrDefault(cursor.getColumnIndex(columnName), defValue); + } + + public int getIntOrDefault(String columnName) { + return getIntOrDefault(cursor.getColumnIndex(columnName)); + } + + public int getIntOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getInt(index); + } else { + return 0; + } } public int getIntOrDefault(int index, int defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getInt(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getInt(index); } else { return defValue; } } public int getIntOrDefault(String columnName, int defValue) { - return getIntOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getIntOrDefault(cursor.getColumnIndex(columnName), defValue); } public Integer getIntOrDefault(int index, Integer defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getInt(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getInt(index); } else { return defValue; } } public Integer getIntOrDefault(String columnName, Integer defValue) { - return getIntOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getIntOrDefault(cursor.getColumnIndex(columnName), defValue); } public double getDoubleOrDefault(int index, double defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getDouble(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getDouble(index); } else { return defValue; } } + public double getDoubleOrDefault(String columnName) { + return getDoubleOrDefault(cursor.getColumnIndex(columnName)); + } + + public double getDoubleOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getDouble(index); + } else { + return 0; + } + } + public double getDoubleOrDefault(String columnName, double defValue) { - return getDoubleOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue); } public Double getDoubleOrDefault(int index, Double defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getDouble(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getDouble(index); } else { return defValue; } } public Double getDoubleOrDefault(String columnName, Double defValue) { - return getDoubleOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue); } public float getFloatOrDefault(int index, float defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getFloat(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getFloat(index); } else { return defValue; } } + public float getFloatOrDefault(String columnName) { + return getFloatOrDefault(cursor.getColumnIndex(columnName)); + } + + public float getFloatOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getFloat(index); + } else { + return 0; + } + } + public float getFloatOrDefault(String columnName, float defValue) { - return getFloatOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getFloatOrDefault(cursor.getColumnIndex(columnName), defValue); } public Float getFloatOrDefault(int index, Float defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getFloat(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getFloat(index); } else { return defValue; } } public Float getFloatOrDefault(String columnName, Float defValue) { - return getFloatOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getFloatOrDefault(cursor.getColumnIndex(columnName), defValue); } public long getLongOrDefault(int index, long defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getLong(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getLong(index); } else { return defValue; } } + public long getLongOrDefault(String columnName) { + return getLongOrDefault(cursor.getColumnIndex(columnName)); + } + + public long getLongOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getLong(index); + } else { + return 0; + } + } + public long getLongOrDefault(String columnName, long defValue) { - return getLongOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getLongOrDefault(cursor.getColumnIndex(columnName), defValue); } public Long getLongOrDefault(int index, Long defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getLong(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getLong(index); } else { return defValue; } } public Long getLongOrDefault(String columnName, Long defValue) { - return getLongOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getLongOrDefault(cursor.getColumnIndex(columnName), defValue); } public short getShortOrDefault(int index, short defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getShort(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getShort(index); } else { return defValue; } } + public short getShortOrDefault(String columnName) { + return getShortOrDefault(cursor.getColumnIndex(columnName)); + } + + public short getShortOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getShort(index); + } else { + return 0; + } + } + public short getShortOrDefault(String columnName, short defValue) { - return getShortOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getShortOrDefault(cursor.getColumnIndex(columnName), defValue); } public Short getShortOrDefault(int index, Short defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getShort(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getShort(index); } else { return defValue; } } public Short getShortOrDefault(String columnName, Short defValue) { - return getShortOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getShortOrDefault(cursor.getColumnIndex(columnName), defValue); } public byte[] getBlobOrDefault(int index, byte[] defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { - return getWrappedCursor().getBlob(index); + if (index != -1 && !cursor.isNull(index)) { + return cursor.getBlob(index); } else { return defValue; } } public byte[] getBlobOrDefault(String columnName, byte[] defValue) { - return getBlobOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getBlobOrDefault(cursor.getColumnIndex(columnName), defValue); } public boolean getBooleanOrDefault(int index, boolean defValue) { - if (index != -1 && !getWrappedCursor().isNull(index)) { + if (index != -1 && !cursor.isNull(index)) { return getBoolean(index); } else { return defValue; } } + public boolean getBooleanOrDefault(String columnName) { + return getBooleanOrDefault(cursor.getColumnIndex(columnName)); + } + + public boolean getBooleanOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return getBoolean(index); + } else { + return false; + } + } + public boolean getBooleanOrDefault(String columnName, boolean defValue) { - return getBooleanOrDefault(getWrappedCursor().getColumnIndex(columnName), defValue); + return getBooleanOrDefault(cursor.getColumnIndex(columnName), defValue); } public boolean getBoolean(int index) { - return getWrappedCursor().getInt(index) == 1; + return cursor.getInt(index) == 1; } } From 3111b83aa4ad01ec51c4ff024a4e0482aea35c06 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 11:58:16 -0400 Subject: [PATCH 18/37] [tests] fix failing test. --- .../android/dbflow/contentobserver/ContentObserverTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/contentobserver/ContentObserverTest.kt b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/contentobserver/ContentObserverTest.kt index 8d58835a7..aaf231ac4 100644 --- a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/contentobserver/ContentObserverTest.kt +++ b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/contentobserver/ContentObserverTest.kt @@ -59,7 +59,7 @@ class ContentObserverTest : BaseInstrumentedUnitTest() { @Test fun testSpecificUrlSave() { // insert on SAVE - assertProperConditions(BaseModel.Action.SAVE, { it.apply { age = 57 }.save() }) + assertProperConditions(BaseModel.Action.INSERT, { it.apply { age = 57 }.save() }) } @Test From efedf7d2072c5d998c20486a72599f0d270afbe0 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 12:00:20 -0400 Subject: [PATCH 19/37] [cursor] add javadoc for class. --- .../android/dbflow/structure/database/FlowCursor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java index e55cfc112..715b95ed9 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java @@ -5,6 +5,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +/** + * Common {@link Cursor} class that wraps cursors we use in this library with convenience loading methods. + * This is used to help cut down on generated code size and potentially decrease method count. + */ public class FlowCursor extends CursorWrapper { public static FlowCursor from(Cursor cursor) { From 2a2bad4905b57335807f0a0f4cc4576470b0ce9f Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 13:07:52 -0400 Subject: [PATCH 20/37] [foreign key] unique postfix on variables using generated table name. --- .../column/ForeignKeyAccessCombiner.kt | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt index 346373ed4..d4011b8b2 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyAccessCombiner.kt @@ -3,6 +3,7 @@ package com.raizlabs.android.dbflow.processor.definition.column import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.SQLiteHelper import com.raizlabs.android.dbflow.processor.utils.statement +import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.NameAllocator import com.squareup.javapoet.TypeName @@ -73,8 +74,9 @@ class ForeignKeyLoadFromCursorCombiner(val fieldAccessor: ColumnAccessor, } for ((i, it) in fieldAccesses.withIndex()) { it.addRetrieval(setterBlock, index.get(), referencedTableTypeName, isStubbed, fieldAccessor, nameAllocator) - it.addColumnIndex(code, index.get(), nameAllocator) - it.addIndexCheckStatement(ifChecker, index.get(), i == fieldAccesses.size - 1, nameAllocator) + it.addColumnIndex(code, index.get(), referencedTableTypeName, nameAllocator) + it.addIndexCheckStatement(ifChecker, index.get(), referencedTableTypeName, + i == fieldAccesses.size - 1, nameAllocator) if (i < fieldAccesses.size - 1) { index.incrementAndGet() @@ -106,10 +108,12 @@ class PartialLoadFromCursorAccessCombiner( var indexName: CodeBlock? = null - fun getIndexName(index: Int, nameAllocator: NameAllocator): CodeBlock { + fun getIndexName(index: Int, nameAllocator: NameAllocator, referencedTypeName: TypeName): CodeBlock { if (indexName == null) { indexName = if (!orderedCursorLookup) { - CodeBlock.of(nameAllocator.newName("index_$columnRepresentation", columnRepresentation)) + // post fix with referenced type name simple name + CodeBlock.of(nameAllocator.newName("index_${columnRepresentation}_" + + if (referencedTypeName is ClassName) referencedTypeName.simpleName() else "", columnRepresentation)) } else { CodeBlock.of(index.toString()) } @@ -122,7 +126,8 @@ class PartialLoadFromCursorAccessCombiner( isStubbed: Boolean, parentAccessor: ColumnAccessor, nameAllocator: NameAllocator) { val cursorAccess = CodeBlock.of("cursor.\$L(\$L)", - SQLiteHelper.getMethod(subWrapperTypeName ?: fieldTypeName), getIndexName(index, nameAllocator)) + SQLiteHelper.getMethod(subWrapperTypeName ?: fieldTypeName), + getIndexName(index, nameAllocator, referencedTableTypeName)) val fieldAccessBlock = subWrapperAccessor?.set(cursorAccess) ?: cursorAccess if (!isStubbed) { @@ -134,18 +139,22 @@ class PartialLoadFromCursorAccessCombiner( } - fun addColumnIndex(code: CodeBlock.Builder, index: Int, nameAllocator: NameAllocator) { + fun addColumnIndex(code: CodeBlock.Builder, index: Int, + referencedTableTypeName: TypeName, + nameAllocator: NameAllocator) { if (!orderedCursorLookup) { code.statement(CodeBlock.of("int \$L = cursor.getColumnIndex(\$S)", - getIndexName(index, nameAllocator), columnRepresentation)) + getIndexName(index, nameAllocator, referencedTableTypeName), columnRepresentation)) } } fun addIndexCheckStatement(code: CodeBlock.Builder, index: Int, + referencedTableTypeName: TypeName, isLast: Boolean, nameAllocator: NameAllocator) { - if (!orderedCursorLookup) code.add("\$L != -1 && ", getIndexName(index, nameAllocator)) + val indexName = getIndexName(index, nameAllocator, referencedTableTypeName) + if (!orderedCursorLookup) code.add("$indexName != -1 && ") - code.add("!cursor.isNull(\$L)", getIndexName(index, nameAllocator)) + code.add("!cursor.isNull($indexName)") if (!isLast) code.add(" && ") } From e1ab6a0ba957c5daa503fa441afa3bd111ae9f69 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 13:54:19 -0400 Subject: [PATCH 21/37] [typeconverter] fix issue where nullable char fields did not compile properly by adding CharConverter --- .../android/dbflow/converter/CharConverter.java | 17 +++++++++++++++++ .../android/dbflow/processor/Handlers.kt | 3 ++- .../definition/column/ColumnDefinition.kt | 12 ++++++++---- .../android/dbflow/models/SimpleTestModels.kt | 3 +++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java new file mode 100644 index 000000000..f7bf81692 --- /dev/null +++ b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java @@ -0,0 +1,17 @@ +package com.raizlabs.android.dbflow.converter; + +/** + * Description: Converts a {@link Character} into a {@link String} for database storage. + */ +public class CharConverter extends TypeConverter { + + @Override + public String getDBValue(Character model) { + return model != null ? new String(new char[]{model}) : null; + } + + @Override + public Character getModelValue(String data) { + return data != null ? data.charAt(0) : null; + } +} diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt index c49810d7a..92a6a72ef 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/Handlers.kt @@ -157,7 +157,8 @@ class TypeConverterHandler : BaseContainerHandler() { private val DEFAULT_TYPE_CONVERTERS = arrayOf>(CalendarConverter::class.java, BigDecimalConverter::class.java, DateConverter::class.java, SqlDateConverter::class.java, - BooleanConverter::class.java, UUIDConverter::class.java) + BooleanConverter::class.java, UUIDConverter::class.java, + CharConverter::class.java) } } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index fc5c68f9b..fba1b4e14 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt @@ -36,6 +36,7 @@ constructor(processorManager: ProcessorManager, element: Element, private val QUOTE_PATTERN = Pattern.compile("\".*\"") var columnName: String = "" + var propertyFieldName: String = "" var hasTypeConverter: Boolean = false var isPrimaryKey: Boolean = false @@ -109,6 +110,9 @@ constructor(processorManager: ProcessorManager, element: Element, this.columnName = element.simpleName.toString() } + val nameAllocator = NameAllocator() + propertyFieldName = nameAllocator.newName(this.columnName) + if (isPackagePrivate) { columnAccessor = PackagePrivateScopeColumnAccessor(elementName, packageName, baseTableDefinition.databaseDefinition?.classSeparator, @@ -259,7 +263,7 @@ constructor(processorManager: ProcessorManager, element: Element, } val fieldBuilder = FieldSpec.builder(propParam, - columnName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + propertyFieldName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) if (isNonPrimitiveTypeConverter) { val codeBlock = CodeBlock.builder() @@ -290,13 +294,13 @@ constructor(processorManager: ProcessorManager, element: Element, open fun addPropertyCase(methodBuilder: MethodSpec.Builder) { methodBuilder.apply { beginControlFlow("case \$S: ", QueryBuilder.quote(columnName)) - addStatement("return \$L", columnName) + addStatement("return \$L", propertyFieldName) endControlFlow() } } open fun addColumnName(codeBuilder: CodeBlock.Builder) { - codeBuilder.add(columnName) + codeBuilder.add(propertyFieldName) } open val contentValuesStatement: CodeBlock @@ -373,7 +377,7 @@ constructor(processorManager: ProcessorManager, element: Element, open fun appendPropertyComparisonAccessStatement(codeBuilder: CodeBlock.Builder) { PrimaryReferenceAccessCombiner(combiner).apply { - codeBuilder.addCode(columnName, getDefaultValueBlock(), 0, modelBlock) + codeBuilder.addCode(propertyFieldName, getDefaultValueBlock(), 0, modelBlock) } } diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt index b0ea4b9ad..cce4690c8 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/SimpleTestModels.kt @@ -17,6 +17,9 @@ class SimpleCustomModel(@Column var name: String? = "") @Table(database = TestDatabase::class, insertConflict = ConflictAction.FAIL, updateConflict = ConflictAction.FAIL) class NumberModel(@PrimaryKey var id: Int = 0) +@Table(database = TestDatabase::class) +class CharModel(@PrimaryKey var id: Int = 0, @Column var char: Char? = null) + @Table(database = TestDatabase::class) class TwoColumnModel(@PrimaryKey var name: String? = "", @Column var id: Int = 0) From 7c12ce9f659777fa2dc863691df5a79b24f118a6 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 14:10:01 -0400 Subject: [PATCH 22/37] [relationships] add section on custom @ForeignKeyReference --- usage2/Relationships.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/usage2/Relationships.md b/usage2/Relationships.md index befd11a6e..ba69ce54d 100644 --- a/usage2/Relationships.md +++ b/usage2/Relationships.md @@ -107,6 +107,19 @@ public class Queen extends BaseModel { ``` +### Custom ForeignKeyReferences +When simple `@ForeignKey` annotation is not enough, you can manually specify references for your table: + +```java + +@ForeignKey(saveForeignKeyModel = false, +references = {@ForeignKeyReference(columnName = "colony", foreignKeyColumnName = "id")}) +Colony colony; + +``` + +By default not specifying references will take each field and append "${foreignKeyFieldName}_${ForeignKeyReferenceColumnName}" to make the reference column name. So by default the previous example would use `colony_id` without references. With references it becomes `colony`. + ## Many To Many From e396e765d90df1b2f6090bb89859753f5a2ac2ba Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 14:35:43 -0400 Subject: [PATCH 23/37] [references] fix reference to specify proper variable name for access only and not reference name. --- .../column/ForeignKeyReferenceDefinition.kt | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyReferenceDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyReferenceDefinition.kt index bfa6a6ca3..3d0593ee7 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyReferenceDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyReferenceDefinition.kt @@ -49,18 +49,17 @@ class ForeignKeyReferenceDefinition(private val manager: ProcessorManager, foreignKeyFieldName: String, getterSetter: GetterSetter, name: String, packageName: String) { if (isReferencedFieldPrivate) { - val isBoolean = columnClassName?.box() == TypeName.BOOLEAN.box() columnAccessor = PrivateScopeColumnAccessor(foreignKeyFieldName, getterSetter, false) } else if (isReferencedFieldPackagePrivate) { - columnAccessor = PackagePrivateScopeColumnAccessor(foreignColumnName, packageName, - foreignKeyColumnDefinition.baseTableDefinition.databaseDefinition?.classSeparator, - name) + columnAccessor = PackagePrivateScopeColumnAccessor(foreignKeyFieldName, packageName, + foreignKeyColumnDefinition.baseTableDefinition.databaseDefinition?.classSeparator, + name) PackagePrivateScopeColumnAccessor.putElement( - (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, - foreignColumnName) + (columnAccessor as PackagePrivateScopeColumnAccessor).helperClassName, + foreignKeyFieldName) } else { - columnAccessor = VisibleScopeColumnAccessor(foreignColumnName) + columnAccessor = VisibleScopeColumnAccessor(foreignKeyFieldName) } } @@ -70,10 +69,10 @@ class ForeignKeyReferenceDefinition(private val manager: ProcessorManager, evaluateTypeConverter(typeConverterDefinition) val combiner = Combiner(columnAccessor, columnClassName!!, wrapperAccessor, - wrapperTypeName, subWrapperAccessor) + wrapperTypeName, subWrapperAccessor) partialAccessor = PartialLoadFromCursorAccessCombiner(columnName, foreignColumnName, - columnClassName, foreignKeyColumnDefinition.baseTableDefinition.orderedCursorLookUp, - columnAccessor, wrapperAccessor, wrapperTypeName) + columnClassName, foreignKeyColumnDefinition.baseTableDefinition.orderedCursorLookUp, + columnAccessor, wrapperAccessor, wrapperTypeName) primaryReferenceField = ForeignKeyAccessField(columnName, PrimaryReferenceAccessCombiner(combiner)) @@ -89,12 +88,12 @@ class ForeignKeyReferenceDefinition(private val manager: ProcessorManager, if (it.modelTypeName != columnClassName) { manager.logError("The specified custom TypeConverter's Model Value %1s from %1s must match the type of the column %1s. ", - it.modelTypeName, it.className, columnClassName) + it.modelTypeName, it.className, columnClassName) } else { hasTypeConverter = true val fieldName = foreignKeyColumnDefinition.baseTableDefinition - .addColumnForTypeConverter(foreignKeyColumnDefinition, it.className) + .addColumnForTypeConverter(foreignKeyColumnDefinition, it.className) wrapperAccessor = TypeConverterScopeColumnAccessor(fieldName) wrapperTypeName = it.dbTypeName @@ -110,7 +109,7 @@ class ForeignKeyReferenceDefinition(private val manager: ProcessorManager, if (!localColumnName.isNullOrEmpty()) { this.columnName = localColumnName } else if (!foreignKeyColumnDefinition.isPrimaryKey && !foreignKeyColumnDefinition.isPrimaryKeyAutoIncrement - && !foreignKeyColumnDefinition.isRowId || referenceCount > 0) { + && !foreignKeyColumnDefinition.isRowId || referenceCount > 0) { this.columnName = foreignKeyFieldName + "_" + referencedColumn.columnName } else { this.columnName = foreignKeyFieldName @@ -121,8 +120,8 @@ class ForeignKeyReferenceDefinition(private val manager: ProcessorManager, isReferencedFieldPackagePrivate = referencedColumn.columnAccessor is PackagePrivateScopeColumnAccessor val isPackagePrivate = ElementUtility.isPackagePrivate(referencedColumn.element) val isPackagePrivateNotInSamePackage = isPackagePrivate && - !ElementUtility.isInSamePackage(manager, referencedColumn.element, - foreignKeyColumnDefinition.element) + !ElementUtility.isInSamePackage(manager, referencedColumn.element, + foreignKeyColumnDefinition.element) isReferencedFieldPackagePrivate = isReferencedFieldPackagePrivate || isPackagePrivateNotInSamePackage val packageName = referencedColumn.packageName val name = ClassName.get(referencedColumn.element.enclosingElement as TypeElement).simpleName() From 32f4f3f32adb7e3b621a62544acb52427eace79d Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 14:44:10 -0400 Subject: [PATCH 24/37] [querymodels] fix both modelview and query model definitions from reading wrong element when checking validity. --- .../processor/definition/ModelViewDefinition.kt | 6 +++--- .../processor/definition/QueryModelDefinition.kt | 15 +++++++++------ .../raizlabs/android/dbflow/models/QueryModels.kt | 5 ++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt index 152e5ff62..f10baa3ac 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/ModelViewDefinition.kt @@ -90,13 +90,13 @@ class ModelViewDefinition(manager: ProcessorManager, element: Element) : BaseTab val columnValidator = ColumnValidator() for (variableElement in variableElements) { - val isValidAllFields = ElementUtility.isValidAllFields(allFields, element) + val isValidAllFields = ElementUtility.isValidAllFields(allFields, variableElement) if (variableElement.annotation() != null || isValidAllFields) { // package private, will generate helper - val isPackagePrivate = ElementUtility.isPackagePrivate(element) - val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, element, this.element) + val isPackagePrivate = ElementUtility.isPackagePrivate(variableElement) + val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, variableElement, this.element) val columnDefinition = ColumnDefinition(manager, variableElement, this, isPackagePrivateNotInSamePackage) if (columnValidator.validate(manager, columnDefinition)) { diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt index cd4dc7383..c7e38409f 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/QueryModelDefinition.kt @@ -1,6 +1,9 @@ package com.raizlabs.android.dbflow.processor.definition -import com.grosner.kpoet.* +import com.grosner.kpoet.`return` +import com.grosner.kpoet.final +import com.grosner.kpoet.modifiers +import com.grosner.kpoet.public import com.raizlabs.android.dbflow.annotation.Column import com.raizlabs.android.dbflow.annotation.QueryModel import com.raizlabs.android.dbflow.processor.ClassNames @@ -47,7 +50,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana if (element is TypeElement) { implementsLoadFromCursorListener = - (element as TypeElement).implementsClass(manager.processingEnvironment, ClassNames.LOAD_FROM_CURSOR_LISTENER) + (element as TypeElement).implementsClass(manager.processingEnvironment, ClassNames.LOAD_FROM_CURSOR_LISTENER) } @@ -87,7 +90,7 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana } methods.mapNotNull { it.methodSpec } - .forEach { typeBuilder.addMethod(it) } + .forEach { typeBuilder.addMethod(it) } } override fun createColumnDefinitions(typeElement: TypeElement) { @@ -101,10 +104,10 @@ class QueryModelDefinition(typeElement: Element, processorManager: ProcessorMana for (variableElement in variableElements) { // no private static or final fields - val isAllFields = ElementUtility.isValidAllFields(allFields, element) + val isAllFields = ElementUtility.isValidAllFields(allFields, variableElement) // package private, will generate helper - val isPackagePrivate = ElementUtility.isPackagePrivate(element) - val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, element, this.element) + val isPackagePrivate = ElementUtility.isPackagePrivate(variableElement) + val isPackagePrivateNotInSamePackage = isPackagePrivate && !ElementUtility.isInSamePackage(manager, variableElement, this.element) if (variableElement.annotation() != null || isAllFields) { diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModels.kt index 8c4c2fcac..86d61f072 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModels.kt @@ -23,4 +23,7 @@ class CustomBlobModel(@Column var myBlob: MyBlob? = null) { override fun getModelValue(data: Blob) = MyBlob(data.blob) } -} \ No newline at end of file +} + +@QueryModel(database = TestDatabase::class, allFields = true) +class AllFieldsQueryModel(var model: String? = null) \ No newline at end of file From a8bebadfe5d92a6c1e7eaf5ca98e817649125c1d Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 15:23:57 -0400 Subject: [PATCH 25/37] [db] add on downgrade method for DBHelperListeenr. --- .../database/BaseDatabaseHelper.java | 14 +++++++---- .../database/BaseDatabaseStatement.java | 1 + .../database/DatabaseHelperDelegate.java | 24 ++++++++++++------- .../database/DatabaseHelperListener.java | 9 ++++++- .../database/FlowSQLiteOpenHelper.java | 10 ++++++++ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java index 0dedd1a24..35a9a3663 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java @@ -57,6 +57,10 @@ public void onOpen(DatabaseWrapper db) { checkForeignKeySupport(db); } + public void onDowngrade(DatabaseWrapper db, int oldVersion, int newVersion) { + checkForeignKeySupport(db); + } + /** * If foreign keys are supported, we turn it on the DB specified. */ @@ -86,10 +90,10 @@ protected void executeCreations(final DatabaseWrapper database) { List modelViews = databaseDefinition.getModelViewAdapters(); for (ModelViewAdapter modelView : modelViews) { QueryBuilder queryBuilder = new QueryBuilder() - .append("CREATE VIEW IF NOT EXISTS") - .appendSpaceSeparated(modelView.getViewName()) - .append("AS ") - .append(modelView.getCreationQuery()); + .append("CREATE VIEW IF NOT EXISTS") + .appendSpaceSeparated(modelView.getViewName()) + .append("AS ") + .append(modelView.getCreationQuery()); try { database.execSQL(queryBuilder.getQuery()); } catch (SQLiteException e) { @@ -108,7 +112,7 @@ protected void executeMigrations(final DatabaseWrapper db, final int oldVersion, // will try migrations file or execute migrations from code try { final List files = Arrays.asList(FlowManager.getContext().getAssets().list( - MIGRATION_PATH + "/" + databaseDefinition.getDatabaseName())); + MIGRATION_PATH + "/" + databaseDefinition.getDatabaseName())); Collections.sort(files, new NaturalOrderComparator()); final Map> migrationFileMap = new HashMap<>(); diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java index 14d91c735..ba888970d 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java @@ -47,4 +47,5 @@ public void bindBlobOrNull(int index, @Nullable byte[] bytes) { bindNull(index); } } + } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java index 34a25cd3d..399624068 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java @@ -42,12 +42,12 @@ public DatabaseHelperDelegate(DatabaseHelperListener databaseHelperListener, public void performRestoreFromBackup() { movePrepackagedDB(getDatabaseDefinition().getDatabaseFileName(), - getDatabaseDefinition().getDatabaseFileName()); + getDatabaseDefinition().getDatabaseFileName()); if (getDatabaseDefinition().backupEnabled()) { if (backupHelper == null) { throw new IllegalStateException("the passed backup helper was null, even though backup is enabled. " + - "Ensure that its passed in."); + "Ensure that its passed in."); } restoreDatabase(getTempDbFileName(), getDatabaseDefinition().getDatabaseFileName()); backupHelper.getDatabase(); @@ -86,6 +86,14 @@ public void onOpen(DatabaseWrapper db) { super.onOpen(db); } + @Override + public void onDowngrade(DatabaseWrapper db, int oldVersion, int newVersion) { + if (databaseHelperListener != null) { + databaseHelperListener.onDowngrade(db, oldVersion, newVersion); + } + super.onDowngrade(db, oldVersion, newVersion); + } + /** * @return the temporary database file name for when we have backups enabled * {@link DatabaseDefinition#backupEnabled()} @@ -106,8 +114,8 @@ public void movePrepackagedDB(String databaseName, String prepackagedName) { // If the database already exists, and is ok return if (dbPath.exists() && (!getDatabaseDefinition().areConsistencyChecksEnabled() || - (getDatabaseDefinition().areConsistencyChecksEnabled() - && isDatabaseIntegrityOk(getWritableDatabase())))) { + (getDatabaseDefinition().areConsistencyChecksEnabled() + && isDatabaseIntegrityOk(getWritableDatabase())))) { return; } @@ -121,7 +129,7 @@ && isDatabaseIntegrityOk(getWritableDatabase())))) { InputStream inputStream; // if it exists and the integrity is ok we use backup as the main DB is no longer valid if (existingDb.exists() && (!getDatabaseDefinition().backupEnabled() || getDatabaseDefinition().backupEnabled() - && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { + && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { inputStream = new FileInputStream(existingDb); } else { inputStream = FlowManager.getContext().getAssets().open(prepackagedName); @@ -161,7 +169,7 @@ public boolean isDatabaseIntegrityOk(DatabaseWrapper databaseWrapper) { if (!result.equalsIgnoreCase("ok")) { // integrity_checker failed on main or attached databases FlowLog.log(FlowLog.Level.E, "PRAGMA integrity_check on " + - getDatabaseDefinition().getDatabaseName() + " returned: " + result); + getDatabaseDefinition().getDatabaseName() + " returned: " + result); integrityOk = false; @@ -246,7 +254,7 @@ public void restoreDatabase(String databaseName, String prepackagedName) { InputStream inputStream; // if it exists and the integrity is ok if (existingDb.exists() && (getDatabaseDefinition().backupEnabled() - && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { + && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { inputStream = new FileInputStream(existingDb); } else { inputStream = FlowManager.getContext().getAssets().open(prepackagedName); @@ -265,7 +273,7 @@ public void restoreDatabase(String databaseName, String prepackagedName) { public void backupDB() { if (!getDatabaseDefinition().backupEnabled() || !getDatabaseDefinition().areConsistencyChecksEnabled()) { throw new IllegalStateException("Backups are not enabled for : " + getDatabaseDefinition().getDatabaseName() + ". Please consider adding " + - "both backupEnabled and consistency checks enabled to the Database annotation"); + "both backupEnabled and consistency checks enabled to the Database annotation"); } getDatabaseDefinition().beginTransactionAsync(new ITransaction() { diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java index b00518226..d8de94442 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java @@ -28,5 +28,12 @@ public interface DatabaseHelperListener { */ void onUpgrade(DatabaseWrapper database, int oldVersion, int newVersion); - + /** + * Called when DB is downgraded. Note that this may not be supported by all implementations of the DB. + * + * @param databaseWrapper The database downgraded. + * @param oldVersion The old. higher version. + * @param newVersion The new lower version. + */ + void onDowngrade(DatabaseWrapper databaseWrapper, int oldVersion, int newVersion); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java index 7d2b54231..bd9492b63 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java @@ -81,6 +81,11 @@ public void onOpen(SQLiteDatabase db) { databaseHelperDelegate.onOpen(AndroidDatabase.from(db)); } + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + databaseHelperDelegate.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion); + } + @Override public void closeDB() { getDatabase(); @@ -145,6 +150,11 @@ public void onOpen(SQLiteDatabase db) { baseDatabaseHelper.onOpen(AndroidDatabase.from(db)); } + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + baseDatabaseHelper.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion); + } + @Override public void closeDB() { } From be8a8556580e8c0e12478c6f569f01015b510d8e Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Mon, 24 Apr 2017 16:20:51 -0400 Subject: [PATCH 26/37] [onetomany] support databasewrapper param in the onetoManyMethod. --- .../dbflow/processor/definition/Methods.kt | 75 +++++++++++-------- .../definition/OneToManyDefinition.kt | 54 ++++++++----- .../definition/column/ColumnAccessor.kt | 21 +++--- .../dbflow/models/OneToManyModelTest.kt | 5 +- .../android/dbflow/models/OneToManyModels.kt | 9 ++- 5 files changed, 100 insertions(+), 64 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 2f2e28968..9f9fd974a 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -36,11 +36,11 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini override val methodSpec: MethodSpec? get() { val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "bindToInsertValues" else "bindToContentValues") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.CONTENT_VALUES, PARAM_CONTENT_VALUES) - .addParameter(baseTableDefinition.parameterClassName, ModelUtils.variable) - .returns(TypeName.VOID) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.CONTENT_VALUES, PARAM_CONTENT_VALUES) + .addParameter(baseTableDefinition.parameterClassName, ModelUtils.variable) + .returns(TypeName.VOID) var retMethodBuilder: MethodSpec.Builder? = methodBuilder @@ -53,7 +53,7 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini if (implementsContentValuesListener) { methodBuilder.addStatement("\$L.onBindTo\$LValues(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) + ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) } } else { if (baseTableDefinition.hasAutoIncrement || baseTableDefinition.hasRowID) { @@ -68,7 +68,7 @@ class BindToContentValuesMethod(private val baseTableDefinition: BaseTableDefini methodBuilder.addStatement("bindToInsertValues(\$L, \$L)", PARAM_CONTENT_VALUES, ModelUtils.variable) if (implementsContentValuesListener) { methodBuilder.addStatement("\$L.onBindTo\$LValues(\$L)", - ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) + ModelUtils.variable, if (isInsert) "Insert" else "Content", PARAM_CONTENT_VALUES) } } @@ -88,11 +88,11 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat override val methodSpec: MethodSpec? get() { val methodBuilder = MethodSpec.methodBuilder(if (isInsert) "bindToInsertStatement" else "bindToStatement") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.DATABASE_STATEMENT, PARAM_STATEMENT) - .addParameter(tableDefinition.parameterClassName, - ModelUtils.variable).returns(TypeName.VOID) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addParameter(ClassNames.DATABASE_STATEMENT, PARAM_STATEMENT) + .addParameter(tableDefinition.parameterClassName, + ModelUtils.variable).returns(TypeName.VOID) // write the reference method if (isInsert) { @@ -107,7 +107,7 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat if (tableDefinition.implementsSqlStatementListener) { methodBuilder.addStatement("${ModelUtils.variable}.onBindTo\$LStatement($PARAM_STATEMENT)", - if (isInsert) "Insert" else "") + if (isInsert) "Insert" else "") } } else { var start = 0 @@ -121,7 +121,7 @@ class BindToStatementMethod(private val tableDefinition: TableDefinition, privat } else if (tableDefinition.implementsSqlStatementListener) { methodBuilder.addStatement("bindToInsertStatement($PARAM_STATEMENT, ${ModelUtils.variable}, $start)") methodBuilder.addStatement("${ModelUtils.variable}.onBindTo\$LStatement($PARAM_STATEMENT)", - if (isInsert) "Insert" else "") + if (isInsert) "Insert" else "") } else { // don't generate method return null @@ -172,7 +172,7 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method } val codeBuilder = CodeBlock.builder() - .add("return ${creationBuilder.toString().S}") + .add("return ${creationBuilder.toString().S}") val foreignKeyBlocks = ArrayList() val tableNameBlocks = ArrayList() @@ -237,7 +237,7 @@ class CustomTypeConverterPropertyMethod(private val baseTableDefinition: BaseTab val firstDef = def?.get(0) firstDef?.typeConverterElementNames?.forEach { elementName -> code.statement("global_typeConverter${it.simpleName()} " + - "= (\$T) holder.getTypeConverterForClass(\$T.class)", it, elementName) + "= (\$T) holder.getTypeConverterForClass(\$T.class)", it, elementName) } } return code @@ -251,8 +251,8 @@ class ExistenceMethod(private val tableDefinition: BaseTableDefinition) : Method override val methodSpec get() = `override fun`(TypeName.BOOLEAN, "exists", - param(tableDefinition.parameterClassName!!, ModelUtils.variable), - param(ClassNames.DATABASE_WRAPPER, "wrapper")) { + param(tableDefinition.parameterClassName!!, ModelUtils.variable), + param(ClassNames.DATABASE_WRAPPER, "wrapper")) { modifiers(public, final) code { // only quick check if enabled. @@ -277,10 +277,10 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p return null // dont write method here because we reuse the compiled statement query method } return `override fun`(String::class, - if (isInsert) "getInsertStatementQuery" else "getCompiledStatementQuery") { + if (isInsert) "getInsertStatementQuery" else "getCompiledStatementQuery") { modifiers(public, final) val isSingleAutoincrement = tableDefinition.hasAutoIncrement - && tableDefinition.columnDefinitions.size == 1 && isInsert + && tableDefinition.columnDefinitions.size == 1 && isInsert `return`(codeBlock { add("INSERT ") if (!tableDefinition.insertConflictActionName.isEmpty()) { @@ -298,10 +298,10 @@ class InsertStatementQueryMethod(private val tableDefinition: TableDefinition, p add(") VALUES (") tableDefinition.columnDefinitions.filter { !it.isPrimaryKeyAutoIncrement && !it.isRowId || !isInsert } - .forEachIndexed { index, columnDefinition -> - if (index > 0) add(",") - add(columnDefinition.insertStatementValuesString) - } + .forEachIndexed { index, columnDefinition -> + if (index > 0) add(",") + add(columnDefinition.insertStatementValuesString) + } if (isSingleAutoincrement) add("NULL") @@ -318,8 +318,8 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) override val methodSpec: MethodSpec get() = `override fun`(TypeName.VOID, "loadFromCursor", - param(ClassNames.FLOW_CURSOR, PARAM_CURSOR), - param(baseTableDefinition.parameterClassName!!, ModelUtils.variable)) { + param(ClassNames.FLOW_CURSOR, PARAM_CURSOR), + param(baseTableDefinition.parameterClassName!!, ModelUtils.variable)) { modifiers(public, final) val index = AtomicInteger(0) val nameAllocator = NameAllocator() // unique names @@ -329,11 +329,13 @@ class LoadFromCursorMethod(private val baseTableDefinition: BaseTableDefinition) } if (baseTableDefinition is TableDefinition) { + val foundDef = baseTableDefinition.oneToManyDefinitions.find { it.hasWrapper } + foundDef?.let { foundDef.writeWrapperStatement(this) } code { baseTableDefinition.oneToManyDefinitions - .filter { it.isLoad } - .forEach { it.writeLoad(this) } + .filter { it.isLoad } + .forEach { it.writeLoad(this) } this } } @@ -362,7 +364,7 @@ class OneToManyDeleteMethod(private val tableDefinition: TableDefinition, val shouldWrite = tableDefinition.oneToManyDefinitions.any { it.isDelete } if (shouldWrite || tableDefinition.cachingEnabled) { return `override fun`(TypeName.BOOLEAN, "delete", - param(tableDefinition.elementClassName!!, ModelUtils.variable)) { + param(tableDefinition.elementClassName!!, ModelUtils.variable)) { modifiers(public, final) if (useWrapper) { addParameter(ClassNames.DATABASE_WRAPPER, ModelUtils.wrapper) @@ -373,6 +375,11 @@ class OneToManyDeleteMethod(private val tableDefinition: TableDefinition, statement("boolean successful = super.delete(${ModelUtils.variable}${wrapperCommaIfBaseModel(useWrapper)})") + if (!useWrapper) { + val foundDef = tableDefinition.oneToManyDefinitions.find { it.hasWrapper } + foundDef?.let { foundDef.writeWrapperStatement(this) } + } + tableDefinition.oneToManyDefinitions.forEach { it.writeDelete(this, useWrapper) } `return`("successful") @@ -402,7 +409,7 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, } return `override fun`(retType, methodName, - param(tableDefinition.elementClassName!!, ModelUtils.variable)) { + param(tableDefinition.elementClassName!!, ModelUtils.variable)) { modifiers(public, final) if (useWrapper) { @@ -422,6 +429,12 @@ class OneToManySaveMethod(private val tableDefinition: TableDefinition, this } + // need wrapper access if we have wrapper param here. + if (!useWrapper) { + val foundDef = tableDefinition.oneToManyDefinitions.find { it.hasWrapper } + foundDef?.let { foundDef.writeWrapperStatement(this) } + } + for (oneToManyDefinition in tableDefinition.oneToManyDefinitions) { when (methodName) { METHOD_SAVE -> oneToManyDefinition.writeSave(this, useWrapper) @@ -452,7 +465,7 @@ class PrimaryConditionMethod(private val tableDefinition: BaseTableDefinition) : override val methodSpec: MethodSpec? get() = `override fun`(ClassNames.OPERATOR_GROUP, "getPrimaryConditionClause", - param(tableDefinition.parameterClassName!!, ModelUtils.variable)) { + param(tableDefinition.parameterClassName!!, ModelUtils.variable)) { modifiers(public, final) code { statement("\$T clause = \$T.clause()", ClassNames.OPERATOR_GROUP, ClassNames.OPERATOR_GROUP) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index dfbbcd13f..90c32e09a 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -1,10 +1,7 @@ package com.raizlabs.android.dbflow.processor.definition import com.google.common.collect.Lists -import com.grosner.kpoet.`for` -import com.grosner.kpoet.`if` -import com.grosner.kpoet.end -import com.grosner.kpoet.statement +import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.processor.ClassNames import com.raizlabs.android.dbflow.processor.ProcessorManager @@ -17,8 +14,8 @@ import javax.lang.model.element.TypeElement /** * Description: Represents the [OneToMany] annotation. */ -class OneToManyDefinition(typeElement: ExecutableElement, - processorManager: ProcessorManager) : BaseDefinition(typeElement, processorManager) { +class OneToManyDefinition(executableElement: ExecutableElement, + processorManager: ProcessorManager) : BaseDefinition(executableElement, processorManager) { private var _methodName: String @@ -38,17 +35,19 @@ class OneToManyDefinition(typeElement: ExecutableElement, val isSave: Boolean get() = isAll || methods.contains(OneToMany.Method.SAVE) + var referencedTableType: TypeName? = null + var hasWrapper = false + private var columnAccessor: ColumnAccessor private var extendsBaseModel: Boolean = false private var extendsModel: Boolean = false - private var referencedTableType: TypeName? = null private var referencedType: TypeElement? = null init { - val oneToMany = typeElement.annotation()!! + val oneToMany = executableElement.annotation()!! - _methodName = typeElement.simpleName.toString() + _methodName = executableElement.simpleName.toString() _variableName = oneToMany.variableName if (_variableName.isEmpty()) { _variableName = _methodName.replace("get", "") @@ -56,17 +55,32 @@ class OneToManyDefinition(typeElement: ExecutableElement, } methods.addAll(oneToMany.methods) + val parameters = executableElement.parameters + if (parameters.isNotEmpty()) { + if (parameters.size > 1) { + manager.logError(OneToManyDefinition::class, "OneToMany Methods can only have one parameter and that be the DatabaseWrapper.") + } else { + val param = parameters[0] + val name = param.asType().typeName + if (name == ClassNames.DATABASE_WRAPPER) { + hasWrapper = true + } else { + manager.logError(OneToManyDefinition::class, "OneToMany Methods can only specify a ${ClassNames.DATABASE_WRAPPER} as its parameter.") + } + } + } + if (oneToMany.isVariablePrivate) { columnAccessor = PrivateScopeColumnAccessor(_variableName, object : GetterSetter { override val getterName: String = "" override val setterName: String = "" - }) + }, optionalGetterParam = if (hasWrapper) ModelUtils.wrapper else "") } else { columnAccessor = VisibleScopeColumnAccessor(_variableName) } extendsBaseModel = false - val returnType = typeElement.returnType + val returnType = executableElement.returnType val typeName = TypeName.get(returnType) if (typeName is ParameterizedTypeName) { val typeArguments = typeName.typeArguments @@ -82,10 +96,17 @@ class OneToManyDefinition(typeElement: ExecutableElement, extendsModel = referencedType.isSubclass(manager.processingEnvironment, ClassNames.MODEL) } } + } - private val methodName = String.format("%1s.%1s()", ModelUtils.variable, _methodName) + private val methodName = "${ModelUtils.variable}.$_methodName(${wrapperIfUsed(hasWrapper)})" + private fun wrapperIfUsed(useWrapper: Boolean) = if (useWrapper) ModelUtils.wrapper else "" + + fun writeWrapperStatement(method: MethodSpec.Builder) { + method.statement("\$T ${ModelUtils.wrapper} = \$T.getWritableDatabaseForTable(\$T.class)", + ClassNames.DATABASE_WRAPPER, ClassNames.FLOW_MANAGER, referencedTableType) + } /** * Writes the method to the specified builder for loading from DB. @@ -125,17 +146,16 @@ class OneToManyDefinition(typeElement: ExecutableElement, val loopClass: ClassName? = if (extendsBaseModel) ClassNames.BASE_MODEL else ClassName.get(referencedType) // need to load adapter for non-model classes - if (!extendsModel) { statement("\$T adapter = \$T.getModelAdapter(\$T.class)", - ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), - ClassNames.FLOW_MANAGER, referencedTableType) + ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), + ClassNames.FLOW_MANAGER, referencedTableType) statement("adapter.${methodName}All($oneToManyMethodName\$L)", - if (useWrapper) ", " + ModelUtils.wrapper else "") + if (useWrapper) ", " + ModelUtils.wrapper else "") } else { `for`("(\$T value: $oneToManyMethodName)", loopClass) { - statement("value.$methodName(\$L)", if (useWrapper) ModelUtils.wrapper else "") + statement("value.$methodName(${wrapperIfUsed(useWrapper)})") } } }.end() diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt index 05659e775..6872ef07f 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnAccessor.kt @@ -62,19 +62,20 @@ class VisibleScopeColumnAccessor(propertyName: String) : ColumnAccessor(property val codeBlock: CodeBlock.Builder = CodeBlock.builder() baseVariableName?.let { codeBlock.add("\$L.", baseVariableName) } return codeBlock.add("\$L = \$L", propertyName, existingBlock) - .build() + .build() } override fun get(existingBlock: CodeBlock?): CodeBlock { val codeBlock: CodeBlock.Builder = CodeBlock.builder() existingBlock?.let { codeBlock.add("\$L.", existingBlock) } return codeBlock.add(propertyName) - .build() + .build() } } class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSetter? = null, - private val useIsForPrivateBooleans: Boolean = false) + private val useIsForPrivateBooleans: Boolean = false, + private val optionalGetterParam: String? = "") : ColumnAccessor(propertyName) { private var getterName: String = "" @@ -82,7 +83,7 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette override fun get(existingBlock: CodeBlock?) = code { existingBlock?.let { this.add("$existingBlock.") } - add("$getterNameElement()") + add("$getterNameElement($optionalGetterParam)") } override fun set(existingBlock: CodeBlock?, baseVariableName: CodeBlock?, @@ -128,7 +129,7 @@ class PrivateScopeColumnAccessor(propertyName: String, getterSetter: GetterSette } class PackagePrivateScopeColumnAccessor( - propertyName: String, packageName: String, separator: String?, tableClassName: String) + propertyName: String, packageName: String, separator: String?, tableClassName: String) : ColumnAccessor(propertyName) { val helperClassName: ClassName @@ -141,16 +142,16 @@ class PackagePrivateScopeColumnAccessor( override fun get(existingBlock: CodeBlock?): CodeBlock { return CodeBlock.of("\$T.get\$L(\$L)", internalHelperClassName, - propertyName.capitalizeFirstLetter(), - existingBlock) + propertyName.capitalizeFirstLetter(), + existingBlock) } override fun set(existingBlock: CodeBlock?, baseVariableName: CodeBlock?, isDefault: Boolean): CodeBlock { return CodeBlock.of("\$T.set\$L(\$L, \$L)", helperClassName, - propertyName.capitalizeFirstLetter(), - baseVariableName, - existingBlock) + propertyName.capitalizeFirstLetter(), + baseVariableName, + existingBlock) } companion object { diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt index a6c568832..8639faeb4 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt @@ -23,8 +23,9 @@ class OneToManyModelTest : BaseUnitTest() { // assert loading works as expected. oneToManyModel = (select from OneToManyModel::class).result!! - assertNotNull(oneToManyModel.getRelatedOrders()) - assertTrue(!oneToManyModel.getRelatedOrders().isEmpty()) + val wrapper = writableDatabaseForTable() + assertNotNull(oneToManyModel.getRelatedOrders(wrapper)) + assertTrue(!oneToManyModel.getRelatedOrders(wrapper).isEmpty()) // assert the deletion cleared the variable oneToManyModel.delete() diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt index 32cb3b889..d7bf4ece6 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt @@ -5,10 +5,10 @@ import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.annotation.PrimaryKey import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list import com.raizlabs.android.dbflow.kotlinextensions.select import com.raizlabs.android.dbflow.kotlinextensions.where import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id +import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper @Table(database = TestDatabase::class) class OneToManyModel(@PrimaryKey var name: String? = null) { @@ -16,11 +16,12 @@ class OneToManyModel(@PrimaryKey var name: String? = null) { var orders: List? = null @OneToMany(methods = arrayOf(OneToMany.Method.ALL), isVariablePrivate = true, - variableName = "orders") - fun getRelatedOrders(): List { + variableName = "orders") + fun getRelatedOrders(databaseWrapper: DatabaseWrapper): List { var localOrders = orders if (localOrders == null) { - localOrders = (select from TwoColumnModel::class where id.greaterThan(3)).list + localOrders = (select from TwoColumnModel::class where id.greaterThan(3)) + .queryList(databaseWrapper) } orders = localOrders return localOrders From f4bf14a53efe8e7a401e8dbb518616196806e255 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Tue, 25 Apr 2017 14:40:18 -0400 Subject: [PATCH 27/37] [model] add methods to model interface to align with basemodel. add efficient code gen option to onetomany, otherwise can do non efficient. --- .../android/dbflow/annotation/OneToMany.java | 6 ++ .../PropertyMethodExtensions.kt | 35 +++--- .../kotlinextensions/QueryExtensions.kt | 18 ++-- .../definition/OneToManyDefinition.kt | 41 ++++--- .../android/dbflow/models/OneToManyModels.kt | 2 +- .../android/dbflow/sql/BaseAsyncObject.java | 27 +++-- .../android/dbflow/structure/AsyncModel.java | 102 ++++++++++++------ .../android/dbflow/structure/BaseModel.java | 46 ++++---- .../android/dbflow/structure/Model.java | 39 +++++++ .../dbflow/structure/NoModificationModel.java | 4 +- .../dbflow/structure/ReadOnlyModel.java | 16 +++ .../structure/provider/ContentUtils.java | 29 ++--- 12 files changed, 228 insertions(+), 137 deletions(-) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java index 73fe6ab51..e200b5dff 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java +++ b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java @@ -57,4 +57,10 @@ enum Method { * a setter for it. */ boolean isVariablePrivate() default false; + + /** + * @return If true, the code generated for this relationship done as efficiently as possible. + * It will not work on nested relationships, caching, and other code that requires overriding of BaseModel or Model operations. + */ + boolean efficientMethods() default true; } diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt index be223b744..37930eea1 100644 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt +++ b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt @@ -5,7 +5,6 @@ import com.raizlabs.android.dbflow.sql.language.IConditional import com.raizlabs.android.dbflow.sql.language.Operator import com.raizlabs.android.dbflow.sql.language.Operator.Between import com.raizlabs.android.dbflow.sql.language.property.Property -import com.raizlabs.android.dbflow.structure.Model /** * Description: Provides property methods in via infix functions. @@ -93,36 +92,28 @@ infix fun IConditional.notIn(values: Array): Operator.In<*> { } } -infix fun IConditional.`is`(baseModelQueriable: BaseModelQueriable): Operator<*> = this.`is`(baseModelQueriable) +infix fun IConditional.`is`(baseModelQueriable: BaseModelQueriable): Operator<*> = this.`is`(baseModelQueriable) -infix fun IConditional.eq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.eq(baseModelQueriable) +infix fun IConditional.eq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.eq(baseModelQueriable) -infix fun IConditional.isNot(baseModelQueriable: BaseModelQueriable): Operator<*> = this.isNot(baseModelQueriable) +infix fun IConditional.isNot(baseModelQueriable: BaseModelQueriable): Operator<*> = this.isNot(baseModelQueriable) +infix fun IConditional.notEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.notEq(baseModelQueriable) +infix fun IConditional.like(baseModelQueriable: BaseModelQueriable): Operator<*> = this.like(baseModelQueriable) +infix fun IConditional.glob(baseModelQueriable: BaseModelQueriable): Operator<*> = this.glob(baseModelQueriable) +infix fun IConditional.greaterThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThan(baseModelQueriable) +infix fun IConditional.greaterThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThanOrEq(baseModelQueriable) +infix fun IConditional.lessThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThan(baseModelQueriable) +infix fun IConditional.lessThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThanOrEq(baseModelQueriable) +infix fun IConditional.between(baseModelQueriable: BaseModelQueriable): Between<*> = this.between(baseModelQueriable) -infix fun IConditional.notEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.notEq(baseModelQueriable) - -infix fun IConditional.like(baseModelQueriable: BaseModelQueriable): Operator<*> = this.like(baseModelQueriable) - -infix fun IConditional.glob(baseModelQueriable: BaseModelQueriable): Operator<*> = this.glob(baseModelQueriable) - -infix fun IConditional.greaterThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThan(baseModelQueriable) - -infix fun IConditional.greaterThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThanOrEq(baseModelQueriable) - -infix fun IConditional.lessThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThan(baseModelQueriable) - -infix fun IConditional.lessThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThanOrEq(baseModelQueriable) - -infix fun IConditional.between(baseModelQueriable: BaseModelQueriable): Between<*> = this.between(baseModelQueriable) - -infix fun IConditional.`in`(values: Array>): Operator.In<*> { +infix fun IConditional.`in`(values: Array>): Operator.In<*> { return when (values.size) { 1 -> `in`(values[0]) else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) } } -infix fun IConditional.notIn(values: Array>): Operator.In<*> { +infix fun IConditional.notIn(values: Array>): Operator.In<*> { return when (values.size) { 1 -> notIn(values[0]) else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt index 22edf7cca..fd5a43cb4 100644 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt +++ b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt @@ -9,7 +9,7 @@ import com.raizlabs.android.dbflow.sql.queriable.AsyncQuery import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable import com.raizlabs.android.dbflow.sql.queriable.Queriable import com.raizlabs.android.dbflow.structure.AsyncModel -import com.raizlabs.android.dbflow.structure.BaseModel +import com.raizlabs.android.dbflow.structure.Model import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction import kotlin.reflect.KClass @@ -100,18 +100,18 @@ inline val ModelQueriable.async get() = async() infix inline fun AsyncQuery.list(crossinline callback: (QueryTransaction<*>, MutableList) -> Unit) - = queryListResultCallback { queryTransaction, mutableList -> callback(queryTransaction, mutableList) } - .execute() + = queryListResultCallback { queryTransaction, mutableList -> callback(queryTransaction, mutableList) } + .execute() infix inline fun AsyncQuery.result(crossinline callback: (QueryTransaction<*>, T?) -> Unit) - = querySingleResultCallback { queryTransaction, model -> callback(queryTransaction, model) } - .execute() + = querySingleResultCallback { queryTransaction, model -> callback(queryTransaction, model) } + .execute() infix inline fun AsyncQuery.cursorResult(crossinline callback: (QueryTransaction<*>, CursorResult) -> Unit) - = queryResultCallback { queryTransaction, cursorResult -> callback(queryTransaction, cursorResult) } - .execute() + = queryResultCallback { queryTransaction, cursorResult -> callback(queryTransaction, cursorResult) } + .execute() -inline val BaseModel.async: AsyncModel +inline val Model.async get() = async() infix inline fun AsyncModel.insert(crossinline listener: (T) -> Unit) = withListener { listener(it) }.insert() @@ -163,7 +163,7 @@ infix fun Update.set(sqlOperator: SQLOperator) = set(sqlOperator) inline fun delete() = SQLite.delete(T::class.java) inline fun delete(deleteClause: From.() -> BaseModelQueriable) - = deleteClause(SQLite.delete(T::class.java)) + = deleteClause(SQLite.delete(T::class.java)) // insert methods diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 90c32e09a..2b1a96010 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -1,6 +1,5 @@ package com.raizlabs.android.dbflow.processor.definition -import com.google.common.collect.Lists import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.OneToMany import com.raizlabs.android.dbflow.processor.ClassNames @@ -21,12 +20,12 @@ class OneToManyDefinition(executableElement: ExecutableElement, private var _variableName: String - var methods: MutableList = Lists.newArrayList() + var methods = mutableListOf() - val isLoad: Boolean + val isLoad get() = isAll || methods.contains(OneToMany.Method.LOAD) - val isAll: Boolean + val isAll get() = methods.contains(OneToMany.Method.ALL) val isDelete: Boolean @@ -43,10 +42,14 @@ class OneToManyDefinition(executableElement: ExecutableElement, private var extendsModel: Boolean = false private var referencedType: TypeElement? = null + private var efficientCodeMethods = false + init { val oneToMany = executableElement.annotation()!! + efficientCodeMethods = oneToMany.efficientMethods + _methodName = executableElement.simpleName.toString() _variableName = oneToMany.variableName if (_variableName.isEmpty()) { @@ -99,13 +102,11 @@ class OneToManyDefinition(executableElement: ExecutableElement, } - private val methodName = "${ModelUtils.variable}.$_methodName(${wrapperIfUsed(hasWrapper)})" - - private fun wrapperIfUsed(useWrapper: Boolean) = if (useWrapper) ModelUtils.wrapper else "" + private val methodName = "${ModelUtils.variable}.$_methodName(${wrapperIfBaseModel(hasWrapper)})" fun writeWrapperStatement(method: MethodSpec.Builder) { - method.statement("\$T ${ModelUtils.wrapper} = \$T.getWritableDatabaseForTable(\$T.class)", - ClassNames.DATABASE_WRAPPER, ClassNames.FLOW_MANAGER, referencedTableType) + method.statement("\$T ${ModelUtils.wrapper} = \$T.getWritableDatabaseForTable(\$T.class)", + ClassNames.DATABASE_WRAPPER, ClassNames.FLOW_MANAGER, referencedTableType) } /** @@ -142,7 +143,7 @@ class OneToManyDefinition(executableElement: ExecutableElement, private fun writeLoopWithMethod(codeBuilder: MethodSpec.Builder, methodName: String, useWrapper: Boolean) { val oneToManyMethodName = this@OneToManyDefinition.methodName codeBuilder.apply { - `if`("($oneToManyMethodName != null)") { + `if`("$oneToManyMethodName != null") { val loopClass: ClassName? = if (extendsBaseModel) ClassNames.BASE_MODEL else ClassName.get(referencedType) // need to load adapter for non-model classes @@ -150,16 +151,22 @@ class OneToManyDefinition(executableElement: ExecutableElement, statement("\$T adapter = \$T.getModelAdapter(\$T.class)", ParameterizedTypeName.get(ClassNames.MODEL_ADAPTER, referencedTableType), ClassNames.FLOW_MANAGER, referencedTableType) + } - statement("adapter.${methodName}All($oneToManyMethodName\$L)", - if (useWrapper) ", " + ModelUtils.wrapper else "") + if (efficientCodeMethods) { + statement("adapter.${methodName}All($oneToManyMethodName${wrapperCommaIfBaseModel(useWrapper)})") } else { - `for`("(\$T value: $oneToManyMethodName)", loopClass) { - statement("value.$methodName(${wrapperIfUsed(useWrapper)})") + `for`("\$T value: $oneToManyMethodName", loopClass) { + if (!extendsModel) { + statement("adapter.$methodName(value${wrapperCommaIfBaseModel(useWrapper)})") + } else { + statement("value.$methodName(${wrapperIfBaseModel(useWrapper)})") + } + this } } - }.end() - } + } + }.end() } - } + diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt index d7bf4ece6..a34188b1b 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt @@ -16,7 +16,7 @@ class OneToManyModel(@PrimaryKey var name: String? = null) { var orders: List? = null @OneToMany(methods = arrayOf(OneToMany.Method.ALL), isVariablePrivate = true, - variableName = "orders") + variableName = "orders", efficientMethods = true) fun getRelatedOrders(databaseWrapper: DatabaseWrapper): List { var localOrders = orders if (localOrders == null) { diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java index f7d2298ee..098e310bd 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java @@ -1,5 +1,7 @@ package com.raizlabs.android.dbflow.sql; +import android.support.annotation.NonNull; + import com.raizlabs.android.dbflow.config.DatabaseDefinition; import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction; @@ -13,10 +15,17 @@ public class BaseAsyncObject { private Transaction.Success successCallback; private Transaction.Error errorCallback; private Transaction currentTransaction; + + private final Class table; private final DatabaseDefinition databaseDefinition; - public BaseAsyncObject(Class modelClass) { - databaseDefinition = FlowManager.getDatabaseForTable(modelClass); + public BaseAsyncObject(Class table) { + this.table = table; + databaseDefinition = FlowManager.getDatabaseForTable(table); + } + + public Class getTable() { + return table; } /** @@ -46,21 +55,21 @@ public void cancel() { } } - protected void executeTransaction(ITransaction transaction) { + protected void executeTransaction(@NonNull ITransaction transaction) { cancel(); currentTransaction = databaseDefinition - .beginTransactionAsync(transaction) - .error(error) - .success(success) - .build(); + .beginTransactionAsync(transaction) + .error(error) + .success(success) + .build(); currentTransaction.execute(); } - protected void onError(Transaction transaction, Throwable error) { + protected void onError(@NonNull Transaction transaction, Throwable error) { } - protected void onSuccess(Transaction transaction) { + protected void onSuccess(@NonNull Transaction transaction) { } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java index 40d819703..f2d15e5bd 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java @@ -55,63 +55,93 @@ private ModelAdapter getModelAdapter() { return modelAdapter; } + @Override + public boolean save(@NonNull DatabaseWrapper wrapper) { + return save(); + } + @Override public boolean save() { executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().save(model, wrapper); - } - }).add(model).build()); + new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(TModel model, DatabaseWrapper wrapper) { + getModelAdapter().save(model, wrapper); + } + }).add(model).build()); return false; } + @Override + public boolean delete(@NonNull DatabaseWrapper wrapper) { + return delete(); + } + @Override public boolean delete() { executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().delete(model, wrapper); - } - }).add(model).build()); + new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(TModel model, DatabaseWrapper wrapper) { + getModelAdapter().delete(model, wrapper); + } + }).add(model).build()); return false; } + @Override + public boolean update(@NonNull DatabaseWrapper wrapper) { + return update(); + } + @Override public boolean update() { executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().update(model, wrapper); - } - }).add(model).build()); + new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(TModel model, DatabaseWrapper wrapper) { + getModelAdapter().update(model, wrapper); + } + }).add(model).build()); return false; } + @Override + public long insert(DatabaseWrapper wrapper) { + return insert(); + } + @Override public long insert() { executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().insert(model, wrapper); - } - }).add(model).build()); + new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(TModel model, DatabaseWrapper wrapper) { + getModelAdapter().insert(model, wrapper); + } + }).add(model).build()); return INVALID_ROW_ID; } + @Override + public void load(DatabaseWrapper wrapper) { + load(); + } + @Override public void load() { executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().load(model, wrapper); - } - }).add(model).build()); + new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(TModel model, DatabaseWrapper wrapper) { + getModelAdapter().load(model, wrapper); + } + }).add(model).build()); + } + + @Override + public boolean exists(DatabaseWrapper wrapper) { + return exists(); } @Override @@ -119,8 +149,18 @@ public boolean exists() { return getModelAdapter().exists(model); } + /** + * @return Itself since it's already async. + */ + @NonNull + @Override + public AsyncModel async() { + //noinspection unchecked + return (AsyncModel) this; + } + @Override - protected void onSuccess(Transaction transaction) { + protected void onSuccess(@NonNull Transaction transaction) { if (onModelChangedListener != null && onModelChangedListener.get() != null) { onModelChangedListener.get().onModelChanged(model); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java index 411658f80..6bbbe80a5 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java @@ -1,14 +1,16 @@ package com.raizlabs.android.dbflow.structure; +import android.support.annotation.NonNull; + import com.raizlabs.android.dbflow.annotation.ColumnIgnore; import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; /** * Description: The base implementation of {@link Model}. It is recommended to use this class as * the base for your {@link Model}, but it is not required. */ +@SuppressWarnings("unchecked") public class BaseModel implements Model { /** @@ -45,76 +47,70 @@ public enum Action { @ColumnIgnore private transient ModelAdapter modelAdapter; - @SuppressWarnings("unchecked") @Override public void load() { getModelAdapter().load(this); } - @SuppressWarnings("unchecked") - public void load(DatabaseWrapper databaseWrapper) { - getModelAdapter().load(this, databaseWrapper); + @Override + public void load(DatabaseWrapper wrapper) { + getModelAdapter().load(this, wrapper); } - @SuppressWarnings("unchecked") @Override public boolean save() { return getModelAdapter().save(this); } - @SuppressWarnings("unchecked") - public boolean save(DatabaseWrapper databaseWrapper) { + + @Override + public boolean save(@NonNull DatabaseWrapper databaseWrapper) { return getModelAdapter().save(this, databaseWrapper); } - @SuppressWarnings("unchecked") @Override public boolean delete() { return getModelAdapter().delete(this); } - @SuppressWarnings("unchecked") - public boolean delete(DatabaseWrapper databaseWrapper) { + @Override + public boolean delete(@NonNull DatabaseWrapper databaseWrapper) { return getModelAdapter().delete(this, databaseWrapper); } - @SuppressWarnings("unchecked") @Override public boolean update() { return getModelAdapter().update(this); } - @SuppressWarnings("unchecked") - public void update(DatabaseWrapper databaseWrapper) { - getModelAdapter().update(this, databaseWrapper); + @Override + public boolean update(@NonNull DatabaseWrapper databaseWrapper) { + return getModelAdapter().update(this, databaseWrapper); } - @SuppressWarnings("unchecked") @Override public long insert() { return getModelAdapter().insert(this); } - @SuppressWarnings("unchecked") - public void insert(DatabaseWrapper databaseWrapper) { - getModelAdapter().insert(this, databaseWrapper); + @Override + public long insert(DatabaseWrapper databaseWrapper) { + return getModelAdapter().insert(this, databaseWrapper); } - @SuppressWarnings("unchecked") @Override public boolean exists() { return getModelAdapter().exists(this); } - @SuppressWarnings("unchecked") + @Override public boolean exists(DatabaseWrapper databaseWrapper) { return getModelAdapter().exists(this, databaseWrapper); } - /** - * @return An async instance of this model where all transactions are on the {@link DefaultTransactionQueue} - */ - public AsyncModel async() { + @NonNull + @Override + public AsyncModel async() { return new AsyncModel<>(this); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java index f52f54185..12ea1a6f3 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java @@ -1,5 +1,10 @@ package com.raizlabs.android.dbflow.structure; +import android.support.annotation.NonNull; + +import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; + public interface Model extends ReadOnlyModel { /** @@ -14,6 +19,13 @@ public interface Model extends ReadOnlyModel { */ boolean save(); + /** + * Saves the object in the DB. + * + * @return true if successful + */ + boolean save(@NonNull DatabaseWrapper wrapper); + /** * Deletes the object in the DB * @@ -21,6 +33,13 @@ public interface Model extends ReadOnlyModel { */ boolean delete(); + /** + * Deletes the object in the DB + * + * @return true if successful + */ + boolean delete(@NonNull DatabaseWrapper wrapper); + /** * Updates an object in the DB. Does not insert on failure. * @@ -28,6 +47,13 @@ public interface Model extends ReadOnlyModel { */ boolean update(); + /** + * Updates an object in the DB. Does not insert on failure. + * + * @return true if successful + */ + boolean update(@NonNull DatabaseWrapper wrapper); + /** * Inserts the object into the DB * @@ -35,4 +61,17 @@ public interface Model extends ReadOnlyModel { */ long insert(); + /** + * Inserts the object into the DB + * + * @return the count of the rows affected, should only be 1 here, or -1 if failed. + */ + long insert(DatabaseWrapper wrapper); + + /** + * @return An async instance of this model where all transactions are on the {@link DefaultTransactionQueue} + */ + @NonNull + AsyncModel async(); + } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java index 474fef898..eb6016224 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java @@ -26,8 +26,8 @@ public void load() { } @SuppressWarnings("unchecked") - public void load(DatabaseWrapper databaseWrapper) { - getRetrievalAdapter().load(this, databaseWrapper); + public void load(DatabaseWrapper wrapper) { + getRetrievalAdapter().load(this, wrapper); } public RetrievalAdapter getRetrievalAdapter() { diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java index 7e872ed6f..01d2c3c6d 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java @@ -1,5 +1,8 @@ package com.raizlabs.android.dbflow.structure; +import com.raizlabs.android.dbflow.sql.migration.Migration; +import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; + public interface ReadOnlyModel { /** @@ -7,10 +10,23 @@ public interface ReadOnlyModel { */ void load(); + /** + * Loads from the database the most recent version of the model based on it's primary keys. + * + * @param wrapper Database object to use. Useful for {@link Migration} classes. + */ + void load(DatabaseWrapper wrapper); + /** * @return true if this object exists in the DB. It combines all of it's primary key fields * into a SELECT query and checks to see if any results occur. */ boolean exists(); + /** + * @return true if this object exists in the DB. It combines all of it's primary key fields + * into a SELECT query and checks to see if any results occur. + * @param wrapper Database object to use. Useful for {@link Migration} classes. + */ + boolean exists(DatabaseWrapper wrapper); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java index cdf599fa1..31902791e 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java @@ -10,7 +10,6 @@ import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.sql.language.Operator; import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.structure.Model; import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.raizlabs.android.dbflow.structure.database.FlowCursor; @@ -58,9 +57,8 @@ public static Uri buildUri(String baseContentUri, String authority, String... pa * Inserts the model into the {@link android.content.ContentResolver}. Uses the insertUri to resolve * the reference and the model to convert its data into {@link android.content.ContentValues} * - * @param insertUri A {@link android.net.Uri} from the {@link ContentProvider} class definition. - * @param model The model to insert. - * @param The class that implements {@link Model} + * @param insertUri A {@link android.net.Uri} from the {@link ContentProvider} class definition. + * @param model The model to insert. * @return A Uri of the inserted data. */ public static Uri insert(Uri insertUri, TableClass model) { @@ -74,7 +72,6 @@ public static Uri insert(Uri insertUri, TableClass model) { * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) * @param insertUri A {@link android.net.Uri} from the {@link ContentProvider} class definition. * @param model The model to insert. - * @param The class that implements {@link Model} * @return The Uri of the inserted data. */ @SuppressWarnings("unchecked") @@ -98,7 +95,6 @@ public static Uri insert(ContentResolver contentResolver, Uri inser * @param bulkInsertUri The URI to bulk insert with * @param table The table to insert into * @param models The models to insert. - * @param The class that implements {@link Model} * @return The count of the rows affected by the insert. */ public static int bulkInsert(ContentResolver contentResolver, Uri bulkInsertUri, @@ -124,7 +120,6 @@ public static int bulkInsert(ContentResolver contentResolver, Uri b * @param bulkInsertUri The URI to bulk insert with * @param table The table to insert into * @param models The models to insert. - * @param The class that implements {@link Model} * @return The count of the rows affected by the insert. */ public static int bulkInsert(Uri bulkInsertUri, Class table, List models) { @@ -135,9 +130,8 @@ public static int bulkInsert(Uri bulkInsertUri, Class t * Updates the model through the {@link android.content.ContentResolver}. Uses the updateUri to * resolve the reference and the model to convert its data in {@link android.content.ContentValues} * - * @param updateUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model A model to update - * @param The class that implements {@link Model} + * @param updateUri A {@link android.net.Uri} from the {@link ContentProvider} + * @param model A model to update * @return The number of rows updated. */ public static int update(Uri updateUri, TableClass model) { @@ -151,7 +145,6 @@ public static int update(Uri updateUri, TableClass model) { * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) * @param updateUri A {@link android.net.Uri} from the {@link ContentProvider} * @param model The model to update - * @param The class that implements {@link Model} * @return The number of rows updated. */ @SuppressWarnings("unchecked") @@ -169,11 +162,10 @@ public static int update(ContentResolver contentResolver, Uri updat /** * Deletes the specified model through the {@link android.content.ContentResolver}. Uses the deleteUri - * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Model)} + * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Object)}} * - * @param deleteUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model The model to delete - * @param The class that implements {@link Model} + * @param deleteUri A {@link android.net.Uri} from the {@link ContentProvider} + * @param model The model to delete * @return The number of rows deleted. */ @SuppressWarnings("unchecked") @@ -183,12 +175,11 @@ public static int delete(Uri deleteUri, TableClass model) { /** * Deletes the specified model through the {@link android.content.ContentResolver}. Uses the deleteUri - * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Model)} + * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Object)} * * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) * @param deleteUri A {@link android.net.Uri} from the {@link ContentProvider} * @param model The model to delete - * @param The class that implements {@link Model} * @return The number of rows deleted. */ @SuppressWarnings("unchecked") @@ -232,7 +223,6 @@ public static Cursor query(ContentResolver contentResolver, Uri queryUri, * @param whereConditions The set of {@link Operator} to query the content provider. * @param orderBy The order by clause without the ORDER BY * @param columns The list of columns to query. - * @param The class that implements {@link Model} * @return A list of {@link TableClass} */ public static List queryList(Uri queryUri, Class table, @@ -252,7 +242,6 @@ public static List queryList(Uri queryUri, Class The class that implements {@link Model} * @return A list of {@link TableClass} */ public static List queryList(ContentResolver contentResolver, Uri queryUri, @@ -277,7 +266,6 @@ public static List queryList(ContentResolver contentRes * @param whereConditions The set of {@link Operator} to query the content provider. * @param orderBy The order by clause without the ORDER BY * @param columns The list of columns to query. - * @param The class that implements {@link Model} * @return The first {@link TableClass} of the list query from the content provider. */ public static TableClass querySingle(Uri queryUri, Class table, @@ -296,7 +284,6 @@ public static TableClass querySingle(Uri queryUri, Class The class that implements {@link Model} * @return The first {@link TableClass} of the list query from the content provider. */ public static TableClass querySingle(ContentResolver contentResolver, Uri queryUri, Class table, From 9c261774c8fbfdcae396225b85c3bffa9522e450 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Tue, 25 Apr 2017 15:11:38 -0400 Subject: [PATCH 28/37] [migrations] fix null passed for operators. Dont include them if theyre null. --- dbflow-tests/build.gradle | 3 +-- .../sql/migration/UpdateTableMigrationTest.kt | 22 +++++++++++++++++++ .../sql/language/BaseTransformable.java | 2 +- .../dbflow/sql/language/OperatorGroup.java | 11 ++++++---- .../sql/migration/UpdateTableMigration.java | 7 ++++-- 5 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt diff --git a/dbflow-tests/build.gradle b/dbflow-tests/build.gradle index 77d4f829a..9a281dd3e 100644 --- a/dbflow-tests/build.gradle +++ b/dbflow-tests/build.gradle @@ -41,7 +41,7 @@ dependencies { kapt project("${dbflow_project_prefix}dbflow-processor") compile project(':dbflow') - compile 'com.android.support:appcompat-v7:25.3.0' + compile 'com.android.support:appcompat-v7:25.3.1' compile project("${dbflow_project_prefix}dbflow") compile project("${dbflow_project_prefix}dbflow-sqlcipher") compile project("${dbflow_project_prefix}dbflow-kotlinextensions") @@ -53,7 +53,6 @@ dependencies { kaptTest project("${dbflow_project_prefix}dbflow-processor") testCompile 'junit:junit:4.12' - testCompile 'com.jayway.awaitility:awaitility:1.6.5' testCompile "org.robolectric:robolectric:3.1.4" testCompile("com.nhaarman:mockito-kotlin:1.3.0") { exclude group: "org.jetbrains.kotlin" diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt new file mode 100644 index 000000000..767d35a84 --- /dev/null +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt @@ -0,0 +1,22 @@ +package com.raizlabs.android.dbflow.sql.migration + +import com.raizlabs.android.dbflow.BaseUnitTest +import com.raizlabs.android.dbflow.kotlinextensions.writableDatabaseForTable +import com.raizlabs.android.dbflow.models.SimpleModel +import com.raizlabs.android.dbflow.models.SimpleModel_Table +import org.junit.Test + +/** + * Description: + */ + +class UpdateTableMigrationTest : BaseUnitTest() { + + + @Test + fun testUpdateMigrationQuery() { + val update = UpdateTableMigration(SimpleModel::class.java) + update.set(SimpleModel_Table.name.eq("yes")) + update.migrate(writableDatabaseForTable()) + } +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java index 39778fbef..1bb0f2b7a 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java @@ -24,7 +24,7 @@ protected BaseTransformable(Class table) { } @NonNull - public Where where(SQLOperator... conditions) { + public Where where(@NonNull SQLOperator... conditions) { return new Where<>(this, conditions); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java index c233b2087..7457c105c 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java @@ -1,6 +1,7 @@ package com.raizlabs.android.dbflow.sql.language; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.sql.Query; import com.raizlabs.android.dbflow.sql.QueryBuilder; @@ -164,10 +165,12 @@ public OperatorGroup orAll(Collection sqlOperators) { * Appends the {@link SQLOperator} with the specified operator string. */ @NonNull - private OperatorGroup operator(String operator, SQLOperator sqlOperator) { - setPreviousSeparator(operator); - conditionsList.add(sqlOperator); - isChanged = true; + private OperatorGroup operator(String operator, @Nullable SQLOperator sqlOperator) { + if (sqlOperator != null) { + setPreviousSeparator(operator); + conditionsList.add(sqlOperator); + isChanged = true; + } return this; } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java index 33be7377a..6aa328552 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java @@ -1,6 +1,7 @@ package com.raizlabs.android.dbflow.sql.migration; import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; import com.raizlabs.android.dbflow.sql.language.BaseQueriable; import com.raizlabs.android.dbflow.sql.language.OperatorGroup; @@ -23,11 +24,13 @@ public class UpdateTableMigration extends BaseMigration { /** * Builds the conditions for the WHERE part of our query */ + @Nullable private OperatorGroup whereOperatorGroup; /** * The conditions to use to set fields in the update query */ + @Nullable private OperatorGroup setOperatorGroup; /** @@ -78,8 +81,8 @@ public void onPostMigrate() { public BaseQueriable getUpdateStatement() { return SQLite.update(table) - .set(setOperatorGroup) - .where(whereOperatorGroup); + .set(setOperatorGroup) + .where(whereOperatorGroup); } } From 361dae1300583121ee68f2b046ef799219157437 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Tue, 25 Apr 2017 15:35:57 -0400 Subject: [PATCH 29/37] [foreign key] add ability to specify a foreign key as deferred. --- .../com/raizlabs/android/dbflow/annotation/ForeignKey.java | 7 +++++++ .../android/dbflow/processor/definition/Methods.kt | 3 +++ .../definition/column/ForeignKeyColumnDefinition.kt | 3 +++ .../com/raizlabs/android/dbflow/models/ForeignKeyModels.kt | 7 +++++++ 4 files changed, 20 insertions(+) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java index 8c96ca9c3..fb82d308c 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java +++ b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java @@ -43,6 +43,13 @@ */ boolean stubbedRelationship() default false; + /** + * @return If true, during a transaction, FK constraints are not violated immediately until the resulting transaction commits. + * This is useful for out of order foreign key operations. + * @see Deferred Foreign Key Constraints + */ + boolean deferred() default false; + /** * @return an optional table class that this reference points to. It's only used if the field * is NOT a Model class. diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt index 9f9fd974a..78d303808 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/Methods.kt @@ -195,6 +195,9 @@ class CreationQueryMethod(private val tableDefinition: TableDefinition) : Method add("(") add(fk._foreignKeyReferenceDefinitionList.joinToString { QueryBuilder.quote(it.foreignColumnName) }) add(") ON UPDATE ${fk.onUpdate.name.replace("_", " ")} ON DELETE ${fk.onDelete.name.replace("_", " ")}") + if (fk.deferred) { + add(" DEFERRABLE INITIALLY DEFERRED") + } }.build()) } diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt index 76b0c84aa..7b342c4db 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ForeignKeyColumnDefinition.kt @@ -48,6 +48,8 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab var needsReferences = true + var deferred = false + override val typeConverterElementNames: List get() = _foreignKeyReferenceDefinitionList.filter { it.hasTypeConverter }.map { it.columnClassName } @@ -57,6 +59,7 @@ class ForeignKeyColumnDefinition(manager: ProcessorManager, tableDefinition: Tab onUpdate = foreignKey.onUpdate onDelete = foreignKey.onDelete + deferred = foreignKey.deferred isStubbedRelationship = foreignKey.stubbedRelationship try { diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ForeignKeyModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ForeignKeyModels.kt index 193f37484..641f0f36d 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ForeignKeyModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ForeignKeyModels.kt @@ -18,6 +18,13 @@ class Author(@PrimaryKey(autoincrement = true) var id: Int = 0, @Column(name = "first_name") var firstName: String = "", @Column(name = "last_name") var lastName: String = "") +/** + * Example of simple foreign key object with its [ForeignKey] deferred. + */ +@Table(database = TestDatabase::class) +class BlogDeferred(@PrimaryKey(autoincrement = true) var id: Int = 0, @Column var name: String = "", + @ForeignKey(deferred = true) var author: Author? = null) + /** * Class has example of single foreign key with [ForeignKeyReference] specified */ From c78cffa8ba6f2c608d39d9f6b4bad5358c452561 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Tue, 25 Apr 2017 15:52:39 -0400 Subject: [PATCH 30/37] [migrations] consolidate the method for adding migrations. Simplify generated code. --- .../definition/DatabaseDefinition.kt | 22 +++++++++---------- .../raizlabs/android/dbflow/TestDatabase.kt | 12 +++++++++- .../dbflow/config/DatabaseDefinition.java | 19 +++++++++++----- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt index c1bb9d61b..7f305cbe4 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/DatabaseDefinition.kt @@ -3,7 +3,10 @@ package com.raizlabs.android.dbflow.processor.definition import com.grosner.kpoet.* import com.raizlabs.android.dbflow.annotation.ConflictAction import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.processor.* +import com.raizlabs.android.dbflow.processor.ClassNames +import com.raizlabs.android.dbflow.processor.ModelViewValidator +import com.raizlabs.android.dbflow.processor.ProcessorManager +import com.raizlabs.android.dbflow.processor.TableValidator import com.raizlabs.android.dbflow.processor.utils.`override fun` import com.raizlabs.android.dbflow.processor.utils.annotation import com.raizlabs.android.dbflow.processor.utils.isNullOrEmpty @@ -52,7 +55,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi } if (!isValidDatabaseName(databaseName)) { throw Error("Database name [ " + databaseName + " ] is not valid. It must pass [A-Za-z_$]+[a-zA-Z0-9_$]* " + - "regex so it can't start with a number or contain any special character except '$'. Especially a dot character is not allowed!") + "regex so it can't start with a number or contain any special character except '$'. Especially a dot character is not allowed!") } consistencyChecksEnabled = database.consistencyCheckEnabled @@ -89,15 +92,15 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi val map = HashMap() val tableValidator = TableValidator() manager.getTableDefinitions(it) - .filter { tableValidator.validate(ProcessorManager.manager, it) } - .forEach { it.elementClassName?.let { className -> map.put(className, it) } } + .filter { tableValidator.validate(ProcessorManager.manager, it) } + .forEach { it.elementClassName?.let { className -> map.put(className, it) } } manager.setTableDefinitions(map, it) val modelViewDefinitionMap = HashMap() val modelViewValidator = ModelViewValidator() manager.getModelViewDefinitions(it) - .filter { modelViewValidator.validate(ProcessorManager.manager, it) } - .forEach { it.elementClassName?.let { className -> modelViewDefinitionMap.put(className, it) } } + .filter { modelViewValidator.validate(ProcessorManager.manager, it) } + .forEach { it.elementClassName?.let { className -> modelViewDefinitionMap.put(className, it) } } manager.setModelViewDefinitions(modelViewDefinitionMap, it) } } @@ -147,11 +150,8 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi val migrationDefinitions = migrationDefinitionMap[version] migrationDefinitions?.let { Collections.sort(migrationDefinitions, { o1, o2 -> Integer.valueOf(o2.priority)!!.compareTo(o1.priority) }) - statement("\$T migrations\$L = new \$T()", ParameterizedTypeName.get(ClassName.get(List::class.java), ClassNames.MIGRATION), - version, ParameterizedTypeName.get(ClassName.get(ArrayList::class.java), ClassNames.MIGRATION)) - statement("${DatabaseHandler.MIGRATION_FIELD_NAME}.put($version, migrations$version)") for (migrationDefinition in migrationDefinitions) { - statement("migrations$version.add(new \$T${migrationDefinition.constructorName})", migrationDefinition.elementClassName) + statement("addMigration($version, new \$T${migrationDefinition.constructorName})", migrationDefinition.elementClassName) } } } @@ -165,7 +165,7 @@ class DatabaseDefinition(manager: ProcessorManager, element: Element) : BaseDefi private fun writeGetters(typeBuilder: TypeSpec.Builder) { typeBuilder.apply { `override fun`(ParameterizedTypeName.get(ClassName.get(Class::class.java), WildcardTypeName.subtypeOf(Any::class.java)), - "getAssociatedDatabaseClassFile") { + "getAssociatedDatabaseClassFile") { modifiers(public, final) `return`("\$T.class", elementTypeName) } diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt index 505f6b72c..78fe6a715 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt @@ -1,6 +1,9 @@ package com.raizlabs.android.dbflow import com.raizlabs.android.dbflow.annotation.Database +import com.raizlabs.android.dbflow.annotation.Migration +import com.raizlabs.android.dbflow.models.SimpleModel +import com.raizlabs.android.dbflow.sql.migration.UpdateTableMigration /** * Description: @@ -10,6 +13,13 @@ object TestDatabase { const val VERSION = 1 - const val NAME = "TestDatabase"; + const val NAME = "TestDatabase" + + @Migration(version = 1, database = TestDatabase::class) + class TestMigration : UpdateTableMigration(SimpleModel::class.java) { + override fun onPostMigrate() { + super.onPostMigrate() + } + } } \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java index a45264220..3a82771f0 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java @@ -36,15 +36,15 @@ */ public abstract class DatabaseDefinition { - final Map> migrationMap = new HashMap<>(); + private final Map> migrationMap = new HashMap<>(); - final Map, ModelAdapter> modelAdapters = new HashMap<>(); + private final Map, ModelAdapter> modelAdapters = new HashMap<>(); - final Map> modelTableNames = new HashMap<>(); + private final Map> modelTableNames = new HashMap<>(); - final Map, ModelViewAdapter> modelViewAdapterMap = new LinkedHashMap<>(); + private final Map, ModelViewAdapter> modelViewAdapterMap = new LinkedHashMap<>(); - final Map, QueryModelAdapter> queryModelAdapterMap = new LinkedHashMap<>(); + private final Map, QueryModelAdapter> queryModelAdapterMap = new LinkedHashMap<>(); /** * The helper that manages database changes and initialization @@ -119,6 +119,15 @@ protected void addQueryModelAdapter(QueryModelAdapter queryModelAdapter, queryModelAdapterMap.put(queryModelAdapter.getModelClass(), queryModelAdapter); } + protected void addMigration(int version, Migration migration) { + List list = migrationMap.get(version); + if (list == null) { + list = new ArrayList<>(); + migrationMap.put(version, list); + } + list.add(migration); + } + /** * @return a list of all model classes in this database. */ From 1207e156cbbe34a26b3b4b8c73ac0e50e3b42867 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Tue, 25 Apr 2017 17:41:02 -0400 Subject: [PATCH 31/37] [onetomany] move code gen to use Model instead of BaseModel due to interface change. --- .../processor/definition/OneToManyDefinition.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt index 2b1a96010..6a8c702b5 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/OneToManyDefinition.kt @@ -38,8 +38,7 @@ class OneToManyDefinition(executableElement: ExecutableElement, var hasWrapper = false private var columnAccessor: ColumnAccessor - private var extendsBaseModel: Boolean = false - private var extendsModel: Boolean = false + private var extendsModel = false private var referencedType: TypeElement? = null private var efficientCodeMethods = false @@ -82,7 +81,6 @@ class OneToManyDefinition(executableElement: ExecutableElement, columnAccessor = VisibleScopeColumnAccessor(_variableName) } - extendsBaseModel = false val returnType = executableElement.returnType val typeName = TypeName.get(returnType) if (typeName is ParameterizedTypeName) { @@ -95,7 +93,6 @@ class OneToManyDefinition(executableElement: ExecutableElement, referencedTableType = refTableType referencedType = referencedTableType.toTypeElement(manager) - extendsBaseModel = referencedType.isSubclass(manager.processingEnvironment, ClassNames.BASE_MODEL) extendsModel = referencedType.isSubclass(manager.processingEnvironment, ClassNames.MODEL) } } @@ -123,29 +120,27 @@ class OneToManyDefinition(executableElement: ExecutableElement, */ fun writeDelete(method: MethodSpec.Builder, useWrapper: Boolean) { if (isDelete) { - writeLoopWithMethod(method, "delete", useWrapper && extendsBaseModel) + writeLoopWithMethod(method, "delete", useWrapper) method.statement(columnAccessor.set(CodeBlock.of("null"), modelBlock)) } } fun writeSave(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { - if (isSave) writeLoopWithMethod(codeBuilder, "save", useWrapper && extendsBaseModel) + if (isSave) writeLoopWithMethod(codeBuilder, "save", useWrapper) } fun writeUpdate(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { - if (isSave) writeLoopWithMethod(codeBuilder, "update", useWrapper && extendsBaseModel) + if (isSave) writeLoopWithMethod(codeBuilder, "update", useWrapper) } fun writeInsert(codeBuilder: MethodSpec.Builder, useWrapper: Boolean) { - if (isSave) writeLoopWithMethod(codeBuilder, "insert", useWrapper && (extendsBaseModel || !extendsModel)) + if (isSave) writeLoopWithMethod(codeBuilder, "insert", useWrapper) } private fun writeLoopWithMethod(codeBuilder: MethodSpec.Builder, methodName: String, useWrapper: Boolean) { val oneToManyMethodName = this@OneToManyDefinition.methodName codeBuilder.apply { `if`("$oneToManyMethodName != null") { - val loopClass: ClassName? = if (extendsBaseModel) ClassNames.BASE_MODEL else ClassName.get(referencedType) - // need to load adapter for non-model classes if (!extendsModel) { statement("\$T adapter = \$T.getModelAdapter(\$T.class)", @@ -156,7 +151,7 @@ class OneToManyDefinition(executableElement: ExecutableElement, if (efficientCodeMethods) { statement("adapter.${methodName}All($oneToManyMethodName${wrapperCommaIfBaseModel(useWrapper)})") } else { - `for`("\$T value: $oneToManyMethodName", loopClass) { + `for`("\$T value: $oneToManyMethodName", ClassName.get(referencedType)) { if (!extendsModel) { statement("adapter.$methodName(value${wrapperCommaIfBaseModel(useWrapper)})") } else { From 67d0f98e04db30b199cd93c30501f480a6a6928d Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Wed, 26 Apr 2017 14:52:57 -0400 Subject: [PATCH 32/37] [documentation] make usage 2 a submodule and gitbook project. --- .gitmodules | 3 +++ build.gradle | 2 +- .../raizlabs/android/dbflow/models/OneToManyModelTest.kt | 4 ++-- .../com/raizlabs/android/dbflow/models/OneToManyModels.kt | 7 +++---- .../com/raizlabs/android/dbflow/sql/language/FromTest.kt | 3 ++- 5 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c2e9a3e4a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "usage2"] + path = usage2 + url = https://git.gitbook.com/agrosner/dbflow.git diff --git a/build.gradle b/build.gradle index 0c784ed80..07002b362 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.1.1' + ext.kotlin_version = '1.1.2' repositories { jcenter() } diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt index 8639faeb4..338cf044b 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt @@ -24,8 +24,8 @@ class OneToManyModelTest : BaseUnitTest() { // assert loading works as expected. oneToManyModel = (select from OneToManyModel::class).result!! val wrapper = writableDatabaseForTable() - assertNotNull(oneToManyModel.getRelatedOrders(wrapper)) - assertTrue(!oneToManyModel.getRelatedOrders(wrapper).isEmpty()) + assertNotNull(oneToManyModel.getRelatedOrders()) + assertTrue(!oneToManyModel.getRelatedOrders().isEmpty()) // assert the deletion cleared the variable oneToManyModel.delete() diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt index a34188b1b..3c7374f75 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt @@ -8,7 +8,6 @@ import com.raizlabs.android.dbflow.kotlinextensions.from import com.raizlabs.android.dbflow.kotlinextensions.select import com.raizlabs.android.dbflow.kotlinextensions.where import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper @Table(database = TestDatabase::class) class OneToManyModel(@PrimaryKey var name: String? = null) { @@ -16,12 +15,12 @@ class OneToManyModel(@PrimaryKey var name: String? = null) { var orders: List? = null @OneToMany(methods = arrayOf(OneToMany.Method.ALL), isVariablePrivate = true, - variableName = "orders", efficientMethods = true) - fun getRelatedOrders(databaseWrapper: DatabaseWrapper): List { + variableName = "orders", efficientMethods = false) + fun getRelatedOrders(): List { var localOrders = orders if (localOrders == null) { localOrders = (select from TwoColumnModel::class where id.greaterThan(3)) - .queryList(databaseWrapper) + .queryList() } orders = localOrders return localOrders diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/FromTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/FromTest.kt index c469c20b1..95cc8d7ec 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/FromTest.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/FromTest.kt @@ -1,12 +1,13 @@ package com.raizlabs.android.dbflow.sql.language import com.raizlabs.android.dbflow.BaseUnitTest +import com.raizlabs.android.dbflow.contentobserver.User +import com.raizlabs.android.dbflow.kotlinextensions.* import com.raizlabs.android.dbflow.models.SimpleModel import com.raizlabs.android.dbflow.models.SimpleModel_Table.name import com.raizlabs.android.dbflow.models.TwoColumnModel import com.raizlabs.android.dbflow.models.TwoColumnModel_Table import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.kotlinextensions.* import com.raizlabs.android.dbflow.sql.language.SQLite.select import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue From f7c06dc1a9ebd31cb9f940330c86a6660b76d1b1 Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Wed, 26 Apr 2017 14:53:41 -0400 Subject: [PATCH 33/37] [doc] mode usage to gitbook. --- usage2 | 1 + usage2/Caching.md | 143 ------- usage2/ContentProviderGeneration.md | 234 ----------- usage2/Databases.md | 131 ------ usage2/GettingStarted.md | 154 ------- usage2/Indexing.md | 78 ---- usage2/Intro.md | 126 ------ usage2/KotlinSupport.md | 231 ---------- usage2/ListBasedQueries.md | 157 ------- usage2/Migration3Guide.md | 630 ---------------------------- usage2/Migration4Guide.md | 40 -- usage2/Migrations.md | 175 -------- usage2/ModelViews.md | 47 --- usage2/Models.md | 163 ------- usage2/MultipleModules.md | 48 --- usage2/Observability.md | 154 ------- usage2/QueryModels.md | 107 ----- usage2/RXSupport.md | 170 -------- usage2/Relationships.md | 227 ---------- usage2/Retrieval.md | 116 ----- usage2/SQLCipherSupport.md | 57 --- usage2/SQLiteWrapperLanguage.md | 384 ----------------- usage2/StoringData.md | 313 -------------- usage2/TypeConverters.md | 58 --- 24 files changed, 1 insertion(+), 3943 deletions(-) create mode 160000 usage2 delete mode 100644 usage2/Caching.md delete mode 100644 usage2/ContentProviderGeneration.md delete mode 100644 usage2/Databases.md delete mode 100644 usage2/GettingStarted.md delete mode 100644 usage2/Indexing.md delete mode 100644 usage2/Intro.md delete mode 100644 usage2/KotlinSupport.md delete mode 100644 usage2/ListBasedQueries.md delete mode 100644 usage2/Migration3Guide.md delete mode 100644 usage2/Migration4Guide.md delete mode 100644 usage2/Migrations.md delete mode 100644 usage2/ModelViews.md delete mode 100644 usage2/Models.md delete mode 100644 usage2/MultipleModules.md delete mode 100644 usage2/Observability.md delete mode 100644 usage2/QueryModels.md delete mode 100644 usage2/RXSupport.md delete mode 100644 usage2/Relationships.md delete mode 100644 usage2/Retrieval.md delete mode 100644 usage2/SQLCipherSupport.md delete mode 100644 usage2/SQLiteWrapperLanguage.md delete mode 100644 usage2/StoringData.md delete mode 100644 usage2/TypeConverters.md diff --git a/usage2 b/usage2 new file mode 160000 index 000000000..acbf59ea7 --- /dev/null +++ b/usage2 @@ -0,0 +1 @@ +Subproject commit acbf59ea7a647150696e3aaea386acb39b392176 diff --git a/usage2/Caching.md b/usage2/Caching.md deleted file mode 100644 index 834f88a74..000000000 --- a/usage2/Caching.md +++ /dev/null @@ -1,143 +0,0 @@ -# Caching - -DBFlow provides powerful caching mechanisms to speed up retrieval from the database -to enable high performance in our applications. - -Caching is not enabled by default, but it is very easy to enable. - -**Note**: caching functions correctly in only when we do CRUD operations on full model queries (i.e. Single table, no projections). - -Caching should be used when: - 1. Loading the same set of `Model` many times with slightly different selection. - 2. Operating on full `Model` objects (containing all `@PrimaryKey`) - -Caching should be avoided (or clear caches) when: - 1. Performing selection, complex queries, anything different than starting with `SQLite.select()` - 2. Using the wrapper modifications such as `Insert`, `Update`, or `Delete`. In this case you should clear the associated cache, post-operation. - -## Supported Caching Classes - -Caching is supported for: - 1. `SparseArray` via `SparseArrayBasedCache` (platform SparseArray) - 2. `Map` via `SimpleMapCache` - 3. `LruCache` via `ModelLruCache` (copy of `LruCache`, so dependency avoided) - 4. Custom Caching classes that implement `ModelCache` - -Cache sizes are not supported for `SimpleMapCache`. This is because `Map` can hold -arbitrary size of contents. - -## Enable Caching - -To enable caching on a single-primary key table, simply specify that it is enabled: - -```java - - -@Table(database = AppDatabase.class, cachingEnabled = true) -public class CacheableModel { - - @PrimaryKey(autoincrement = true) - long id; - - @Column - String name; -} - -``` - -to use caching on a table that uses multiple primary keys, [see](/usage2/Caching.md#multiple-primary-key-caching). - -By default we use a `SimpleMapCache`, which loads `Model` into a `Map`. The key is -either the primary key of the object or a combination of the two, but it should have -an associated `HashCode` and `equals()` value. - -## Loading from the DB - -When retrieving from the database, we still run a full query that returns a `Cursor`. -We skirt the expensive conversion process by checking the combination of Primary key on each row. -If an item exists with the same primary key combination, we return that object out of the cache. - -If you operate on a model object, that change gets reflected in the cache. But beware -modifying them in a separate thread might result in state state returned if the cache is not synchronized -properly. - -Any time a field on these objects are modified, you _should_ immediately save those -since we have a direct reference to the object from the cache. Otherwise, the DB -and cache could get into an inconsistent state. - -```java - -MyModel model = SQLite.select().from(MyModel.class).where(...).querySingle(); -model.setName("Name"); -model.save(); // save it to DB post any modifications to this object. - -``` - -## Disable Caching For Some Queries - -To disable caching on certain queries as you might want to project on only a few columns, -rather than the full dataset. Just call `disableCaching()`: - -```java - -select(My_Table.column, My_Table.column2) - .from(My.class) - .disableCaching() - .queryList(); - -``` - -## Advanced - -### Specifying cache Size - -To specify cache size, set `@Table(cacheSize = {size})`. Please note that not all -caches support sizing. It's up to each cache. - -### Custom Caches - -To specify a custom cache for a table, please define a `public static final` field: - -```java - -@ModelCacheField -public static ModelCache modelCache = new SimpleMapCache<>(); // replace with any cache you want. - -``` - -### Multiple Primary Key Caching - -This allows for tables that have multiple primary keys be used in caching. To use, -add a `@MultiCacheField` `public static final` field. -for example we have a `Coordinate` class: - - -```java - - -@Table(database = AppDatabase.class, cachingEnabled = true) -public class Coordinate { - - @MultiCacheField - public static final IMultiKeyCacheConverter multiKeyCacheModel = new IMultiKeyCacheConverter() { - - @Override - @NonNull - public String getCachingKey(@NonNull Object[] values) { - return "(" + values[0] + "," + values[1] + ")"; - } - }; - - @PrimaryKey - double latitude; - - @PrimaryKey - double longitude; - - -``` - -In this case we use the `IMultiKeyCacheConverter` class, which specifies a key type -that the object returns. The `getCachingKey` method returns an ordered set of `@PrimaryKey` -columns in declaration order. Also the value that is returned should have an `equals()` or `hashcode()` specified -especially when used in the `SimpleMapCache`. diff --git a/usage2/ContentProviderGeneration.md b/usage2/ContentProviderGeneration.md deleted file mode 100644 index 3bf2d5566..000000000 --- a/usage2/ContentProviderGeneration.md +++ /dev/null @@ -1,234 +0,0 @@ -# Content Provider Generation -This library includes a very fast, easy way to use `ContentProvider`! Using annotations, you can generate `ContentProvider` with ease. - -## Getting Started -This feature is largely based off of [schematic](https://github.com/SimonVT/schematic), - while leveraging DBFlow's power. - -### Placeholder ContentProvider -In order to define a `ContentProvider`, you must define it in a placeholder class: - -```java - -@ContentProvider(authority = TestContentProvider.AUTHORITY, - database = TestDatabase.class, - baseContentUri = TestContentProvider.BASE_CONTENT_URI) -public class TestContentProvider { - - public static final String AUTHORITY = "com.raizlabs.android.dbflow.test.provider"; - - public static final String BASE_CONTENT_URI = "content://"; - -} -``` - -or you can use the annotation in any class you wish. The recommended place would be in a `@Database` placeholder class. This is to simplify some of the declarations and keep it all in one place. - -```java - -@ContentProvider(authority = TestDatabase.AUTHORITY, - database = TestDatabase.class, - baseContentUri = TestDatabase.BASE_CONTENT_URI) -@Database(name = TestDatabase.NAME, version = TestDatabase.VERSION) -public class TestDatabase { - - public static final String NAME = "TestDatabase"; - - public static final int VERSION = "1"; - - public static final String AUTHORITY = "com.raizlabs.android.dbflow.test.provider"; - - public static final String BASE_CONTENT_URI = "content://"; - -} -``` - -### Adding To Manifest -In other applications or your current's `AndroidManifest.xml` add the **generated $Provider** class: - -```xml - - -``` - -`android:exported`: setting this to true, enables other applications to make use of it. - -**True** is recommended for outside application access. - -**Note you must have at least one `@TableEndpoint` for it to compile/pass error checking** - -### Adding endpoints into the data -There are two ways of defining `@TableEndpoint`: -1. Create an inner class within the `@ContentProvider` annotation. -2. Or Add the annotation to a `@Table` and specify the content provider class name (ex. TestContentProvider) - -`@TableEndpoint`: links up a query, insert, delete, and update to a specific table in the `ContentProvider` local database. - -Some recommendations: -1. (if inside a `@ContentProvider` class) Name the inner class same as the table it's referencing -2. Create a `public static final String ENDPOINT = "{tableName}"` field for reusability -3. Create `buildUri()` method (see below) to aid in creating other ones. - -To define one: - -```java - -@TableEndpoint(ContentProviderModel.ENDPOINT) -public static class ContentProviderModel { - - public static final String ENDPOINT = "ContentProviderModel"; - - private static Uri buildUri(String... paths) { - Uri.Builder builder = Uri.parse(BASE_CONTENT_URI + AUTHORITY).buildUpon(); - for (String path : paths) { - builder.appendPath(path); - } - return builder.build(); - } - - @ContentUri(path = ContentProviderModel.ENDPOINT, - type = ContentUri.ContentType.VND_MULTIPLE + ENDPOINT) - public static Uri CONTENT_URI = buildUri(ENDPOINT); - -} -``` - -or via the table it belongs to - -```java - - -@TableEndpoint(name = ContentProviderModel.NAME, contentProvider = ContentDatabase.class) -@Table(database = ContentDatabase.class, tableName = ContentProviderModel.NAME) -public class ContentProviderModel extends BaseProviderModel { - - public static final String NAME = "ContentProviderModel"; - - @ContentUri(path = NAME, type = ContentUri.ContentType.VND_MULTIPLE + NAME) - public static final Uri CONTENT_URI = ContentUtils.buildUriWithAuthority(ContentDatabase.AUTHORITY); - - @PrimaryKey(autoincrement = true) - long id; - - @Column - String notes; - - @Column - String title; - - @Override - public Uri getDeleteUri() { - return TestContentProvider.ContentProviderModel.CONTENT_URI; - } - - @Override - public Uri getInsertUri() { - return TestContentProvider.ContentProviderModel.CONTENT_URI; - } - - @Override - public Uri getUpdateUri() { - return TestContentProvider.ContentProviderModel.CONTENT_URI; - } - - @Override - public Uri getQueryUri() { - return TestContentProvider.ContentProviderModel.CONTENT_URI; - } -} -``` - -There are much more detailed usages of the `@ContentUri` annotation. Those will be in a later section. - -### Connect Model operations to the newly created ContentProvider -There are two kinds of `Model` that connect your application to a ContentProvider that was defined in your app, or another app. Extend these for convenience, however they are not required. - -`BaseProviderModel`: Overrides all `Model` methods and performs them on the `ContentProvider` - -`BaseSyncableProviderModel`: same as above, except it will synchronize the data changes with the local app database as well! - -#### Interacting with the Content Provider -You can use the `ContentUtils` methods: - -```java - -ContentProviderModel contentProviderModel = ...; // some instance - -int count = ContentUtils.update(getContentResolver(), ContentProviderModel.CONTENT_URI, contentProviderModel); - -Uri uri = ContentUtils.insert(getContentResolver(), ContentProviderModel.CONTENT_URI, contentProviderModel); - -int count = ContentUtils.delete(getContentResolver(), someContentUri, contentProviderModel); -``` - -**Recommended** usage is extending `BaseSyncableProviderModel` (for inter-app usage) so the local database contains the same data. Otherwise `BaseProviderModel` works just as well. - -```java - -MyModel model = new MyModel(); -model.id = 5; -model.load(); // queries the content provider - -model.someProp = "Hello" -model.update(false); // runs an update on the CP - -model.insert(false); // inserts the data into the CP -``` - -## Advanced Usage -### Notify Methods -You can define `@Notify` method to specify a custom interaction with the `ContentProvider` and return a custom `Uri[]` that notifies the contained `ContentResolver`. These methods can have any valid parameter from the `ContentProvider` methods. - -Supported kinds include: -1. Update -2. Insert -3. Delete - -#### Example - -```java - -@Notify(method = Notify.Method.UPDATE, -paths = {}) // specify paths that will call this method when specified. -public static Uri[] onUpdate(Context context, Uri uri) { - - return new Uri[] { - // return custom uris here - }; -} -``` - -### ContentUri Advanced -#### Path Segments -Path segments enable you to "filter" the uri query, update, insert, and deletion by a specific column and a value define by '#'. - -To specify one, this is an example `path` - -```java - -path = "Friends/#/#" -``` - -then match up the segments as: - -```java - -segments = {@PathSegment(segment = 1, column = "id"), - @PathSegment(segment = 2, column = "name")} -``` - -And to put it all together: - -```java - -@ContentUri(type = ContentType.VND_MULTIPLE, -path = "Friends/#/#", -segments = {@PathSegment(segment = 1, column = "id"), - @PathSegment(segment = 2, column = "name")}) -public static Uri withIdAndName(int id, String name) { - return buildUri(id, name); -} -``` diff --git a/usage2/Databases.md b/usage2/Databases.md deleted file mode 100644 index b61b8feb4..000000000 --- a/usage2/Databases.md +++ /dev/null @@ -1,131 +0,0 @@ -# Databases - -This section describes how databases are created in DBFlow and some more -advanced features. - -## Creating a Database - -In DBFlow, creating a database is as simple as only a few lines of code. DBFlow -supports any number of databases, however individual tables and other related files -can only be associated with one database. - -```java - -@Database(name = AppDatabase.NAME, version = AppDatabase.VERSION) -public class AppDatabase { - - public static final String NAME = "AppDatabase"; // we will add the .db extension - - public static final int VERSION = 1; -} - - -``` - -## Database Migrations - -Database migrations are run when upon open of the database connection, -the version number increases on an existing database. - -It is preferred that `Migration` files go in the same file as the database, for -organizational purposes. -An example migration: - -```java - -@Database(name = AppDatabase.NAME, version = AppDatabase.VERSION) -public class AppDatabase { - - public static final String NAME = "AppDatabase"; // we will add the .db extension - - public static final int VERSION = 2; - - @Migration(version = 2, database = MigrationDatabase.class) - public static class AddEmailToUserMigration extends AlterTableMigration { - - public AddEmailToUserMigration(Class table) { - super(table); - } - - @Override - public void onPreMigrate() { - addColumn(SQLiteType.TEXT, "email"); - } - } -} - -``` -This simple example adds a column to the `User` table named "email". In code, just add -the column to the `Model` class and this migration runs only on existing dbs. - To read more on migrations and more examples of different kinds, visit the [page](/usage2/Migrations.md). - -## Advanced Database features - -This section goes through features that are for more advanced use of a database, -and may be very useful. - -### Prepackaged Databases -To include a prepackaged database for your application, simply include the ".db" file in `src/main/assets/{databaseName}.db`. On creation of the database, we copy over the file into the application for usage. Since this is prepackaged within the APK, we cannot delete it once it's copied over, -which can bulk up your raw APK size. _Note_ this is only copied over on initial creation -of the database for the app. - -### Global Conflict Handling -In DBFlow when an INSERT or UPDATE are performed, by default, we use `NONE`. If you wish to configure this globally, you can define it to apply for all tables from a given database: - - -```java - -@Database(name = AppDatabase.NAME, version = AppDatabase.VERSION, insertConflict = ConflictAction.IGNORE, updateConflict= ConflictAction.REPLACE) -public class AppDatabase { -} - -``` - -These follow the SQLite standard [here](https://www.sqlite.org/conflict.html). - -### Integrity Checking - -Databases can get corrupted or in an invalid state at some point. If you specify -`consistencyChecksEnabled=true` It runs a `PRAGMA quick_check(1)` -whenever the database is opened. If it fails, you should provide a backup database -that it will copy over. If not, **we wipe the internal database**. Note that during this -time in case of failure we create a **third copy of the database** in case transfer fails. - -### Custom FlowSQLiteOpenHelper - -For variety of reasons, you may want to provide your own `FlowSQLiteOpenHelper` -to manage database interactions. To do so, you must implement `OpenHelper`, but -for convenience you should extend `FlowSQLiteOpenHelper` (for Android databases), -or `SQLCipherOpenHelper` for SQLCipher. Read more [here](/usage2/SQLCipherSupport.md) - -```java - - -public class CustomFlowSQliteOpenHelper extends FlowSQLiteOpenHelper { - - public CustomFlowSQliteOpenHelper(BaseDatabaseDefinition databaseDefinition, DatabaseHelperListener listener) { - super(databaseDefinition, listener); - } -} - - -``` - -Then in your `DatabaseConfig`: - -```java - -FlowManager.init(new FlowConfig.Builder(RuntimeEnvironment.application) - .addDatabaseConfig( - new DatabaseConfig.Builder(CipherDatabase.class) - .openHelper(new DatabaseConfig.OpenHelperCreator() { - @Override - public OpenHelper createHelper(DatabaseDefinition databaseDefinition, DatabaseHelperListener helperListener) { - return new CustomFlowSQliteOpenHelper(databaseDefinition, helperListener); - } - }) - .build()) - .build()); - - -``` diff --git a/usage2/GettingStarted.md b/usage2/GettingStarted.md deleted file mode 100644 index 9f40e7307..000000000 --- a/usage2/GettingStarted.md +++ /dev/null @@ -1,154 +0,0 @@ -# Getting Started - -This section describes how Models and tables are constructed via DBFlow. first -let's describe how to get a database up and running. - -## Creating a Database - -In DBFlow, creating a database is as simple as only a few lines of code. DBFlow -supports any number of databases, however individual tables and other related files -can only be associated with one database. - -```java - -@Database(name = AppDatabase.NAME, version = AppDatabase.VERSION) -public class AppDatabase { - - public static final String NAME = "AppDatabase"; // we will add the .db extension - - public static final int VERSION = 1; -} - - -``` - -Writing this file generates (by default) a `AppDatabaseAppDatabase_Database.java` -file, which contains tables, views, and more all tied to a specific database. This -class is automatically placed into the main `GeneratedDatabaseHolder`, which holds -potentially many databases. The name, `AppDatabaseAppDatabase_Database.java`, is generated -via {DatabaseClassName}{DatabaseFileName}{GeneratedClassSepator, default = "\_"}Database - -To learn more about what you can configure in a database, read [here](/usage2/Databases.md) - -## Initialize FlowManager - -DBFlow needs an instance of `Context` in order to use it for a few features such -as reading from assets, content observing, and generating `ContentProvider`. - -Initialize in your `Application` subclass. You can also initialize it from other -`Context` but we always grab the `Application` `Context` (this is done only once). - -```java -public class ExampleApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - FlowManager.init(this); - } -} - -``` - -Finally, add the definition to the manifest (with the name that you chose for your custom application): -```xml - - -``` - -A database within DBFlow is only initialized once you call `FlowManager.getDatabase(SomeDatabase.class).getWritableDatabase()`. If you -don't want this behavior or prefer it to happen immediately, modify your `FlowConfig`: - -```java - -@Override -public void onCreate() { - super.onCreate(); - FlowManager.init(new FlowConfig.Builder(this) - .openDatabasesOnInit(true).build()); -} - -``` - -If you do not like the built-in `DefaultTransactionManager`, or just want to roll your own existing system: - -```java - -FlowManager.init(new FlowConfig.Builder(this) - .addDatabaseConfig( - new DatabaseConfig.Builder(AppDatabase.class) - .transactionManager(new CustomTransactionManager()) - .build())); - -``` -You can define different kinds for each database. -To read more on transactions and subclassing `BaseTransactionManager` go [here](/usage2/StoringData.md) - - -## Create Models - -Creating models are as simple as defining the model class, and adding the `@Table` annotation. -To read more on this, read [here](/usage2/Models.md). - -An example: - -```java - - -@Table(database = TestDatabase.class) -public class Currency { - - @PrimaryKey(autoincrement = true) - long id; // package-private recommended, not required - - @Column - @Unique - String symbol; - - @Column - String shortName; - - @Column - @Unique - private String name; // private with getters and setters - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} - -``` - -## Perform Some Queries - -DBFlow uses expressive builders to represent and translate to the SQLite language. - -A simple query in SQLite: - -```sqlite - -SELECT * FROM Currency WHERE symbol='$'; - -``` - -Can be represented by: - -```java - -SQLite.select() - .from(Currency.class) - .where(Currency_Table.symbol.eq("$")); - -``` - -We support many kinds of complex and complicated queries using the builder -language. To read more about this, see [the wrapper language docs](/usage2/SQLiteWrapperLanguage.md) - -There is much more you can do in DBFlow. Read through the other docs to -get a sense of the library. diff --git a/usage2/Indexing.md b/usage2/Indexing.md deleted file mode 100644 index 1e8129b55..000000000 --- a/usage2/Indexing.md +++ /dev/null @@ -1,78 +0,0 @@ -# Indexing - -In SQLite, an `Index` is a pointer to specific columns in a table that enable super-fast retrieval. - -__Note__: The database size can increase significantly, however if performance is more important, the tradeoff is worth it. - -Indexes are defined using the `indexGroups()` property of the `@Table` annotation. These operate similar to how `UniqueGroup` work: -1. specify an `@IndexGroup` -2. Add the `@Index` -3. Build and an `IndexProperty` gets generated. This allows super-easy access to the index so you can enable/disable it with ease. - -__Note__: `Index` are not explicitly enabled unless coupled with an `IndexMigration`. ([read here](/usage2/Migrations.md#index-migrations)). - -You can define as many `@IndexGroup` you want within a `@Table` as long as one field references the group. Also individual `@Column` can belong to any number of groups: - -```java - -@Table(database = TestDatabase.class, - indexGroups = { - @IndexGroup(number = 1, name = "firstIndex"), - @IndexGroup(number = 2, name = "secondIndex"), - @IndexGroup(number = 3, name = "thirdIndex") - }) -public class IndexModel2 { - - @Index(indexGroups = {1, 2, 3}) - @PrimaryKey - int id; - - @Index(indexGroups = 1) - @Column - String first_name; - - @Index(indexGroups = 2) - @Column - String last_name; - - @Index(indexGroups = {1, 3}) - @Column - Date created_date; - - @Index(indexGroups = {2, 3}) - @Column - boolean isPro; -} -``` - -By defining the index this way, we generate an `IndexProperty`, which makes it very -easy to enable, disable, and use it within queries: - -```java - -IndexModel2_Table.firstIndex.createIfNotExists(); - -SQLite.select() - .from(IndexModel2.class) - .indexedBy(IndexModel2_Table.firstIndex) - .where(...); // do a query here. - -IndexModel2_Table.firstIndex.drop(); // turn it off when no longer needed. -``` - -## SQLite Index Wrapper - -For flexibility, we also support the SQLite `Index` wrapper object, in which the `IndexProperty` -uses underneath. - -```java - -Index index = SQLite.index("MyIndex") - .on(SomeTable.class, SomeTable_Table.name, SomeTable_Table.othercolumn); -index.enable(); - -// do some operations - -index.disable(); // disable when no longer needed - -``` diff --git a/usage2/Intro.md b/usage2/Intro.md deleted file mode 100644 index cb3ae6b7b..000000000 --- a/usage2/Intro.md +++ /dev/null @@ -1,126 +0,0 @@ -# DBFlow - -DBFlow for Android lets you write very efficient database code while remaining -expressive and concise. - -```java - -@Table(database = AppDatabase.class) -public class Automobile extends BaseModel { // convenience, but not required to interact with db - - @PrimaryKey - String vin; - - @Column - String make; - - @Column - String model; - - @Column - int year; - -} - -Automobile venza = new Automobile(); -venza.vin = "499499449"; -venza.make = "Toyota"; -venza.model = "Venza"; -venza.year = 2013; -venza.save(); // inserts if not exists by primary key, updates if exists. - -// querying -// SELECT * FROM `Automobile` WHERE `year`=2001 AND `model`='Camry' -// we autogen a "_Table" class that contains convenience Properties which provide easy SQL ops. -SQLite().select() - .from(Automobile.class) - .where(Automobile_Table.year.is(2001)) - .and(Automobile_Table.model.is("Camry")) - .async() - .queryResultCallback(new QueryTransaction.QueryResultCallback() { - @Override - public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult tResult) { - // called when query returns on UI thread - List autos = tResult.toListClose(); - // do something with results - } - }, new Transaction.Error() { - @Override - public void onError(Transaction transaction, Throwable error) { - // handle any errors - } - }).execute(); - -// run a transaction synchronous easily. -DatabaseDefinition database = FlowManager.getDatabase(AppDatabase.class); -database.executeTransaction(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - // do something here - } -}); - -// run asynchronous transactions easily, with expressive builders -database.beginTransactionAsync(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - // do something in BG - } - }).success(successCallback).error(errorCallback).build().execute(); - -``` - -## Proguard - -Since DBFlow uses annotation processing, which is run pre-proguard phase, -the configuration is highly minimal: - -``` --keep class * extends com.raizlabs.android.dbflow.config.DatabaseHolder { *; } -``` - -## Sections - -For migrating from 3.x to 4.0, read [here](/usage2/Migration4Guide.md) - -The list of documentation is listed here: - - [Getting Started](/usage2/GettingStarted.md) - - [Databases](/usage2/Databases.md) - - [Models](/usage2/Models.md) - - [Relationships](/usage2/Relationships.md) - - [Storing Data](/usage2/StoringData.md) - - [Retrieval](/usage2/Retrieval.md) - - [The SQLite Wrapper Language](/usage2/SQLiteWrapperLanguage.md) - - [Caching](/usage2/Caching.md) - - [List-Based Queries](/usage2/ListBasedQueries.md) - - [Migrations](/usage2/Migrations.md) - - [Observability](/usage2/Observability.md) - - [Type Converters](/usage2/TypeConverters.md) - -For advanced DBFlow usages: - - [Kotlin Support](/usage2/KotlinSupport.md) - - [RX Java Support](/usage2/RXSupport.md) - - [Multiple Modules](/usage2/MultipleModules.md) - - [Views](/usage2/ModelViews.md) - - [Query Models](/usage2/QueryModels.md) - - [Indexing](/usage2/Indexing.md) - - [SQLCipher](/usage2/SQLCipherSupport.md) diff --git a/usage2/KotlinSupport.md b/usage2/KotlinSupport.md deleted file mode 100644 index 51eb891eb..000000000 --- a/usage2/KotlinSupport.md +++ /dev/null @@ -1,231 +0,0 @@ -# Kotlin Support + Extensions - -DBFlow supports Kotlin out of the box and is fairly easily to use and implement. - -To add useful Kotlin-specific extensions: -``` -dependencies { - compile "com.github.Raizlabs.DBFlow:dbflow-kotlinextensions:${dbflow_version}@aar" -} - -``` -We also support [kotlin extensions for RX 1 + 2](/usage2/RXSupport.md)! - -## Classes - -DBFlow Classes are beautifully concise to write: - -```kotlin -@Table(database = KotlinDatabase::class) -class Person(@PrimaryKey var id: Int = 0, @Column var name: String? = null) -``` - -Also `data` classes are supported. - -```kotlin -@Table(database = KotlinTestDatabase::class) -data class Car(@PrimaryKey var id: Int = 0, @Column var name: String? = null) -``` - -In 4.0.0+, DBFlow contains a few extensions for Kotlin models which enable you -to keep your models acting like `BaseModel`, but do not have to explicitly extend -the class! - -```kotlin - -car.save() // extension method, optional databaseWrapper parameter. -car.insert() -car.update() -car.delete() -car.exists() - -``` - -## Query LINQ Syntax - -Kotlin has nice support for custim `infix` operators. Using this we can convert a regular, Plain old java query into a C#-like LINQ syntax. - -java: -``` - -List = SQLite.select() - .from(Result.class) - .where(Result_Table.column.eq(6)) - .and(Result_Table.column2.in("5", "6", "9")).queryList() - -``` - -kotlin: - -``` -val results = (select - from Result::class - where (column eq 6) - and (column2 `in`("5", "6", "9")) - groupBy column).list - // can call .result for single result - // .hasData if it has results - // .statement for a compiled statement -``` - -Enabling us to write code that is closer in syntax to SQLite! - -This supported for almost any SQLite operator that this library provides including: - 1. `Select` - 2. `Insert` - 3. `Update` - 4. `Delete` - -**Async Operations**: -With extensions we also support `async` operations on queries: - -```kotlin - -// easy async list query -(select - from Result::class - where (column eq 6)) -.async list { transaction, list -> - // do something here - updateUI(list) -} - -// easy single result query -(select - from Result::class - where (column eq 6)) -.async result { transaction, model -> - // do something here - updateUI(model) -} - -val model = Result() - -model.async save { - // completed, now do something with model -} - -``` - -### Property Extensions - -With Kotlin, we can define extension methods on pretty much any class. - -With this, we added methods to easily create `IProperty` from anything to make -queries a little more streamlined. In this query, we also make use of the extension -method for `from` to streamline the query even more. - -```kotlin - -var query = (select - from TestModel::class - where (5.property lessThan column) - and (clause(date.property between start_date) - and(end_date))) - - -``` - -### Query Extensions - -We can easily create nested `Operator` into `OperatorGroup` also fairly easily, also -other, random extensions: -```kotlin - -select from SomeTable::class where (name.eq("name") and id.eq(0)) - -"name".op() collate NOCASE - -"name".nameAlias - -"name".nameAlias `as` "My Name" - -// query sugar - -select from SomeTable::class where (name eq "name") or (id eq 0) - -``` - - -### Database Extensions - -#### Process Models Asynchronously - -In Java, we need to write something of the fashion: - -```java - -List items = SQLite.select() - .from(TestModel.class) - .queryList(); - - database.beginTransactionAsync(new ProcessModelTransaction.Builder<>( - new ProcessModel() { - @Override - public void processModel(TestModel model, DatabaseWrapper database) { - - } - }) - .success(successCallback) - .error(errorCallback).build() - .execute(); - -``` - -In Kotlin, we can use a combo of DSL and extension methods to: - -```kotlin - -var items = (select from TestModel1::class).list - - // easily delete all these items. - items.processInTransactionAsync { it, databaseWrapper -> it.delete(databaseWrapper) } - - // easily delete all these items with success - items.processInTransactionAsync({ it, databaseWrapper -> it.delete(databaseWrapper) }, - Transaction.Success { - // do something here - }) -// delete with all callbacks -items.processInTransactionAsync({ it, databaseWrapper -> it.delete(databaseWrapper) }, - Transaction.Success { - // do something here - }, - Transaction.Error { transaction, throwable -> - - }) - -``` - -The extension method on `Collection` allows you to perform this on all -collections from your Table! - -If you wish to easily do them _synchronously_ then use: - -```kotlin - -items.processInTransaction { it, databaseWrapper -> it.delete(databaseWrapper) } - -``` - -#### Class Extensions - -If you need access to the Database, ModelAdapter, etc for a specific class you -can now use the following (and more) reified global functions for easy access! - -```kotlin - -database() - -databaseForTable() - -writableDatabaseForTable() - -tableName() - -modelAdapter() - - -``` - -Which under-the-hood call their corresponding `FlowManager` methods. diff --git a/usage2/ListBasedQueries.md b/usage2/ListBasedQueries.md deleted file mode 100644 index 1e2949e01..000000000 --- a/usage2/ListBasedQueries.md +++ /dev/null @@ -1,157 +0,0 @@ -# List-Based Queries - -When we have large datasets from the database in our application, we wish to -display them in a `ListView`, `RecyclerView` or some other component that recycles -it's views. Instead of running a potentially very large query on the database, -converting it to a `List` and then keeping that chunk of memory active, we -can lazy-load each row from the query/table. - -DBFlow makes it easy using the `FlowCursorList`, for simple `BaseAdapter`-like methods, -or the `FlowQueryList`, which implements the `List` interface. - -Getting one of these lists is as simple as: - -```java - -FlowQueryList list = SQLite.select() - .from(MyTable.class) - .where(...) // some conditions - .flowQueryList(); -FlowCursorList list = SQLite.select() - .from(MyTable.class) - .where(...) // some conditions - .cursorList(); - - list.close(); // ensure you close these, as they utilize active cursors :) - -``` - -Any query method allows you to retrieve a default implementation of each. You -can also manually instantiate them: - -```java - -FlowQueryList list = new FlowQueryList.Builder<>(SQLite.select().from(MyTable.class)) - .cachingEnabled(false) // caching enabled by default - .build(); - -FlowCursorList list = new FlowCursorList.Builder<>(SQLite.select().from(MyTable.class)) - .cachingEnabled(true) - .modelCache(cache) // provide custom cache for this list - .build(); - -``` - -## Caching - -Both of these classes come with the ability to cache `Model` used in it's queries -so that loading only happens once and performance can remain high once loaded. The default -caching mechanism is a `ModelLruCache`, which provides an `LruCache` to manage -loading `Model`. - -They are done in almost the same way: - -```java - -FlowCursorList list = new FlowCursorList.Builder<>(SQLite.select().from(MyTable.class)) - .modelCache(cache) // provide custom cache for this list - .build(); -FlowQueryList list = new FlowQueryList.Builder<>(SQLite.select().from(MyTable.class)) - .modelCache(cache) - .build(); - -``` - -## FlowCursorList - -The `FlowCursorList` is simply a wrapper around a standard `Cursor`, giving it the -ability to cache `Model`, load items at specific position with conversion, and refresh -it's content easily. - -The `FlowCursorList` by default caches its results, for fast usage. The cache size is determined by the `ModelCache` you're using. Read on [here](/usage2/Caching.md). - -The `FlowCursorList` provides these methods: - - 1. `getItem(position)` - loads item from `Cursor` at specified position, caching and loading from cache (if enabled) - 2. `refresh()` - re-queries the underlying `Cursor`, clears out the cache, and reconstructs it. Use a `OnCursorRefreshListener` to get callbacks when this occurs. - 3. `getAll()` - returns a `List` of all items from the `Cursor`, no caching used - 4. `getCount()` - returns count of `Cursor` or 0 if `Cursor` is `null` - 5. `isEmpty()` - returns if count == 0 - 6. `clearCache()` - manually clears cache - -## Flow Query List - -This class is a much more powerful version of the `FlowCursorList`. It contains a `FlowCursorList`, -which backs it's retrieval operations. - -This class acts as `List` and can be used almost wherever a `List` is used. Also, it is a `FlowContentObserver` -see [Observability](/usage2/Observability.md), meaning other classes can listen -for its specific changes and it can auto-refresh itself when content changes. - -Feature rundown: - 1. `List` implementation of a Query - 2. `FlowContentObserver`, only for the table that it corresponds to in its initial `ModelQueriable` query statement. Mostly used for self refreshes. - 3. Transact changes to the query asynchronously (note that this refreshes itself every callback unless in a transaction state) - 5. Caching (almost same implementation as `FlowCursorList`) - -### List Implementation - -The `List` implementation is mostly for convenience. Please note that most of the modification -methods (`add`, `addAll` etc.) may not affect the query that you expect it to, unless the object you pass -objects that are valid for the query and you enable self refreshes. - -The retrieval methods are where the query works as you would expect. `get()` calls -`getItem()` on the internal `FlowCursorList`, `isEmpty()`, `getCount()`, etc all correspond -to the `Cursor` underneath. - -Both `FlowQueryList` and `FlowTableList` support `Iterator` and provide a very -efficient class: `FlowCursorIterator` that iterates through each row in a `Cursor` -and provides efficient operations. - -**Note**: any retrieval operation that turns it into another object (i.e. `subList()`, -`toArray`, etc) retrieves all objects contained in the query into memory, -and then converts it using the associated method on that returned `List`. - -### FlowContentObserver Implementation - -Using the `FlowContentObserver`, we can enable self-refreshes whenever a model changes -for the table this query points to. See [Observability](/usage2/Observability.md). - -To turn on self-refreshes, call `registerForContentChanges(context)`, which requeries -the data whenever it changes. - -We recommend placing this within a transaction on the `FlowQueryList`, so we only -refresh content the minimal amount of times: - -```java - -flowQueryList.beginTransaction(); - -// perform a bunch of modifications - -flowQueryList.endTransactionAndNotify(); - -``` - -To listen for `Cursor` refreshes register a `OnCursorRefreshListener`: - -```java - -modelList - .addOnCursorRefreshListener(new FlowCursorList.OnCursorRefreshListener() { - @Override - public void onCursorRefreshed(FlowCursorList cursorList) { - - } - }); - -``` - - -### Transact Changes Asynchronously - -If you want to pass or modify the `FlowQueryList` asynchronously, set `setTransact(true)`. -This will run all modifications in a `Transaction` and when completed, a `Cursor` refresh occurs. - -You can also register `Transaction.Error` and `Transaction.Success` callbacks for these modifications -on the `FlowQueryList` to handle when these `Transaction` finish. diff --git a/usage2/Migration3Guide.md b/usage2/Migration3Guide.md deleted file mode 100644 index dd2c606c6..000000000 --- a/usage2/Migration3Guide.md +++ /dev/null @@ -1,630 +0,0 @@ -# DBFlow 3.0 Migration Guide -DBFlow has undergone the most _significant_ changes in its lifetime in 3.0. This guide is meant to assist you in migrating from 2.1.x and above and _may not_ be fully inclusive of all changes. This doc will mention the most glaring and significant changes. If in doubt, consult the usage2 docs. - -A significant portion of the changes include the _complete_ overhaul of the underlying annotation processor, leading to wonderful improvements in maintainability of the code, readability, and stability of the generated code. Now it uses the updated [JavaPoet](https://github.com/square/javapoet) vs the outdated JavaWriter. The changes in this library alone _significantly_ helps out the stability of the generated code. - -_Some Changes to Note:_ -1. `update` no longer attempts to `insert` if it fails. -2. Package private fields from other packages are now automatically accessible via generated `_Helper` classes. The referenced fields must be annotated with `@Column`, `@PrimaryKey`, or `@ForeignKey`. if its a legacy `ForeignKeyReference`, `referendFieldIsPackagePrivate()` must be set to true. -3. `@Column` no longer required in conjunction with `@PrimaryKey` or `@ForeignKey` -4. Can now have DBFlow in multiple modules, libraries, etc via "Modules"! -5. `TransactionManager` has been replaced with a new per-database `BaseTransactionManager`. Each DB has its own `DBTransactionQueue` and you can replace the default with your own system. Also, no longer is this priority-based, but rather order-based. See more [here](/usage2/Transactions.md) - -This doc is to provide some basic examples of what has changed, but read all of the new usage docs! -Starting with [Intro](/usage2/Intro.md) - -## Table Of Contents - 1. [Initialization](/usage2/Migration3Guide.md#initialization) - 2. [Database + Table Structure](/usage2/Migration3Guide.md#database-and-table-structure) - 3. [Transactions Overhaul](/usage2/Migration3Guide.md#transactions-overhaul) - 4. [Properties](/usage2/Migration3Guide.md#properties) - 5. [ModelContainers](/usage2/Migration3Guide.md#modelcontainers) - 6. [ModelViews](/usage2/Migration3Guide.md#modelviews) - 7. [Caching](/usage2/Migration3Guide.md#caching) - 8. [Database Modules](/usage2/Migration3Guide.md#database-modules) - -## Initialization - -Previously DBFlow was intialized via: - -```java -public class ExampleApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - FlowManager.init(this); - } -} - -``` - -Now we use the `FlowConfig.Builder`: - -```java -public class ExampleApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - FlowManager.init(new FlowConfig.Builder(this).build()); - } -} - -``` - -See more of what you can customize [here](/usage2/GettingStarted.md) - -## Database And Table Structure -### Database changes -The default `generatedClassSeparator` is now `_` instead of `$` to play nice with Kotlin by default. A simple addition of: - -```java - -@Database(generatedClassSeparator = "$") -``` - -will keep your generated "Table" and other classes the same name. - -Globally, we no longer reference what `@Database` any database-specific element (Table, Migration, etc) by `String` name, but by `Class` now. - -Before: - -```java - -@Table(databaseName = AppDatabase.NAME) -@Migration(databaseName = AppDatabase.NAME) -``` - -After: - -```java - -@Table(database = AppDatabase.class) -@Migration(database = AppDatabase.class) -``` - -Why: We decided that referencing it directly by class name enforces type-safety and direct enforcement of the database placeholder class. Previously, - -```java - -@Table(databaseName = "AppDatabase") -``` - -was a valid specifier, which might lead to typos or errors. - -## Table Changes -`@Table` have some significant changes. - -Private boolean fields by default have changed. -`useIsForPrivateBooleans()` has changed to `useBooleanGetterSetters()`. By default -this is enabled, meaning `boolean` variables follow the convention: - -```java - -private boolean isEnabled; - -public boolean isEnabled() { - return isEnabled; -} - -public void setEnabled(boolean isEnabled) { - this.isEnabled = isEnabled; -} - -``` - -Instead of generating just `String` column name within a corresponding `$Table` class, it now generates `Property` fields. These fields are significantly smarter and more powerful. They considerably aid in the simplification of many complex queries and make the code in general more readable, type-safe, and just overall better. _NOTE: the properties are no longer capitalized, rather they match exact casing of the Column name._ - -Previously, when you defined a class as: - -```java - -@Table(databaseName = TestDatabase.NAME) -@ModelContainer -public class TestModel2 extends BaseModel { - - @Column - @PrimaryKey - String name; - - @Column(name = "model_order") - int order; -} -``` - -It generated a `TestModel2$Table` class: - -```java - -public final class TestModel2_Table { - - public static final String NAME = "name"; - - public static final String MODEL_ORDER = "model_order"; -} -``` - -Now when you define a class, it generates a definition as follows: - -```java -public final class TestModel2_Table { - public static final Property name = new Property(TestModel2.class, "name"); - - public static final IntProperty model_order = new IntProperty(TestModel2.class, "model_order"); - - public static final IProperty[] getAllColumnProperties() { - return new IProperty[]{name,model_order}; - } - - public static BaseProperty getProperty(String columnName) { - columnName = QueryBuilder.quoteIfNeeded(columnName); - switch (columnName) { - case "`name`": { - return name; - } - case "`model_order`": { - return model_order; - } - default: { - throw new IllegalArgumentException("Invalid column name passed. Ensure you are calling the correct table's column"); - } - } - } -} -``` - -Each `Property` now is used for each all references to a column in query statements. - -The `getProperty()` method allows to keep compatibility with the old format, solve some `ContentProvider` compatibility issues, or allow looking up `Property` by key. - -To read on how these properties interact read "Properties, Conditions, Queries, Replacement of ConditionQueryBuilder and more". - -### Index changes -Added was an `IndexGroup[]` of `indexGroups()`. - -Now we can generate `IndexProperty` (see properties for more information), which provide us a convenient generated `Index` to use for the table. This then is used in a queries that rely on indexes and make it dead simple to activate and deactivate indexes. - -A class written like: - -```java -@Table(database = TestDatabase.class, - indexGroups = { - @IndexGroup(number = 1, name = "firstIndex"), - @IndexGroup(number = 2, name = "secondIndex"), - @IndexGroup(number = 3, name = "thirdIndex") - }) -public class IndexModel2 extends BaseModel { - - @Index(indexGroups = {1, 2, 3}) - @PrimaryKey - int id; - - @Index(indexGroups = 1) - @Column - String first_name; - - @Index(indexGroups = 2) - @Column - String last_name; - - @Index(indexGroups = {1, 3}) - @Column - Date created_date; - - @Index(indexGroups = {2, 3}) - @Column - boolean isPro; -} -``` - -Generates in its "Table" class: - -```java -public final class IndexModel2_Table { - //...previous code omitted - - public static final IndexProperty index_firstIndex = new IndexProperty<>("firstIndex", false, IndexModel2.class, id, first_name, created_date); - - public static final IndexProperty index_secondIndex = new IndexProperty<>("secondIndex", false, IndexModel2.class, id, last_name, isPro); - - public static final IndexProperty index_thirdIndex = new IndexProperty<>("thirdIndex", false, IndexModel2.class, id, created_date, isPro); -``` - -### Foreign Key Changes -`@ForeignKey` fields no longer need to specify it's references or the `@Column` annotation!!! The old way still works, but is no longer necessary for `Model`-based ForeignKeys. The annotation processor takes the primary keys of the referenced table and generates a column with {fieldName}_{referencedColumnName} that represents the same SQLite Type of the field.
_Note: that is not backwards compatible_ with apps already with references. - -Going forward with new tables, you can leave them out. - -Previously: - -```java -@Table(database = TestDatabase.class) -public class ForeignInteractionModel extends TestModel1 { - - @Column - @ForeignKey( - onDelete = ForeignKeyAction.CASCADE, - onUpdate = ForeignKeyAction.CASCADE, - references = - {@ForeignKeyReference(columnName = "testmodel_id", - foreignColumnName = "name", - columnType = String.class), - @ForeignKeyReference(columnName = "testmodel_type", - foreignColumnName = "type", - columnType = String.class)}, - saveForeignKeyModel = false) - ForeignKeyContainer testModel1; -} -``` - -Now: - -```java -@Table(database = TestDatabase.class) -public class ForeignInteractionModel extends TestModel1 { - - @ForeignKey( - onDelete = ForeignKeyAction.CASCADE, - onUpdate = ForeignKeyAction.CASCADE, - saveForeignKeyModel = false) - ForeignKeyContainer testModel1; -} -``` - -The result is _significantly_ cleaner and less overhead to maintain. - -If you wish to keep old references, please keep in mind that `foreignColumnName` is now `foreignKeyColumnName`. - -## Transactions Overhaul - -In 3.0, Transactions got a serious facelift and should be easier to use and handle. -Also their logic and use are much more consolidated a focused. There is no longer -just one `TransactionManager`, rather each database has its own instance so that -operations between databases don't interfere. - -### Inserting Data -The format of how to declare them has changed: -Previously to run a transaction, you had to set it up as so: - -```java -ProcessModelInfo processModelInfo = ProcessModelInfo.withModels(models) - .result(resultReceiver) - .info(myInfo); -TransactionManager.getInstance().addTransaction(new SaveModelTransaction<>(processModelInfo)) -TransactionManager.getInstance().addTransaction(new UpdateModelListTransaction<>(processModelInfo)) -TransactionManager.getInstance().addTransaction(new DeleteModelListTransaction<>(processModelInfo)) - - -``` - -In 3.0, we have dropped the individual transaction types, use a new builder notation, -and with _every_ `Transaction` you get completion and error handling: - -```java - -FlowManager.getDatabase(AppDatabase.class) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(Model model) { - - } - }).build()) - .error(new Transaction.Error() { - @Override - public void onError(Transaction transaction, Throwable error) { - - } - }) - .success(new Transaction.Success() { - @Override - public void onSuccess(Transaction transaction) { - - } - }).build().execute(); - -``` - -One thing to note about the `Transaction.Error` is that if specified, _all_ exceptions -are caught and passed to the callback, otherwise any exception that happens in the Transaction system -gets thrown. - -You still can use the `DBBatchSaveQueue` for batch saves: - -Previously: - -```java - -TransactionManager.getInstance().saveOnSaveQueue(models); - -``` - -In 3.0: - -```java - -FlowManager.getDatabase(AppDatabase.class).getTransactionManager() - .getSaveQueue().addAll(models); - - -``` - -### Querying Data - -Previously when you queried data you have a few different classes that did almost same thing -such as `SelectListTransaction`, `BaseResultTransaction`, `QueryTransaction`, etc. -3.0 consolidates these into much simpler operations via: - -Previously: - -```java - -TransactionManager.getInstance().addTransaction(new SelectListTransaction<>(new TransactionListenerAdapter() { - @Override - public void onResultReceived(List testModels) { - - } - }, TestModel.class, condition1, condition2,..); - - -``` -In 3.0: - -```java - -database.beginTransactionAsync( - new QueryTransaction.Builder<>( - SQLite.select().from(TestModel1.class)) - .queryResult(new QueryTransaction.QueryResultCallback() { - @Override - public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult result) { - - } - }).build()).build(); - -``` - -The `QueryResultCallback` gives back a `CursorResult`, which is a wrapper around abstract -`Cursor` that lets you retrieve easily `Models` from that cursor: - -```java -List models = result.toListClose(); -TestModel1 singleModel = result.toModelClose(); -``` - -Just ensure that you close the `Cursor`. - -### Callback Changes - -With 3.0, we modified the callback for a `Transaction`. Instead of having the -3 methods: - -```java -public interface TransactionListener { - - void onResultReceived(ResultClass result); - - boolean onReady(BaseTransaction transaction); - - boolean hasResult(BaseTransaction transaction, ResultClass result); - -} -``` - -Each `Transaction` automatically gives you ability to handle callbacks: - -```java - - -FlowManager.getDatabase(AppDatabase.class) - .beginTransactionAsync(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - // do anything you want here. - } - }).build()) - .error(new Transaction.Error() { - @Override - public void onError(Transaction transaction, Throwable error) { - - } - }) - .success(new Transaction.Success() { - @Override - public void onSuccess(Transaction transaction) { - - } - }).build().execute(); - -``` - -For more usage on the new system, including the ability to roll your own `TransactionManager`, -visit [Transactions](/usage2/Transactions.md) - - -## Properties -Perhaps the most significant external change to this library is making queries, conditions, and interactions with the database much stricter and more type-safe. - -### Property -Properties replace `String` column names generated in the "$Table" classes. They also match exact case to the column name. They have methods that generate `Condition` that drastically simplify queries. (Please note the `Condition` class has moved to the `.language` package). - -Properties are represented by the interface `IProperty` which are subclassed into `Property`, `Method`, and the primitive properties (`IntProperty`, `CharProperty`, etc). - -Properties can also be represented by values via the `PropertyFactory` class, enabling values to appear first in queries: - -```java -PropertyFactory.from(5l) // generates LongProperty -PropertyFactory.from(5d) // generates DoubleProperty -PropertyFactory.from("Hello") // generates Property -PropertyFactory.from(Date.class, someDate) // generates Property -``` - -It will become apparent why this change was necessary with some examples: - -A non-simple query by SQLite standards: - -```sql -SELECT `name` AS `employee_name`, AVG(`salary`) AS `average_salary`, `order`, SUM(`salary`) as `sum_salary` - FROM `SomeTable` - WHERE `salary` > 150000 -``` - -Before: - -```java -List items = - new Select(ColumnAlias.column(SomeTable$Table.NAME).as("employee_name"), - ColumnAlias.columnsWithFunction("AVG", SomeTable$Table.SALARY).as("average_salary"), - SomeTable$Table.ORDER, - ColumnAlias.columnsWithFunction("SUM", SomeTable$Table.SALARY).as("sum_salary")) - .from(SomeTable.class) - .where(Condition.column(SomeTable$Table.SALARY).greaterThan(150000)) - .queryCustomList(SomeQueryTable.class); -``` - -Now (with static import on `SomeTable_Table` and `Method` ): - -```java -List items = - SQLite.select(name.as("employee_name"), - avg(salary).as("average_salary"), - order, - sum(salary).as("sum_salary")) - .from(SomeTable.class) - .where(salary.greaterThan(150000)) - .queryCustomList(SomeQueryTable.class); -``` - -The code instantly becomes cleaner, and reads more like an actual query. - -### Replacement of the ConditionQueryBuilder -ConditionQueryBuilder was fundamentally flawed. It represented a group of `Condition`, required a `Table`, yet extended `QueryBuilder`, meaning arbitrary `String` information could be appended to it, leading to potential for messy piece of query. - -It has been replaced with the `ConditionGroup` class. This class represents an arbitrary group of `SQLCondition` in which it's sole purpose is to group together `SQLCondition`. Even better a `ConditionGroup` itself is a `SQLCondition`, meaning it can _nest_ inside of other `ConditionGroup` to allow complicated and insane queries. - -Now you can take this: - -```sql -SELECT FROM `SomeTable` WHERE 0 < `latitude` AND (`longitude` > 50 OR `longitude` < 25) AND `name`='MyHome' -``` - -and turn it into: - -```java -SQLite.select() - .from(SomeTable.class) - .where(PropertyFactory.from(0).lessThan(SomeTable_Table.latitude)) - .and(ConditionGroup.clause() - .and(SomeTable_Table.longitude.greaterThan(50)) - .or(SomeTable_Table.longitude.lessThan(25))) - .and(SomeTable_Table.name.eq("MyHome")) -``` - -## ModelContainers -Now `ModelContainer` objects have a multitude of type-safe methods to ensure that they can convert their contained object's data into the field they associate with. What this means is that if our `Model` has a `long` field, while the data object for the `ModelContainer` has a `Integer` object. Previously, we would get a classcastexception. Now what it does is "coerce" the value into the type you need. Supported Types: -1. Integer/int -2. Double/Double -3. Boolean/boolean -4. Short/short -5. Long/long -6. Float/Float -7. String -8. Blob/byte[]/Byte[] -9. Byte/byte -10. Using TypeConverter to retrieve value safely. - -You can now `queryModelContainer` from the database to retrieve a single `Model` into `ModelContainer` format instead of into `Model` and then `ModelContainer`: - -```java - -JSONModel model = SQLite.select().from(SomeTable.class).where(SomeTable_Table.id.eq(5)).queryModelContainer(new JSONModel()); -JSONObject json = model.getData(); -// has data now -``` - -For the `toModel()` conversion/parse method from `ModelContainer` to `Model`, you can now: -1. Have `@Column` excluded from it via `excludeFromToModelMethod()` -2. include other fields in the method as well by adding the `@ContainerKey` annotation to them. - -## ModelViews -No longer do we need to specify the query for the `ModelView` in the annotation without ability to use the wrappper classes. We define a `@ModelViewQuery` field to use and then it simply becomes: - -```java -@ModelViewQuery - public static final Query QUERY = new Select(AModel_Table.time) - .from(AModel.class).where(AModel_Table.time.greaterThan(0l)); -``` - -What this means is that its easier than before to use Views. - -## Caching -I significantly revamped model caching in this release to make it easier, support more tables, and more consistent. Some of the significant changes: - -Previously you needed to extend `BaseCacheableModel` to enable model caching. No longer! The code that was there now generates in the corresponding `ModelAdapter` by setting `cachingEnabled = true` in the `@Table` annotation. - -Before - -```java -@Table(databaseName = TestDatabase.NAME) -public class CacheableModel extends BaseCacheableModel { - - @Column - @PrimaryKey(autoincrement = true) - long id; - - @Column - String name; - - @Override - public int getCacheSize() { - return 1000; - } -} -``` - -After: - -```java -@Table(database = TestDatabase.class, cachingEnabled = true, cacheSize = 1000) -public class CacheableModel extends BaseModel { - - @PrimaryKey(autoincrement = true) - long id; - - @Column - String name; -} -``` - -Also, you can now have caching objects with _multiple_ primary keys!!! - -Simply in your model class define a `@MultiCacheField` and now you can cache objects with multiple primary keys: - -```java -@Table(database = TestDatabase.class, cachingEnabled = true) -public class MultipleCacheableModel extends BaseModel { - - @MultiCacheField - public static IMultiKeyCacheConverter multiKeyCacheModel = new IMultiKeyCacheConverter() { - - @Override - @NonNull - public String getCachingKey(@NonNull Object[] values) { - return "(" + values[0] + "," + values[1] + ")"; - } - }; - - @PrimaryKey - double latitude; - - @PrimaryKey - double longitude; - -} -``` - -Please note that the field must be of type `IMultiKeyCacheConverter` in order to compile and convert correctly. You must provide one, otherwise caching will not work. Also the return caching key _must_ be unique, otherwise inconsistent results may occur from within the cache. - -## Database Modules -Now in DBFlow we have support for libraries, other subprojects, and more in general to all use DBFlow at the same time. The only requirement is that they specify an argument to `apt` in order to prevent clashes and the library loads the class during the initialization phase. To read on how to do this (fairly simply), please check it out here: ([Database Modules](https://github.com/Raizlabs/DBFlow/blob/master/usage/DatabaseModules.md)) diff --git a/usage2/Migration4Guide.md b/usage2/Migration4Guide.md deleted file mode 100644 index d90d65c75..000000000 --- a/usage2/Migration4Guide.md +++ /dev/null @@ -1,40 +0,0 @@ -# DBFlow 4.0 Migration guide - -In 4.0, DBFlow has greatly improved its internals and flexibility in this release. We have removed the `Model` restriction, rewritten the annotation processor completely in Kotlin, and more awesome improvements. - -_Major Changes In this release_ - -1. `PrimaryKey` can have `TypeConverters`, be table-based objects, and all kinds of objects. No real restrictions. - -2. `ForeignKey` have been revamped to allow `stubbedRelationship`. This replaces `ForeignKeyContainer`. - -3. `Model` interface now includes `load()` to enabled reloading very easily when fields change. - -4. All `ModelContainer` implementation + support has been removed. A few reasons pushed the removal, including implementation. Since removing support, the annotation processor is cleaner, easier to maintain, and more streamlined. Also the support for it was not up to par, and by removing it, we can focus on improving the quality of the other features. - -5. The annotation processor has been rewritten in Kotlin! By doing so, we reduced the code by ~13%. - -6. Removed the `Model` restriction on tables. If you leave out extending `BaseModel`, you _must_ interact with the `ModelAdapter`. - -7. We generate much less less code than 3.0. Combined the `_Table` + `_Adapter` into the singular `_Table` class, which contains both `Property` + all of the regular `ModelAdapter` methods. To ease the transition to 4.0, it is named `_Table` but extends `ModelAdapter`. So most use cases / interactions will not break. - -8. `Condition` are now `Operator`, this includes `SQLCondition` -> `SQLOperator`, `ConditionGroup` -> `OperatorGroup`. `Operator` are now typed and safer to use. - 1. `Operator` now also have `div`, `times`, `rem`, `plus` and `minus` methods. - -9. Property class changes: - 1. All primitive `Property` classes have been removed. We already boxed the values internally anyways so removing them cut down on method count and maintenance. - 2. `BaseProperty` no longer needs to exist, so all of it's methods now exist in `Property` - 3. `mod` method is now `rem` (remainder) method to match Kotlin 1.1's changes. - 4. `dividedBy` is now `div` to match Kotlin operators. - 5. `multipliedBy` is now `times` to match Kotlin operators. - -10. Rewrote all Unit tests to be more concise, better tested, and cleaner. - -11. A lot of bug fixes - -12. Kotlin: - 1. Added more Kotlin extensions. - 2. Most importantly you don't need to use `BaseModel`/`Model` at all anymore if you so choose. There are `Model`-like extension methods that supply the `Model` methods. - 3. Updated to version 1.1.1 - -13. RXJava1 and RXJava2 support! Can now write queries that return `Observable` and more. diff --git a/usage2/Migrations.md b/usage2/Migrations.md deleted file mode 100644 index ad73848fd..000000000 --- a/usage2/Migrations.md +++ /dev/null @@ -1,175 +0,0 @@ -# Migrations - -In this section we will discuss how migrations work, how each of the provided -migration classes work, and how to create your own custom one. - -There are two kinds of migrations that DBFlow supports: Script-based SQL files -and class annotation-based migrations. - -## How Migrations Work - -In SQL databases, migrations are used to modify or change existing database schema to adapt -to changing format or nature of stored data. In SQLite we have a limited ability -compared to SQL to modify tables and columns of an existing database. There are only -two kinds of modifications that exist: rename table and add a new column. - -In DBFlow migrations are not only used to modify the _structure_ of the database, but also other operations such as insert data into a database (for prepopulate), or add an index on a specific table. - -Migrations are only run on an existing database _except_ for the "0th" migration. Read [initial database setup](/usage2/Migrations.md#initial-database-setup) - -### Migration Classes - -We recommend placing any `Migration` inside an associated `@Database` class so it's apparent the migration is tied to it. -An example migration class: - -```java -@Database(version = 2, name = AppDatabase.NAME) -public class AppDatabase { - - public static final String NAME = "AppDatabase"; - - @Migration(version = 2, database = AppDatabase.class) - public static class Migration2 extends BaseMigration { - - @Override - public void migrate(DatabaseWrapper database) { - // run some code here - SQLite.update(Employee.class) - .set(Employee_Table.status.eq("Invalid")) - .where(Employee_Table.job.eq("Laid Off")) - .execute(database); // required inside a migration to pass the wrapper - } - } -} -``` - -The classes provide the ability to set a `priority` on the `Migration` so that an order is established. The higher the priority, that one will execute first. - -`Migration` have three methods: - 1. `onPreMigrate()` - called first, do setup, and construction here. - 2. `migrate()` -> called with the `DatabaseWrapper` specified, this is where the actual migration code should execute. - 3. `onPostMigrate()` -> perform some cleanup, or any notifications that it was executed. - -### Migration files - -DBFlow also supports `.sql` migration files. The rules on these follows must be followed: - 1. Place them in `assets/migrations/{DATABASE_NAME}/{versionNumber}.sql`. So that an example `AppDatabase` migration for version 2 resides in `assets/migrations/AppDatabase/2.sql` - 2. The file can contain any number of SQL statements - they are executed in order. Each statement must be on a single line or multiline and must end with `;` - 3. Comments are allowed as long as they appear on an individual file with standard SQLite comment syntax `--` - -### Prevent Recursive Access to the DB - -Since `Migration` occur when the database is opening, we cannot recursively access the database object in our models, SQLite wrapper statements, and other classes in DBFlow that are inside a `Migration`. - -To remedy that, DBFlow comes with support to pass the `DatabaseWrapper` into almost all places that require it: - 1. All query language `BaseQueriable` objects such as `Select`, `Insert`, `Update`, `Delete`, etc have methods that take in the `DatabaseWrapper` - 2. Any subclass of `BaseModel` (`Model` does not provide the methods for simplicity) - -### Initial Database Setup - -DBFlow supports `Migration` that run on version "0" of a database. When Android opens a `SQLiteDatabase` object, if the database is created, DBFlow calls on a `Migration` of -1 to 0th version. In this case, any `Migration` run at `version = 0` will get called. Once a database is created, this migration will not run again. So if you had an existent database at version 1, and changed version to 2, the "0th" `Migration` is not run because the old version the database would have been 1. - -## Provided Migration Classes - -In DBFlow we provide a few helper `Migration` subclasses -to provide default and easier implementation: - 1. `AlterTableMigration` - 2. `IndexMigration/IndexPropertyMigration` - 3. `UpdateTableMigration` - -### AlterTableMigration - -The _structural_ modification of a table is brought to a handy `Migration` subclass. - -It performs both of SQLite supported operations: - 1. Rename tables - 2. Add columns. - -For renaming tables, you should rename the `Model` class' `@Table(name = "{newName}")` before running -this `Migration`. The reason is that DBFlow will know -the new name only and the existing database will get caught up on it through this migration. Any new database created on a device will automatically have the new table name. - -For adding columns, we only support `SQLiteType` (all supported ones [here](https://www.sqlite.org/datatype3.html)) operations to add or remove columns. This is to enforce that the columns are created properly. If a column needs to be a `TypeConverter` column, use the database value from it. We map the associated type of the database field to a `SQLiteType` in [SQLiteType.java](/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/SQLiteType.java). So if you have a `DateConverter` that specifies a `Date` column converted to `Long`, then you should look up `Long` in the `Map`. In this case `Long` converts to `INTEGER`. - -```java - - -@Migration(version = 2, database = AppDatabase.class) -public class Migration2 extends AlterTableMigration { - - public Migration2(Class table) { - super(table); - } - - @Override - public void onPreMigrate() { - addColumn(SQLiteType.TEXT, "myColumn"); - addColumn(SQLiteType.REAL, "anotherColumn"); - } -} - -``` - -### Index Migrations - -An `IndexMigration` (and `IndexPropertyMigration`) is used to structurally activate an `Index` on the database at a specific version. See [here](/usage2/Indexing.md) for information on creating them. - -`IndexMigration` does not require an `IndexProperty` to run, while `IndexPropertyMigration` makes use of the property to run. - -An `IndexMigration`: - -```java - -@Migration(version = 2, priority = 0, database = MigrationDatabase.class) -public static class IndexMigration2 extends IndexMigration { - - public IndexMigration2(@NonNull Class onTable) { - super(onTable); - } - - @NonNull - @Override - public String getName() { - return "TestIndex"; - } -} -``` - -An `IndexPropertyMigration`: - -```java - -@Migration(version = 2, priority = 1, database = MigrationDatabase.class) -public static class IndexPropertyMigration2 extends IndexPropertyMigration { - - @NonNull - @Override - public IndexProperty getIndexProperty() { - return IndexModel_Table.index_customIndex; - } -} - -``` - -### Update Table Migration - -A simple wrapper around `Update`, provides simply a default way to update data during a migration. - -```java - - -@Migration(version = 2, priority = 2, database = MigrationDatabase.class) -public static class UpdateMigration2 extends UpdateTableMigration { - - /** - * Creates an update migration. - * - * @param table The table to update - */ - public UpdateMigration2(Class table) { - super(table); - set(MigrationModel_Table.name.eq("New Name")); - } - -} - ``` diff --git a/usage2/ModelViews.md b/usage2/ModelViews.md deleted file mode 100644 index f9eb4dcbb..000000000 --- a/usage2/ModelViews.md +++ /dev/null @@ -1,47 +0,0 @@ -# ModelViews - -A `ModelView` is a SQLite representation of a `VIEW`. Read official SQLite docs -[here](https://www.sqlite.org/lang_createview.html) for more information. - -As with SQLite a `ModelView` cannot insert, update, or delete itself as it's -read-only. It is a virtual "view" placed on top of a regular table as a prepackaged -`Select` statement. In DBFlow using a `ModelView` should feel familiar and be very simple. - -```java - -@ModelView(database = TestDatabase.class) -public class TestModelView { - - @ModelViewQuery - public static final Query QUERY = SQLite.select().from(TestModel2.class) - .where(TestModel2_Table.model_order.greaterThan(5)); - - @Column - long model_order; -} - -``` - -To specify the query that a `ModelView` creates itself with, we _must_ define -a public static final field annotated with `@ModelViewQuery`. This tells DBFlow -what field is the query. This query is used only once when the database is created -(or updated) to create the view. - - -The full list of limitations/supported types are: - 1. Only `@Column` are allowed - 2. No `@PrimaryKey` or `@ForeignKey` - 3. Supports all fields, and accessibility modifiers that `Model` support - 4. Does not support `@InheritedField`, `@InheritedPrimaryKey` - 5. Basic, type-converted, non-model `@Column`. - 6. __Cannot__: update, insert, or delete - -`ModelView` are used identical to `Model` when retrieving from the database: - -```java - -SQLite.select() - .from(TestModelView.class) - .where(...) // ETC - -``` diff --git a/usage2/Models.md b/usage2/Models.md deleted file mode 100644 index f48853b45..000000000 --- a/usage2/Models.md +++ /dev/null @@ -1,163 +0,0 @@ -# Models - -In DBFlow we dont have any restrictions on what your table class is. We do, however recommend you subclass `BaseModel` on -your highest-order base-class, which provides a default implementation for you. - -When using regular models: -```java -FlowManager.getModelAdapter(MyTable.class).save(myTableObject); -``` - -When using `BaseModel`, it is much cleaner: - -```java -myTableObject.save(); -``` - -## Columns - -We, by default, lazily look for columns. This means that they all must contain either `@PrimaryKey`, `@Column`, or `@ForeignKey` to be used in tables. - -If you wish to make it simpler and include all fields in a class, set `@Table(allFields = true)`. -However this still requires you to specify at least one `@PrimaryKey` field. You -can then explicitly ignore fields via the `@ColumnIgnore` annotation. - -Columns can be `public`, package-private, or `private`. -`private` fields __must__ come with `public` java-bean-style getters and setters. - -Here is an example of a "nice" `private` field: - -```java -@Table(database = AppDatabase.class) -public class Dog { - - @PrimaryKey - private String name; - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - -} - -``` - -Columns have a wide-range of supported types in the `Model` classes: -**Supported Types**: - 1. all java primitives including `char`,`byte`, `short`, and `boolean`. - 2. All java boxed primitive classes - 3. String, Date, java.sql.Date, Calendar, Blob, Boolean - 4. Custom data types via a [TypeConverter](/usage2/TypeConverters.md) - 5. `Model` as fields, but only as `@PrimaryKey` and/or `@ForeignKey` - -**Unsupported Types**: - 1. `List` : List columns are not supported and not generally proper for a relational database. However, you can get away with a non-generic `List` column via a `TypeConverter`. But again, avoid this if you can. - 2. Anything that is generically typed (even with an associated `TypeConverter`). If you need to include the field, subclass the generic object and provide a `TypeConverter`. - -## Inherited Columns - -Since we don't require extension on `BaseModel` directly, tables can extend non-model classes and inherit their fields directly (given proper accessibility) via the `@InheritedColumn` annotation (or `@InheritedPrimaryKey` for primary keys): - -```java - -@Table(database = AppDatabase.class, - inheritedColumns = {@InheritedColumn(column = @Column, fieldName = "name"), - @InheritedColumn(column = @Column, fieldName = "number")}, - inheritedPrimaryKeys = {@InheritedPrimaryKey(column = @Column, - primaryKey = @PrimaryKey, - fieldName = "inherited_primary_key")}) -public class InheritorModel extends InheritedModel implements Model { - -``` - -## Primary Keys - -DBFlow supports multiple primary keys, right out of the box. Simply create a table with multiple `@PrimaryKey`: - -```java -@Table(database = AppDatabase.class) -public class Dog extends BaseModel { - - @PrimaryKey - String name; - - @PrimaryKey - String breed; - -} - -``` - -If we want an auto-incrementing key, you specify `@PrimaryKey(autoincrement = true)`, but only one of these kind can exist in a table and you cannot mix with regular primary keys. - -## Unique Columns - -DBFlow has support for SQLite `UNIQUE` constraint (here for documentation)[http://www.tutorialspoint.com/sqlite/sqlite_constraints.htm]. - -Add `@Unique` annotation to your existing `@Column` and DBFlow adds it as a constraint when -the database table is first created. This means that once it is created you should not change or modify this. - -We can _also_ support multiple unique clauses in order to ensure any combination of fields are unique. For example: - -To generate this in the creation query: -```sqlite -UNIQUE('name', 'number') ON CONFLICT FAIL, UNIQUE('name', 'address') ON CONFLICT ROLLBACK -``` -We declare the annotations as such: - -```java - -@Table(database = AppDatabase.class, - uniqueColumnGroups = {@UniqueGroup(groupNumber = 1, uniqueConflict = ConflictAction.FAIL), - @UniqueGroup(groupNumber = 2, uniqueConflict = ConflictAction.ROLLBACK)) -public class UniqueModel { - - @PrimaryKey - @Unique(unique = false, uniqueGroups = {1,2}) - String name; - - @Column - @Unique(unique = false, uniqueGroups = 1) - String number; - - @Column - @Unique(unique = false, uniqueGroups = 2) - String address; - -} - -``` - -The `groupNumber` within each defined `uniqueColumnGroups` with an associated `@Unique` column. We need to specify `unique=false` for any column used in a group so we expect the column to be part of a group. If true as well, the column will _also_ alone be unique. - -## Default Values - -DBFlow supports default values in a slighty different way that SQLite does. Since we do not know -exactly the intention of missing data when saving a `Model`, since we group all fields, `defaultValue` specifies -a value that we replace when saving to the database when the value of the field is `null`. - -This feature only works on Boxed primitive and the `DataClass` equivalent of objects (such as from TypeConverter), such as String, Integer, Long, Double, etc. -__Note__: If the `DataClass` is a `Blob`, unfortunately this will not work. -For `Boolean` classes, use "1" for true, "0" for false. - -```java - -@Column(defaultValue = "55") -Integer count; - -@Column(defaultValue = "\"this is\"") -String test; - -@Column(defaultValue = "1000L") -Date date; - -@Column(defaultValue = "1") -Boolean aBoolean; - -``` - -DBFlow inserts it's literal value into the `ModelAdapter` for the table so any `String` must be escaped. diff --git a/usage2/MultipleModules.md b/usage2/MultipleModules.md deleted file mode 100644 index 69c963f83..000000000 --- a/usage2/MultipleModules.md +++ /dev/null @@ -1,48 +0,0 @@ -# Multiple Database Modules - -In apps that want to share DBFlow across multiple modules or when developing a library -module that uses DBFlow, we have to provide a little extra configuration to properly -ensure that all database classes are accounted for. - -It's directly related to the fact that annotation processors are isolated between projects -and are not shared. - -In order to add support for multiple modules, in each and every library/subproject that uses -a DBFlow instance, you must add an APT argument (using the [android-apt plugin](https://bitbucket.org/hvisser/android-apt)) to its `build.gradle`: - -```java -apt { - arguments { - targetModuleName 'SomeUniqueModuleName' - } -} -``` - -or for if you use Kotlin, KAPT: - -```java -kapt { - generateStubs = true - arguments { - arg("targetModuleName", "SomeUniqueModuleName") - } -} -``` - - -By passing the targetModuleName, we append that to the `GeneratedDatabaseHolder` class name to create the `{targetModuleName}GeneratedDatabaseHolder` module. __Note__: Specifying this in code means -you need to specify the module when initializing DBFlow: - -From previous sample code, we recommend initializing the specific module inside your library, -to prevent developer error. __Note__: Multiple calls to `FlowManager` will not adversely -affect DBFlow. If DBFlow is already initialized, we append the module to DBFlow if and only if it does not already exist. - -```java - -public void initialize(Context context) { - FlowManager.init(new FlowConfig.Builder(context) - .addDatabaseHolder(SomeUniqueModuleNameGeneratedDatabaseHolder.class) - .build()); -} - -``` diff --git a/usage2/Observability.md b/usage2/Observability.md deleted file mode 100644 index 9655381f8..000000000 --- a/usage2/Observability.md +++ /dev/null @@ -1,154 +0,0 @@ -# Observability - -DBFlow provides a flexible way to observe changes on models and tables in this library. - -By default, DBFlow utilizes the [`ContentResolver`](https://developer.android.com/reference/android/content/ContentResolver.html) -to send changes through the android system. We then can utilize [`ContentObserver`](http://developer.android.com/reference/android/database/ContentObserver.html) to listen for these changes via the `FlowContentObserver`. - -Also, DBFlow also supports direct [model notification](/usage2/Observability.md#direct-changes) via a custom `ModelNotifier`. - -## FlowContentObserver - -The content observer converts each model passed to it into `Uri` format that describes the `Action`, primary keys, and table of the class that changed. - -A model: -```kotlin - -@Table(database = AppDatabase.class) -class User(@PrimaryKey var id: Int = 0, @Column var name: String = "") - -``` - -with data: -```kotlin - -User(55, "Andrew Grosner").delete() - -``` - -converts to: - -``` -dbflow://%60User%60?%2560id%2560=55#DELETE -``` - -Then after we register a `FlowContentObserver`: - -```java - -FlowContentObserver observer = new FlowContentObserver(); - -observer.registerForContentChanges(context, User.class); - -// do something here -// unregister when done -observer.unregisterForContentChanges(context); - -``` - -## Model Changes - -It will now receive the `Uri` for that table. Once we have that, we can register for model changes on that content: - -```java - -observer.addModelChangeListener(new OnModelStateChangedListener() { - @Override - public void onModelStateChanged(@Nullable Class table, BaseModel.Action action, @NonNull SQLOperator[] primaryKeyValues) { - // do something here - } -}); - - -``` -The method will return the `Action` which is one of: - 1. `SAVE` (will call `INSERT` or `UPDATE` as well if that operation was used) - 2. `INSERT` - 3. `UPDATE` - 4. `DELETE` - -The `SQLOperator[]` passed back specify the primary column and value pairs that were changed for the model. - -If we want to get less granular and just get notifications when generally a table changes, read on. - -## Register for Table Changes - -Table change events are similar to `OnModelStateChangedListener`, except that they only specify the table and action taken. These get called for any action on a table, including granular model changes. We recommend batching those events together, which we describe in the next section. - -```java - -addOnTableChangedListener(new OnTableChangedListener() { - @Override - public void onTableChanged(@Nullable Class tableChanged, BaseModel.Action action) { - // perform an action. May get called many times! Use batch transactions to combine them. - } -}); - -``` - -## Batch Up Many Events - -Sometimes we're modifying tens or hundreds of items at the same time and we do not wish to get notified for _every_ one but only once for each _kind_ of change that occurs. - -To batch up the notifications so that they fire all at once, we use batch transactions: - -```java - -FlowContentObserver observer = new FlowContentObserver(); - -observer.beginTransaction(); - -// save, modify models here -for(User user: users) { - users.save(); -} - -observer.endTransactionAndNotify(); // callback batched - -``` - -Batch interactions will store up all unique `Uri` for each action (these include `@Primary` key of the `Model` changed). When `endTransactionAndNotify()` is called, -all those `Uri` are called in the `onChange()` method from the `FlowContentObserver` as expected. - -If we are using `OnTableChangedListener` callbacks, then by default we will receive one callback per `Action` per table. If we wish to only receive a single callback, set `setNotifyAllUris(false)`, which will make the `Uri` all only specify `CHANGE`. - -# Direct Changes - -DBFlow also supports direct observability on model changes rather than convert those models into `Uri` and have to decipher what has changed. - -To set up direct changes we override the default `ModelNotifier`: - -```java - -FlowManager.init(FlowConfig.Builder(context) - .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase.class) - .modelNotifier(DirectModelNotifier.get()) - .build()).build()); - -``` - -We must use the shared instance of the `DirectModelNotifier` since if we do not, your listeners will not receive callbacks. - -Next register for changes on the `DirectModelNotifier`: -```java - -DirectModelNotifier.get().registerForModelChanges(User.class, new ModelChangedListener() { - @Override - public void onModelChanged(User model, BaseModel.Action action) { - // react to model changes - } - - @Override - public void onTableChanged(BaseModel.Action action) { - // react to table changes. - } - };) - -``` - -Then unregister your model change listener when you don't need it anymore (to prevent memory leaks): - -```java -DirectModelNotifier.get().unregisterForModelChanges(Userr.class, modelChangedListener); - -``` diff --git a/usage2/QueryModels.md b/usage2/QueryModels.md deleted file mode 100644 index 8fd081423..000000000 --- a/usage2/QueryModels.md +++ /dev/null @@ -1,107 +0,0 @@ -# Query Models - -A `QueryModel` is purely an ORM object that maps rows from a `Cursor` into -a `Model` such that when loading from the DB, we can easily use the data from it. - -We use a different annotation, `@QueryModel`, to define it separately. These -do not allow for modifications in the DB, rather act as a marshal agent out of the DB. - -## Define a QueryModel - -For this example, we have a list of employees that we want to gather the average salary -for each position in each department from our company. - -We defined an `Employee` table: - -```java - -@Table(database = AppDatabase.class) -public class EmployeeModel { - - @PrimaryKey - String uid; - - @Column - long salary; - - @Column - String name; - - @Column - String title; - - @Column - String department; -} - -``` - -We need someway to retrieve the results of this query, since we want to avoid -dealing with the `Cursor` directly. We can use a SQLite query with our existing models, but -we have no way to map it currently to our tables, since the query returns new Columns -that do not represent any existing table: - -```java - -SQLite.select(EmployeeModel_Table.department, - Method.avg(EmployeeModel_Table.salary.as("average_salary")), - EmployeeModel_Table.title) - .from(EmployeeModel.class) - .groupBy(EmployeeModel_Table.department, EmployeeModel_Table.title); - -``` - -So we must define a `QueryModel`, representing the results of the query: - -```java -@QueryModel(database = AppDatabase.class) -public class AverageSalary { - - @Column - String title; - - @Column - long average_salary; - - @Column - String department; -} -``` - -And adjust our query to handle the new output: - -```java - -SQLite.select(EmployeeModel_Table.department, - Method.avg(EmployeeModel_Table.salary.as("average_salary")), - EmployeeModel_Table.title) - .from(EmployeeModel.class) - .groupBy(EmployeeModel_Table.department, EmployeeModel_Table.title) - .async() - .queryResultCallback(new QueryTransaction.QueryResultCallback() { - @Override - public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult tResult) { - List queryModels = tResult.toCustomListClose(AverageSalary.class); - - // do something with the result - } - }).execute(); - -``` - -## Query Model Support - -`QueryModel` support only a limited subset of `Model` features. - -If you use the optional base class of `BaseQueryModel`, - Modifications such as `insert()`, `update()`, `save()`, and `delete()` will throw - an `InvalidSqlViewOperationException`. Otherwise, `RetrievalAdapter` do not - contain modification methods. - -They support `allFields` and inheritance and visibility modifiers as defined by [Models](/usage2/Models.md). - -`QueryModel` **do not** support: - 1. `InheritedField`/`InheritedPrimaryKey` - 2. `@PrimaryKey`/`@ForeignKey` - 3. caching - 4. changing "useBooleanGetterSetters" for private boolean fields. diff --git a/usage2/RXSupport.md b/usage2/RXSupport.md deleted file mode 100644 index c9e365377..000000000 --- a/usage2/RXSupport.md +++ /dev/null @@ -1,170 +0,0 @@ -# RXJava Support - -RXJava support in DBFlow is an _incubating_ feature and likely to change over time. We support both RX1 and RX2 and have made the extensions + DBFlow compatibility almost identical - save for the changes and where it makes sense in each version. - -Currently it supports - 1. `Insert`, `Update`, `Delete`, `Set`, `Join`, and all wrapper query mechanisms by wrapping them in `rx()` - 2. Single + `List` model `save()`, `insert()`, `update()`, and `delete()`. - 3. Streaming a set of results from a query - 4. Observing on table changes for specific `ModelQueriable` and providing ability to query from that set repeatedly as needed. - 5. Kotlin extension methods in a separate artifact that enhance the conversion. - -## Getting Started - -Add the separate packages to your project: -```groovy - -dependencies { - // RXJava1 - compile "com.github.Raizlabs.DBFlow:dbflow-rx:${dbflow_version}" - - // optional, for use with Kotlin as a nice companion. - compile "com.github.Raizlabs.DBFlow:dbflow-rx-kotlinextensions:${dbflow_version}" - - // RXJava2 - compile "com.github.Raizlabs.DBFlow:dbflow-rx2:${dbflow_version}" - - // optional, for use with Kotlin as a nice companion. - compile "com.github.Raizlabs.DBFlow:dbflow-rx2-kotlinextensions:${dbflow_version}" -} - -``` - -## Wrapper Language -Using the classes is as easy as wrapping all SQL wrapper calls with `RXSQLite.rx()` (Kotlin we supply extension method): - -Before: -```java - -List list = SQLite.select() - .from(MyTable.class) - .queryList(); - -``` - -After: - -```java - -RXSQLite.rx( - SQLite.select().from(MyTable.class)) - .queryList() - .subscribe((list) -> { - - }); - -``` - -or with Kotlin + extension methods: -```kotlin - - select.from(MyTable::class.java) - .rx() - .list { list -> - - } - -``` - -## Model operations -To make the transition as smoothest as possible, we've provided a `BaseRXModel` which replaces `BaseModel` for convenience in the RX space. - -```kotlin - -class Person(@PrimaryKey var id: Int = 0, @Column var name: String? = "") : BaseRXModel - -``` - -Operations are as easy as: -```java - -new Person(5, "Andrew Grosner") - .insert() - .subscribe((rowId) -> { - - }); - -``` - -or with Kotlin+extensions: -```kotlin - -Person(5, "Andrew Grosner") - .insert { rowId -> - - } - -``` - -## Query Stream - -We can use RX to stream the result set, one at a time from the `ModelQueriable` using -the method `queryStreamResults()`: - -```java - -RXSQLite.rx( - SQLite.select() - .from(TestModel1.class)) - .queryStreamResults() - .subscribe((model) -> { - - }); - -``` - -## Kotlin Support - -Most of the support mirrors [kotlin support](/usage2/KotlinSupport.md) with a few -minor changes. - -Extension properties/methods include: - 1. `rx()` extension method making it super easy to integrate RX. - 2. `RXModelQueriable.streamResults` - stream results one at time to a `Subscription` - 3. `list`, `result`,`streamResults`, `cursorResult`,`statement`, `hasData`, `cursor`, and `count` all provide a method lambda that is called within a `Subscription`. - -```kotlin - -select from MyTable::class - where (MyTable.name `is` "Good") - list { list -> // - - } - -``` - -which is the same with RX as: - -```kotlin - -(select.from(MyTable::class.java) - .where(MyTable.name `is` "Good")) - .rx() - .list { list -> - - } - -``` - - -Or if we want to get pretty with `BaseRXModel` + extensions: - -```kotlin - -Person("Somebody").save { success -> - // do something -} - -Person("Somebody").update { success -> - // do something -} - -Person("Somebody").insert { rowId -> - // do something -} - -Person("Somebody").delete { success -> - // do something -} - -``` diff --git a/usage2/Relationships.md b/usage2/Relationships.md deleted file mode 100644 index ba69ce54d..000000000 --- a/usage2/Relationships.md +++ /dev/null @@ -1,227 +0,0 @@ -# Relationships - -We can link `@Table` in DBFlow via 1-1, 1-many, or many-to-many. For 1-1 we use -`@PrimaryKey`, for 1-many we use `@OneToMany`, and for many-to-many we use the `@ManyToMany` annotation. - - -## One To One - -DBFlow supports multiple `@ForeignKey` right out of the box as well (and for the most part, they can also be `@PrimaryKey`). - -```java -@Table(database = AppDatabase.class) -public class Dog extends BaseModel { - - @PrimaryKey - String name; - - @ForeignKey(tableClass = Breed.class) - @PrimaryKey - String breed; - - @ForeignKey - Owner owner; -} - -``` - -`@ForeignKey` can only be a subset of types: - 1. `Model` - 2. Any field not requiring a `TypeConverter`. If not a `Model` or a table class, you _must_ specify the `tableClass` it points to. - 3. Cannot inherit `@ForeignKey` from non-model classes (see [Inherited Columns](/usage2/Models.md#inherited-columns)) - -If you create a circular reference (i.e. two tables with strong references to `Model` as `@ForeignKey` to each other), read on. - -## Stubbed Relationships - -For efficiency reasons we recommend specifying `@ForeignKey(stubbedRelationship = true)`. What this will do is only _preset_ the primary key references into a table object. All other fields will not be set. If you need to access the full object, you will have to call `load()` for `Model`, or use the `ModelAdapter` to load the object from the DB. - -From our previous example of `Dog`, instead of using a `String` field for **breed** -we recommended by using a `Breed`. It is nearly identical, but the difference being -we would then only need to call `load()` on the reference and it would query the `Breed` -table for a row with the `breed` id. This also makes it easier if the table you -reference has multiple primary keys, since DBFlow will handle the work for you. - -Multiple calls to `load()` will query the DB every time, so call when needed. Also if you don't specify `@Database(foreignKeyConstraintsEnforced = true)`, calling `load()` may not have any effect. Essentially without enforcing `@ForeignKey` at a SQLite level, you can end up with floating key references that do not exist in the referenced table. - -In normal circumstances, for every load of a `Dog` object from the database, -we would also do a load of related `Owner`. This means that even if multiple `Dog` say (50) -all point to same owner we end up doing 2x retrievals for every load of `Dog`. Replacing -that model field of `Owner` with a stubbed relationship prevents the extra N lookup time, -leading to much faster loads of `Dog`. - -__Note__: using stubbed relationships also helps to prevent circular references that can -get you in a `StackOverFlowError` if two tables strongly reference each other in `@ForeignKey`. - -Our modified example now looks like this: - -```java -@Table(database = AppDatabase.class) -public class Dog extends BaseModel { - - @PrimaryKey - String name; - - @ForeignKey(stubbedRelationship = true) - @PrimaryKey - Breed breed; // tableClass only needed for single-field refs that are not Model. - - @ForeignKey(stubbedRelationship = true) - Owner owner; -} - -``` - -## One To Many - -In DBFlow, `@OneToMany` is an annotation that you provide to a method in your `Model` class that will allow management of those objects during CRUD operations. -This can allow you to combine a relationship of objects to a single `Model` to happen together on load, save, insert, update, and deletion. - -```java - -@Table(database = ColonyDatabase.class) -public class Queen extends BaseModel { - - @PrimaryKey(autoincrement = true) - long id; - - @Column - String name; - - @ForeignKey(saveForeignKeyModel = false) - Colony colony; - - List ants; - - @OneToMany(methods = {OneToMany.Method.ALL}, _variableName = "ants") - public List getMyAnts() { - if (ants == null || ants.isEmpty()) { - ants = SQLite.select() - .from(Ant.class) - .where(Ant_Table.queenForeignKeyContainer_id.eq(id)) - .queryList(); - } - return ants; - } -} - -``` - -### Custom ForeignKeyReferences -When simple `@ForeignKey` annotation is not enough, you can manually specify references for your table: - -```java - -@ForeignKey(saveForeignKeyModel = false, -references = {@ForeignKeyReference(columnName = "colony", foreignKeyColumnName = "id")}) -Colony colony; - -``` - -By default not specifying references will take each field and append "${foreignKeyFieldName}_${ForeignKeyReferenceColumnName}" to make the reference column name. So by default the previous example would use `colony_id` without references. With references it becomes `colony`. - -## Many To Many - - -In DBFlow many to many is done via source-gen. A simple table: - -```java - -@Table(database = TestDatabase.class) -@ManyToMany(referencedTable = Follower.class) -public class User extends BaseModel { - - @PrimaryKey - String name; - - @PrimaryKey - int id; - -} - -``` - -Generates a `@Table` class named `User_Follower`, which DBFlow treats as if you -coded the class yourself!: - -```java - -@Table( - database = TestDatabase.class -) -public final class User_Follower extends BaseModel { - @PrimaryKey( - autoincrement = true - ) - long _id; - - @ForeignKey( - saveForeignKeyModel = false - ) - Follower follower; - - @ForeignKey( - saveForeignKeyModel = false - ) - User user; - - public final long getId() { - return _id; - } - - public final Followers getFollower() { - return follower; - } - - public final void setFollower(Follower param) { - follower = param; - } - - public final Users getUser() { - return user; - } - - public final void setUser(User param) { - user = param; - } -} - -``` - -This annotation makes it very easy to generate "join" tables for you to use in the app for a ManyToMany relationship. It only generates the table you need. To use it you must reference it in code as normal. - -_Note_: This annotation is only a helper to generate tables that otherwise you -would have to write yourself. It is expected that management still is done by you, the developer. - -### Custom Column Names - -You can change the name of the columns that are generated. By default they are simply -lower case first letter version of the table name. - -`referencedTableColumnName` -> Refers to the referenced table. -`thisTableColumnName` -> Refers to the table that is creating the reference. - -### Multiple ManyToMany - -You can also specify `@MultipleManyToMany` which enables you to define more -than a single `@ManyToMany` relationship on the table. - -A class can use both: - -```java -@Table(database = TestDatabase.class) -@ManyToMany(referencedTable = TestModel1.class) -@MultipleManyToMany({@ManyToMany(referencedTable = TestModel2.class), - @ManyToMany(referencedTable = com.raizlabs.android.dbflow.test.sql.TestModel3.class)}) -public class ManyToManyModel extends BaseModel { - - @PrimaryKey - String name; - - @PrimaryKey - int id; - - @Column - char anotherColumn; -} -``` diff --git a/usage2/Retrieval.md b/usage2/Retrieval.md deleted file mode 100644 index e398e82ec..000000000 --- a/usage2/Retrieval.md +++ /dev/null @@ -1,116 +0,0 @@ -# Retrieval - -DBFlow provides a few ways to retrieve information from the database. Through -the `Model` classes we can map this information to easy-to-use objects. - -DBFlow provides a few different ways to retrieve information from the database. We -can retrieve synchronously or asynchronous (preferred). - -We can also use `ModelView` ([read here](/usage2/ModelViews.md)) and `@Index` ([read here](/usage2/Indexing.md)) to perform faster retrieval on a set of data constantly queried. - -## Synchronous Retrieval - -Using the [SQLite query language](/usage2/SQLiteWrapperLanguage.md) we can retrieve -data easily and expressively. To perform it synchronously: - - -```java - -// list -List employees = SQLite.select() - .from(Employee.class) - .queryList(); - -// single result, we apply a limit(1) automatically to get the result even faster. -Employee employee = SQLite.select() - .from(Employee.class) - .where(Employee_Table.name.eq("Andrew Grosner")) - .querySingle(); - -// get a custom list -List employees = SQLite.select() - .from(Employee.class) - .queryCustomList(AnotherTable.class); - -// custom object -AnotherTable anotherObject = SQLite.select() - .from(Employee.class) - .where(Employee_Table.name.eq("Andrew Grosner")) - .queryCustomSingle(AnotherTable.class); - -``` - -To query custom objects or lists, see how to do so in [QueryModel](/usage2/QueryModel.md). - -Also you can query a `FlowCursorList`/`FlowTableList` from a query easily -via `queryCursorList()` and the `queryTableList()` methods. To see more on these, -go to [Flow Lists](/usage2/FlowLists.md). - - -## Asynchronous Retrieval - -DBFlow provides the very-handy `Transaction` system that allows you to place all -calls to the DB in a queue. Using this system, we recommend placing retrieval queries -on this queue to help prevent locking and threading issues when using a database. - -A quick sample of retrieving data asyncly: - -```java - -SQLite.select() - .from(TestModel1.class) - .where(TestModel1_Table.name.is("Async")) - .async() - .queryResultCallback(new QueryTransaction.QueryResultCallback() { - @Override - public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult tResult) { - - } - }).execute(); - -``` - -This is fundamentally equal to: - -```java - - -FlowManager.getDatabaseForTable(TestModel1.class) - .beginTransactionAsync(new QueryTransaction.Builder<>( - SQLite.select() - .from(TestModel1.class) - .where(TestModel1_Table.name.is("Async"))) - .queryResult(new QueryTransaction.QueryResultCallback() { - @Override - public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult tResult) { - - } - }).build()) -.build().execute(); - -``` - -The first example in this section is more of a convenience for (2). - -By default the library uses the `DefaultTransactionManager` which utilizes -a `DefaultTransactionQueue`. This queue is essentially an ordered queue that -executes FIFO (first-in-first-out) and blocks itself until new `Transaction` are added. - -If you wish to customize and provide a different queue (or map it to an existing system), read up on [Transactions](/usage2/StoringData.md). - - -Compared to pre-3.0 DBFlow, this is a breaking change from the old, priority-based -queue system. The reason for this change was to simplify the queuing system and -allow other systems to exist without confusing loss of functionality. To keep the old -system read [Transactions](/usage2/StoringData.md). - -## Faster Retrieval - -In an effort to squeeze out more speed at the potential cost of flexibility, DBFlow provides a -couple ways to optimize loads from the DB. If you do not wish to use caching but wish -to speed conversion from `Cursor` to `Model`, read on. - - If you simply retrieve a `List` of `Model` -without any projection from your DB, you can take advantage of 2 features: - 1. `@Table(orderedCursorLookUp = true)` -> We do not call `Cursor.getColumnIndex()` and assume that the `Cursor` is ordered by column declarations in the class. - 2. `@Table(assignDefaultValuesFromCursor = false)` -> We do not expect to reuse an object from the DB (or care) if the corresponding fields aren't assigned a value when missing from the `Cursor`. diff --git a/usage2/SQLCipherSupport.md b/usage2/SQLCipherSupport.md deleted file mode 100644 index 83bfee79e..000000000 --- a/usage2/SQLCipherSupport.md +++ /dev/null @@ -1,57 +0,0 @@ -# SQLCipher Support - -As of 3.0.0-beta2+, DBFlow now supports [SQLCipher](https://www.zetetic.net/sqlcipher/) fairly easily. - -To add the library add the library to your `build.gradle` with same version you are using with the rest of the library. - -```groovy -dependencies { - compile "com.github.Raizlabs.DBFlow:dbflow-sqlcipher:${version}" - compile "net.zetetic:android-database-sqlcipher:${sqlcipher_version}@aar" - -} -``` - -You also need to add the Proguard rule: -``` --keep class net.sqlcipher.** { *; } --dontwarn net.sqlcipher.** -``` - -Next, you need to subclass the provided `SQLCipherOpenHelper` (taken from test files): - -```java -public class SQLCipherHelperImpl extends SQLCipherOpenHelper { - - public SQLCipherHelperImpl(DatabaseDefinition databaseDefinition, DatabaseHelperListener listener) { - super(databaseDefinition, listener); - } - - @Override - protected String getCipherSecret() { - return "dbflow-rules"; - } -} -``` - -_Note:_ that the constructor with `DatabaseDefinition` and `DatabaseHelperListener` is required. - -Then in your application class when initializing DBFlow: - -```java - -FlowManager.init(new FlowConfig.Builder(this) - .addDatabaseConfig( - new DatabaseConfig.Builder(CipherDatabase.class) - .openHelper(new DatabaseConfig.OpenHelperCreator() { - @Override - public OpenHelper createHelper(DatabaseDefinition databaseDefinition, DatabaseHelperListener helperListener) { - return new SQLCipherHelperImpl(databaseDefinition, helperListener); - } - }) - .build()) - .build()); - -``` - -And that's it. You're all set to start using SQLCipher! diff --git a/usage2/SQLiteWrapperLanguage.md b/usage2/SQLiteWrapperLanguage.md deleted file mode 100644 index fb24b014b..000000000 --- a/usage2/SQLiteWrapperLanguage.md +++ /dev/null @@ -1,384 +0,0 @@ -# SQLite Wrapper Language - -DBFlow's SQLite wrapper language attempts to make it as easy as possible to -write queries, execute statements, and more. - -We will attempt to make this doc comprehensive, but reference the SQLite language -for how to formulate queries, as DBFlow follows it as much as possible. - -## SELECT - -The way to query data, `SELECT` are started by: - -```java - -SQLite.select().from(SomeTable.class) - -``` - -### Projections - -By default if no parameters are specified in the `select()` query, we use the `*` wildcard qualifier, -meaning all columns are returned in the results. - -To specify individual columns, you _must_ use `Property` variables. -These get generated when you annotate your `Model` with columns, or created manually. - -```java - -SQLite.select(Player_Table.name, Player_Table.position) - .from(Player.class) - -``` - -To specify methods such as `COUNT()` or `SUM()` (static import on `Method`): - - -```java - -SQLite.select(count(Employee_Table.name), sum(Employee_Table.salary)) - .from(Employee.class) - -``` - -Translates to: - -```sqlite - -SELECT COUNT(`name`), SUM(`salary`) FROM `Employee`; - -``` - -There are more handy methods in `Method`. - -### Operators - -DBFlow supports many kinds of operations. They are formulated into a `OperatorGroup`, -which represent a set of `SQLOperator` subclasses combined into a SQLite conditional piece. -`Property` translate themselves into `SQLOperator` via their conditional methods such as -`eq()`, `lessThan()`, `greaterThan()`, `between()`, `in()`, etc. - -They make it very easy to construct concise and meaningful queries: - -```java - -int taxBracketCount = SQLite.select(count(Employee_Table.name)) - .from(Employee.class) - .where(Employee_Table.salary.lessThan(150000)) - .and(Employee_Table.salary.greaterThan(80000)) - .count(); - -``` - -Translates to: - -```sqlite - -SELECT COUNT(`name`) FROM `Employee` WHERE `salary`<150000 AND `salary`>80000; - -``` - -DBFlow supports `IN`/`NOT IN` and `BETWEEN` as well. - -A more comprehensive list of operations DBFlow supports and what they translate to: - - 1. is(), eq() -> = - 2. isNot(), notEq() -> != - 3. isNull() -> IS NULL / isNotNull() -> IS NOT NULL - 4. like(), glob() - 5. greaterThan(), greaterThanOrEqual(), lessThan(), lessThanOrEqual() - 6. between() -> BETWEEN - 7. in(), notIn() - -#### Nested Conditions - -To create nested conditions (in parenthesis more often than not), just include -an `OperatorGroup` as a `SQLOperator` in a query: - - -```java - -SQLite.select() - .from(Location.class) - .where(Location_Table.latitude.eq(home.getLatitude())) - .and(OperatorGroup.clause() - .and(Location_Table.latitude - .minus(PropertyFactory.from(home.getLatitude()) - .eq(1000L)))) - -``` - -Translates to: - -```sqlite - -SELECT * FROM `Location` WHERE `latitude`=45.05 AND (`latitude` - 45.05) = 1000 - -``` - -#### Nested Queries - -To create a nested query simply include it as a `Property` via `PropertyFactory.from(BaseQueriable)`: - -```java - -.where(PropertyFactory.from(SQLite.select().from(...).where(...)) - -``` - -This appends a `WHERE (SELECT * FROM {table} )` to the query. - -### JOINS - -For reference, ([JOIN examples](http://www.tutorialspoint.com/sqlite/sqlite_using_joins.htm)). - -`JOIN` statements are great for combining many-to-many relationships. -If your query returns non-table fields and cannot map to an existing object, -see about [query models](/usage2/QueryModels.md) - -For example we have a table named `Customer` and another named `Reservations`. - -```SQL -SELECT FROM `Customer` AS `C` INNER JOIN `Reservations` AS `R` ON `C`.`customerId`=`R`.`customerId` -``` - -```java -// use the different QueryModel (instead of Table) if the result cannot be applied to existing Model classes. -List customers = new Select() - .from(Customer.class).as("C") - .join(Reservations.class, JoinType.INNER).as("R") - .on(Customer_Table.customerId - .withTable(NameAlias.builder("C").build()) - .eq(Reservations_Table.customerId.withTable("R")) - .queryCustomList(CustomTable.class); -``` - -The `IProperty.withTable()` method will prepend a `NameAlias` or the `Table` alias to the `IProperty` in the query, convenient for JOIN queries: - -```sqlite -SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT - ON COMPANY.ID = DEPARTMENT.EMP_ID -``` - -in DBFlow: - -```java -SQLite.select(Company_Table.EMP_ID, Company_Table.DEPT) - .from(Company.class) - .leftOuterJoin(Department.class) - .on(Company_Table.ID.withTable().eq(Department_Table.EMP_ID.withTable())) -``` - -### Order By - -```java - -// true for 'ASC', false for 'DESC' -SQLite.select() - .from(table) - .where() - .orderBy(Customer_Table.customer_id, true) - - SQLite.select() - .from(table) - .where() - .orderBy(Customer_Table.customer_id, true) - .orderBy(Customer_Table.name, false) -``` - -### Group By - -```java -SQLite.select() - .from(table) - .groupBy(Customer_Table.customer_id, Customer_Table.customer_name) -``` - -### HAVING - -```java -SQLite.select() - .from(table) - .groupBy(Customer_Table.customer_id, Customer_Table.customer_name)) - .having(Customer_Table.customer_id.greaterThan(2)) -``` - -### LIMIT + OFFSET - -```java -SQLite.select() - .from(table) - .limit(3) - .offset(2) -``` - -## UPDATE - -DBFlow supports two kind of UPDATE: - 1. `Model.update()` - 2. `SQLite.update()` - -For simple `UPDATE` for a single or few, concrete set of `Model` stick with (1). -For powerful multiple `Model` update that can span many rows, use (2). In this -section we speak on (2). **Note:** if using model caching, you'll need to clear it out -post an operation from (2). - - -```sql - -UPDATE Ant SET type = 'other' WHERE male = 1 AND type = 'worker'; -``` - -Using DBFlow: - -```java - -// Native SQL wrapper -SQLite.update(Ant.class) - .set(Ant_Table.type.eq("other")) - .where(Ant_Table.type.is("worker")) - .and(Ant_Table.isMale.is(true)) - .async() - .execute(); // non-UI blocking -``` - -The `Set` part of the `Update` supports different kinds of values: - 1. `ContentValues` -> converts to key/value as a `SQLOperator` of `is()`/`eq()` - 2. `SQLOperator`, which are grouped together as part of the `SET` statement. - -## DELETE - -`DELETE` queries in DBFlow are similiar to `Update` in that we have two kinds: - - 1. `Model.delete()` - 2. `SQLite.delete()` - -For simple `DELETE` for a single or few, concrete set of `Model` stick with (1). -For powerful multiple `Model` deletion that can span many rows, use (2). In this -section we speak on (2). **Note:** if using model caching, you'll need to clear it out -post an operation from (2). - - -```java - -// Delete a whole table -Delete.table(MyTable.class); - -// Delete multiple instantly -Delete.tables(MyTable1.class, MyTable2.class); - -// Delete using query -SQLite.delete(MyTable.class) - .where(DeviceObject_Table.carrier.is("T-MOBILE")) - .and(DeviceObject_Table.device.is("Samsung-Galaxy-S5")) - .async() - .execute(); -``` - -## INSERT - -`INSERT` queries in DBFlow are also similiar to `Update` and `Delete` in that we -have two kinds: - - 1. `Model.insert()` - 2. `SQLite.insert()` - -For simple `INSERT` for a single or few, concrete set of `Model` stick with (1). -For powerful multiple `Model` insertion that can span many rows, use (2). In this -section we speak on (2). **Note:** using model caching, you'll need to clear it out -post an operation from (2). - -```java - -// columns + values separately -SQLite.insert(SomeTable.class) - .columns(SomeTable_Table.name, SomeTable_Table.phoneNumber) - .values("Default", "5555555") - .async() - .execute() - -// or combine into Operators - SQLite.insert(SomeTable.class) - .columnValues(SomeTable_Table.name.eq("Default"), - SomeTable_Table.phoneNumber.eq("5555555")) - .async() - .execute() - -``` - -`INSERT` supports inserting multiple rows as well. - -```java - -// columns + values separately -SQLite.insert(SomeTable.class) - .columns(SomeTable_Table.name, SomeTable_Table.phoneNumber) - .values("Default1", "5555555") - .values("Default2", "6666666") - .async() - .execute() - -// or combine into Operators - SQLite.insert(SomeTable.class) - .columnValues(SomeTable_Table.name.eq("Default1"), - SomeTable_Table.phoneNumber.eq("5555555")) - .columnValues(SomeTable_Table.name.eq("Default2"), - SomeTable_Table.phoneNumber.eq("6666666")) - .async() - .execute() - -``` - -## Trigger - -Triggers enable SQLite-level listener operations that perform some operation, modification, -or action to run when a specific database event occurs. [See](https://www.sqlite.org/lang_createtrigger.html) for more documentation on its usage. - -```java - -Trigger.create("SomeTrigger") - .after().insert(ConditionModel.class).begin(new Update<>(TestUpdateModel.class) - .set(TestUpdateModel_Table.value.is("Fired"))).enable(); // enables the trigger if it does not exist, so subsequent calls are OK - - -``` - -## Case - -The SQLite `CASE` operator is very useful to evaluate a set of conditions and "map" them -to a certain value that returns in a SELECT query. - -We have two kinds of case: -1. Simple -2. Searched - -The simple CASE query in DBFlow: - -```java - -SQLite.select(CaseModel_Table.customerId, - CaseModel_Table.firstName, - CaseModel_Table.lastName, - SQLite._case(CaseModel_Table.country) - .when("USA").then("Domestic") - ._else("Foreign") - .end("CustomerGroup")).from(CaseModel.class) - -``` - -The CASE is returned as `CustomerGroup` with the valyes of "Domestic" if the country is from -the 'USA' otherwise we mark the value as "Foreign". These appear alongside the results -set from the SELECT. - -The search CASE is a little more complicated in that each `when()` statement -represents a `SQLOperator`, which return a `boolean` expression: - -```java - -SQLite.select(CaseModel_Table.customerId, - CaseModel_Table.firstName, - CaseModel_Table.lastName, - SQLite.caseWhen(CaseModel_Table.country.eq("USA")) - .then("Domestic") - ._else("Foreign").end("CustomerGroup")).from(CaseModel.class); -``` diff --git a/usage2/StoringData.md b/usage2/StoringData.md deleted file mode 100644 index 692451c3f..000000000 --- a/usage2/StoringData.md +++ /dev/null @@ -1,313 +0,0 @@ -# Storing Data - -DBFlow provide a few mechanisms by which we store data to the database. The difference of options -should not provide confusion but rather allow flexibility in what you decide is the best way -to store information. - -## Synchronous Storage - -While generally saving data synchronous should be avoided, for small amounts of data -it has little effect. - -```java - -FlowManager.getModelAdapter(SomeTable.class).save(model); - -FlowManager.getModelAdapter(SomeTable.class).insert(model); - -FlowManager.getModelAdapter(SomeTable.class).update(model); - -model.insert(); // inserts -model.update(); // updates -model.save(); // checks if exists, if true update, else insert. - -``` - -Code (without running in a transaction) like this should be avoided: - -```java - -for (int i = 0; i < models.size(), i++) { - models.get(i).save(); -} - -``` - -Doing operations on the main thread can block it if you read and write to the DB on a different thread while accessing DB on the main. - -## Synchronous Transactions - -A simple database transaction can be wrapped in a call: - -```java - -FlowManager.getDatabase(AppDatabase.class).executeTransaction(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - // something here - Player player = new Player("Andrew", "Grosner"); - player.save(databaseWrapper); // use wrapper (from BaseModel) - } -}); - -``` - -Even though DBFlow is ridiculously fast, this should be put on a separate thread - outside of the UI, so that your UI remains responsive on all devices. - - Instead we should move onto `Transaction` (the preferred method). - -### Async Transactions - -## Transactions - -Transactions are ACID in SQLite, meaning they either occur completely or not at all. -Using transactions significantly speed up the time it takes to store. So recommendation -you should use transactions whenever you can. - -Async is the preferred method. Transactions, using the `DefaultTransactionManager`, - occur on one thread per-database (to prevent flooding from other DB in your app) - and receive callbacks on the UI. You can override this behavior and roll your own - or hook into an existing system, read [here](/usage2/StoringData.md#custom-transactionmanager). - -Also to use the legacy, priority-based system, read [here](/usage2/StoringData.md#priority-queue). - - A basic transaction: - - ```java - -DatabaseDefinition database = FlowManager.getDatabase(AppDatabase.class); -Transaction transaction = database.beginTransactionAsync(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - called.set(true); - } - }).build(); -transaction.execute(); // execute - -transaction.cancel(); - // attempt to cancel before its run. If it's already ran, this call has no effect. - - ``` - - `Transaction` have callbacks to allow you to "listen" for success and errors. - - ```java - - - transaction - .success(new Transaction.Success() { - @Override - public void onSuccess(Transaction transaction) { - // called post-execution on the UI thread. - } - }) - .error(new Transaction.Error() { - @Override - public void onError(Transaction transaction, Throwable error) { - // call if any errors occur in the transaction. - } - }); - - - ``` - - The `Success` callback runs post-transaction on the UI thread. - The `Error` callback is called on the UI thread if and only if it is specified and an exception occurs, - otherwise it is thrown in the `Transaction` as a `RuntimeException`. **Note**: - all exceptions are caught when specifying the callback. Ensure you handle all - errors, otherwise you might miss some problems. - -### ProcessModelTransaction - -`ProcessModelTransaction` allows for more flexibility and for you to easily operate on a set of `Model` in a -`Transaction` easily. It holds a list of `Model` by which you provide the modification -method in the `Builder`. You can listen for when each are processed inside a normal -`Transaction`. - -It is a convenient way to operate on them: - -```java - -ProcessModelTransaction processModelTransaction = - new ProcessModelTransaction.Builder<>(new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TestModel1 model) { - // call some operation on model here - model.save(); - model.insert(); // or - model.delete(); // or - } - }).processListener(new ProcessModelTransaction.OnModelProcessListener() { - @Override - public void onModelProcessed(long current, long total, TestModel1 modifiedModel) { - modelProcessedCount.incrementAndGet(); - } - }).addAll(items).build(); -Transaction transaction = database.beginTransactionAsync(processModelTransaction).build(); -transaction.execute(); - -``` - -In Kotlin (with `dbflow-kotlinextensions`), we can drastically simplify: - -```java - -items.processInTransactionAsync({ it, databaseWrapper -> it.delete(databaseWrapper) }, - ProcessModelTransaction.OnModelProcessListener { current, size, model -> - modelProcessedCount.incrementAndGet(); - }) - -``` -You can listen to when operations complete for each model via the `OnModelProcessListener`. -These callbacks occur on the UI thread. If you wish to run them on same thread (great for tests), -set `runProcessListenerOnSameThread()` to `true`. - -### FastStoreModelTransaction - -The `FastStoreModelTransaction` is the quickest, lightest way to store a `List` of -`Model` into the database through a `Transaction`. It comes with some restrictions when compared to `ProcessModelTransaction`: - 1. All `Model` must be from same Table/Model Class. - 2. No progress listening - 3. Can only `save`, `insert`, or `update` the whole list entirely. - -```java - -FastStoreModelTransaction - .insertBuilder(FlowManager.getModelAdapter(TestModel2.class)) - .addAll(modelList) - .build() - - // updateBuilder + saveBuilder also available. - -``` - -What it provides: - 1. Reuses `ContentValues`, `DatabaseStatement`, and other classes where possible. - 2. Opens and closes own `DatabaseStatement` per total execution. - 3. Significant speed bump over `ProcessModelTransaction` at the expense of flexibility. - -### Custom TransactionManager - -If you prefer to roll your own thread-management system or have an existing -system you can override the default system included. - - -To begin you must implement a `ITransactionQueue`: - -```java - -public class CustomQueue implements ITransactionQueue { - - @Override - public void add(Transaction transaction) { - - } - - @Override - public void cancel(Transaction transaction) { - - } - - @Override - public void startIfNotAlive() { - - } - - @Override - public void cancel(String name) { - - } - - @Override - public void quit() { - - } -} - -``` - -You must provide ways to `add()`, `cancel(Transaction)`, and `startIfNotAlive()`. -The other two methods are optional, but recommended. - -`startIfNotAlive()` in the `DefaultTransactionQueue` will start itself (since it's -a thread). - - Next you can override the `BaseTransactionManager` (not required, see later): - -```java - -public class CustomTransactionManager extends BaseTransactionManager { - - public CustomTransactionManager(DatabaseDefinition databaseDefinition) { - super(new CustomTransactionQueue(), databaseDefinition); - } - -} - -``` - -To register it with DBFlow, in your `FlowConfig`, you must: - -```java - -FlowManager.init(builder - .addDatabaseConfig(new DatabaseConfig.Builder(AppDatabase.class) - .transactionManagerCreator(new DatabaseConfig.TransactionManagerCreator() { - @Override - public BaseTransactionManager createManager(DatabaseDefinition databaseDefinition) { - // this will be called once database modules are loaded and created. - return new CustomTransactionManager(databaseDefinition); - - // or you can: - //return new DefaultTransactionManager(new CustomTransactionQueue(), databaseDefinition); - } - }) - .build()) - .build()); - -``` - -### Priority Queue - -In versions pre-3.0, DBFlow utilized a `PriorityBlockingQueue` to manage the asynchronous -dispatch of `Transaction`. As of 3.0, it has switched to simply a FIFO queue. To -keep the legacy way, a `PriorityTransactionQueue` was created. - -As seen in [Custom Transaction Managers](/usage2/StoringData.md#custom-transactionmanager), -we provide a custom instance of the `DefaultTransactionManager` with the `PriorityTransactionQueue` specified: - -```java - -FlowManager.init(builder - .addDatabaseConfig(new DatabaseConfig.Builder(AppDatabase.class) - .transactionManagerCreator(new DatabaseConfig.TransactionManagerCreator() { - @Override - public BaseTransactionManager createManager(DatabaseDefinition databaseDefinition) { - // this will be called once database modules are loaded and created. - return new DefaultTransactionManager( - new PriorityTransactionQueue("DBFlow Priority Queue"), - databaseDefinition); - } - }) - .build()) - .build()); - -``` - -What this does is for the specified database (in this case `AppDatabase`), -now require each `ITransaction` specified for the database should wrap itself around -the `PriorityTransactionWrapper`. Otherwise an the `PriorityTransactionQueue` -wraps the existing `Transaction` in a `PriorityTransactionWrapper` with normal priority. - - -To specify a priority: - -```java - -FlowManager.getDatabase(AppDatabase.class) - .beginTransactionAsync(new PriorityTransactionWrapper.Builder(myTransaction) - .priority(PriorityTransactionWrapper.PRIORITY_HIGH).build()) - .build().execute(); - -``` diff --git a/usage2/TypeConverters.md b/usage2/TypeConverters.md deleted file mode 100644 index 6593b83aa..000000000 --- a/usage2/TypeConverters.md +++ /dev/null @@ -1,58 +0,0 @@ -# Type conversion - -When building out `Model` classes, you may wish to provide a different type of `@Column` that from the standard supported column types. To recap the standard column types include: - 1. `String`, `char`, `Character` - 2. All numbers types (primitive + boxed) - 3. `byte[]`/`Byte` - 4. `Blob` (DBFlow's version) - 5. `Date`/`java.sql.Date` - 6. Bools - 7. `Model` as `@ForeignKey` - 8. `Calendar` - 9. `BigDecimal` - 10. `UUID` - -`TypeConverter` do _not_ support: - 1. Any Parameterized fields. - 2. `List`, `Map`, etc. Best way to fix this is to create a separate table [relationship](/usage2/Relationships.md) - 3. Conversion from one type-converter to another (i.e `JSONObject` to `Date`). The first parameter of `TypeConverter` is the value of the type as if it was a primitive/boxed type. - 4. Conversion from custom type to `Model`, or `Model` to a supported type. - 5. The custom class _must_ map to a non-complex field such as `String`, numbers, `char`/`Character` or `Blob` - -## Define a TypeConverter - -Defining a `TypeConverter` is quick and easy. - -This example creates a `TypeConverter` for a field that is `JSONObject` and converts it to a `String` representation: - -```java - -@com.raizlabs.android.dbflow.annotation.TypeConverter -public class JSONConverter extends TypeConverter { - - @Override - public String getDBValue(JSONObject model) { - return model == null ? null : model.toString(); - } - - @Override - public JSONObject getModelValue(String data) { - JSONObject jsonObject = null; - try { - jsonObject = new JSONObject(data); - } catch (JSONException e) { - // you should consider logging or throwing exception. - } - return jsonObject; - } -} - -``` - -Once this is defined, by using the annotation `@TypeConverter`, it is registered automatically accross all databases. - -There are cases where you wish to provide multiple `TypeConverter` for same kind of field (i.e. `Date` with different date formats stored in a DB). - -## TypeConverter for specific `@Column` - -In DBFlow, specifying a `TypeConverter` for a `@Column` is as easy as `@Column(typeConverter = JSONConverter.class)`. What it will do is create the converter once for use only when that column is used. From cfdc3af71394bf7177b8785e1df97506119d3d8a Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Wed, 26 Apr 2017 14:55:11 -0400 Subject: [PATCH 34/37] [readme] now points to gitbooks site. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1046c648a..e25ea00fa 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ DBFlow is built from a collection of the best features of many database librarie Changes exist in the [releases tab](https://github.com/Raizlabs/DBFlow/releases). # Usage Docs -For more detailed usage, check out it out [here](/usage2/Intro.md) +For more detailed usage, check out it out [here](https://agrosner.gitbooks.io/dbflow/content/) # Including in your project From 7bb00c038c5fc74c339d9e909f60faaec07e0b8a Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Thu, 11 May 2017 12:37:37 -0400 Subject: [PATCH 35/37] [property] add convenient methods on property to construct a new orderby object easily. --- .../android/dbflow/models/QueryModelTest.kt | 6 +++--- .../dbflow/sql/language/property/IProperty.java | 7 +++++++ .../dbflow/sql/language/property/Property.java | 14 ++++++++++++++ usage2 | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt index f42ae731a..3ed55a318 100644 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt +++ b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt @@ -26,9 +26,9 @@ class QueryModelTest : BaseUnitTest() { assert(blog.exists()) val result = (select(name.withTable().`as`("blogName"), id.withTable().`as`("authorId"), - Blog_Table.id.withTable().`as`("blogId")) from Blog::class innerJoin - Author::class on (author_id.withTable() eq id.withTable())) - .queryCustomSingle(AuthorNameQuery::class.java)!! + Blog_Table.id.withTable().`as`("blogId")) from Blog::class innerJoin + Author::class on (author_id.withTable() eq id.withTable())) + .queryCustomSingle(AuthorNameQuery::class.java)!! assertEquals(author.id, result.authorId) assertEquals(blog.id, result.blogId) } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java index e41142514..94e417a31 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java @@ -6,6 +6,7 @@ import com.raizlabs.android.dbflow.sql.language.Join; import com.raizlabs.android.dbflow.sql.language.Method; import com.raizlabs.android.dbflow.sql.language.NameAlias; +import com.raizlabs.android.dbflow.sql.language.OrderBy; import com.raizlabs.android.dbflow.structure.Model; /** @@ -117,4 +118,10 @@ public interface IProperty

extends Query { */ @NonNull Class getTable(); + + @NonNull + OrderBy asc(); + + @NonNull + OrderBy desc(); } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java index 2ae98d26e..9d02fc40f 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java @@ -8,6 +8,7 @@ import com.raizlabs.android.dbflow.sql.language.IOperator; import com.raizlabs.android.dbflow.sql.language.NameAlias; import com.raizlabs.android.dbflow.sql.language.Operator; +import com.raizlabs.android.dbflow.sql.language.OrderBy; import java.util.Collection; @@ -505,6 +506,19 @@ public Operator rem(T value) { return getCondition().rem(value); } + + @Override + @NonNull + public OrderBy asc() { + return OrderBy.fromProperty(this).ascending(); + } + + @Override + @NonNull + public OrderBy desc() { + return OrderBy.fromProperty(this).descending(); + } + /** * @return helper method to construct it in a {@link #distinct()} call. */ diff --git a/usage2 b/usage2 index acbf59ea7..99495e356 160000 --- a/usage2 +++ b/usage2 @@ -1 +1 @@ -Subproject commit acbf59ea7a647150696e3aaea386acb39b392176 +Subproject commit 99495e3566e38785870f6c580caa3915709f8d1f From a87dc58b2716bcad85429511dbbecec5d00367ef Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Thu, 11 May 2017 16:45:09 -0400 Subject: [PATCH 36/37] [sql] execute update delete for deleting table. --- .../com/raizlabs/android/dbflow/sql/language/Delete.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java index 8684a5c83..2e020f03e 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java @@ -18,7 +18,7 @@ public class Delete implements Query { * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} */ public static void table(Class table, SQLOperator... conditions) { - new Delete().from(table).where(conditions).query(); + new Delete().from(table).where(conditions).executeUpdateDelete(); } /** @@ -48,7 +48,7 @@ public From from(Class table) { @Override public String getQuery() { return new QueryBuilder() - .append("DELETE") - .appendSpace().getQuery(); + .append("DELETE") + .appendSpace().getQuery(); } } From c0aee933a6afde044a0130a52719d8e66631ea0d Mon Sep 17 00:00:00 2001 From: fuzzagrosner Date: Thu, 11 May 2017 16:46:28 -0400 Subject: [PATCH 37/37] [version] update to 4.0.0 --- README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e25ea00fa..20e7aad02 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Add the library to the project-level build.gradle, using the apt plugin to enabl ```groovy - def dbflow_version = "4.0.0-beta7" + def dbflow_version = "4.0.0" // or dbflow_version = "develop-SNAPSHOT" for grabbing latest dependency in your project on the develop branch // or 10-digit short-hash of a specific commit. (Useful for bugs fixed in develop, but not in a release yet) diff --git a/gradle.properties b/gradle.properties index 8293f2161..c243e81ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.0.0-beta7 +version=4.0.0 version_code=1 group=com.raizlabs.android bt_siteUrl=https://github.com/Raizlabs/DBFlow