diff --git a/build.gradle b/build.gradle index b22d45ba4..d1d25895c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,11 @@ buildscript { - ext.kotlin_version = '1.1.4-2' + ext.kotlin_version = '1.1.51' repositories { jcenter() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0-beta2' + classpath 'com.android.tools.build:gradle:3.0.0-rc2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 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 f7cdd1131..0eb923527 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 @@ -134,7 +134,7 @@ abstract class BaseTableDefinition(typeElement: Element, processorManager: Proce .addModifiers(Modifier.PUBLIC, Modifier.FINAL) for (columnDefinition in packagePrivateList) { - var helperClassName = "${columnDefinition.element.getPackage()}.${columnDefinition.element.enclosingElement.toClassName().simpleName()}${classSeparator}Helper" + var helperClassName = "${columnDefinition.element.getPackage()}.${columnDefinition.element.enclosingElement.toClassName()?.simpleName()}${classSeparator}Helper" if (columnDefinition is ReferenceColumnDefinition) { val tableDefinition: TableDefinition? = databaseDefinition?.objectHolder?.tableDefinitionMap?.get(columnDefinition.referencedClassName as TypeName) if (tableDefinition != null) { 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 30000f270..9efa374ba 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 @@ -30,10 +30,7 @@ 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.BindToStatementMethod.Mode.DELETE -import com.raizlabs.android.dbflow.processor.definition.BindToStatementMethod.Mode.INSERT -import com.raizlabs.android.dbflow.processor.definition.BindToStatementMethod.Mode.NON_INSERT -import com.raizlabs.android.dbflow.processor.definition.BindToStatementMethod.Mode.UPDATE +import com.raizlabs.android.dbflow.processor.definition.BindToStatementMethod.Mode.* 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.ReferenceColumnDefinition @@ -142,7 +139,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) @@ -152,37 +149,37 @@ 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, INSERT), BindToStatementMethod(this, NON_INSERT), - BindToStatementMethod(this, UPDATE), BindToStatementMethod(this, DELETE), - InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), - UpdateStatementQueryMethod(this), DeleteStatementQueryMethod(this), - 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, INSERT), BindToStatementMethod(this, NON_INSERT), + BindToStatementMethod(this, UPDATE), BindToStatementMethod(this, DELETE), + InsertStatementQueryMethod(this, true), InsertStatementQueryMethod(this, false), + UpdateStatementQueryMethod(this), DeleteStatementQueryMethod(this), + 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() { @@ -236,7 +233,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) } @@ -249,7 +246,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) } @@ -263,9 +260,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 } } @@ -286,24 +283,24 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab val isInheritedPrimaryKey = inheritedPrimaryKeyMap.containsKey(element.simpleName.toString()) val isColumnMap = element.annotation() != null if (element.annotation() != null || isForeign || isPrimary - || isAllFields || isInherited || isInheritedPrimaryKey || isColumnMap) { + || isAllFields || isInherited || isInheritedPrimaryKey || isColumnMap) { if (checkInheritancePackagePrivate(isPackagePrivateNotInSamePackage, element)) return val columnDefinition = if (isInheritedPrimaryKey) { val inherited = inheritedPrimaryKeyMap[element.simpleName.toString()] ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, inherited?.primaryKey) + inherited?.column, inherited?.primaryKey) } else if (isInherited) { val inherited = inheritedColumnMap[element.simpleName.toString()] ColumnDefinition(manager, element, this, isPackagePrivateNotInSamePackage, - inherited?.column, null, inherited?.nonNullConflict ?: ConflictAction.NONE) + inherited?.column, null, inherited?.nonNullConflict ?: ConflictAction.NONE) } else if (isForeign || isColumnMap) { ReferenceColumnDefinition(manager, this, - element, isPackagePrivateNotInSamePackage) + element, isPackagePrivateNotInSamePackage) } else { ColumnDefinition(manager, element, - this, isPackagePrivateNotInSamePackage) + this, isPackagePrivateNotInSamePackage) } if (columnValidator.validate(manager, columnDefinition)) { @@ -319,6 +316,17 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab hasRowID = true } + autoIncrementColumn?.let { + // check to ensure not null. + if (it.isNullableType) { + manager.logWarning("Attempting to use nullable field type on an autoincrementing column. " + + "To suppress or remove this warning " + + "switch to java primitive, add @android.support.annotation.NonNull," + + "@org.jetbrains.annotation.NotNull, or in Kotlin don't make it nullable. Check the column ${it.columnName} " + + "on $tableName") + } + } + if (columnDefinition is ReferenceColumnDefinition && !columnDefinition.isColumnMap) { foreignKeyDefinitions.add(columnDefinition) } @@ -404,7 +412,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)) @@ -438,7 +446,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) } @@ -460,28 +468,28 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } val saveForeignKeyFields = columnDefinitions - .filter { (it is ReferenceColumnDefinition) && it.saveForeignKeyModel } - .map { it as ReferenceColumnDefinition } + .filter { (it is ReferenceColumnDefinition) && it.saveForeignKeyModel } + .map { it as ReferenceColumnDefinition } 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 ReferenceColumnDefinition) && it.deleteForeignKeyModel } - .map { it as ReferenceColumnDefinition } + .filter { (it is ReferenceColumnDefinition) && it.deleteForeignKeyModel } + .map { it as ReferenceColumnDefinition } 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()) } @@ -506,21 +514,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) } @@ -530,7 +538,7 @@ class TableDefinition(manager: ProcessorManager, element: TypeElement) : BaseTab } `override fun`(TypeName.VOID, "load", param(elementClassName!!, "model"), - param(ClassNames.DATABASE_WRAPPER, wrapper)) { + param(ClassNames.DATABASE_WRAPPER, wrapper)) { modifiers(public, final) statement("super.load(model, $wrapper)") statement("getModelCache().addModel(getCachingId(${ModelUtils.variable}), ${ModelUtils.variable})") @@ -539,8 +547,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] @@ -551,21 +559,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()) } @@ -596,7 +604,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) } @@ -604,7 +612,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) } @@ -612,8 +620,8 @@ 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) @@ -627,6 +635,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/ColumnDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/column/ColumnDefinition.kt index 2042762ab..1fb2ee096 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 @@ -65,6 +65,7 @@ constructor(processorManager: ProcessorManager, element: Element, var length = -1 var notNull = false var isNotNullType = false + var isNullableType = true var onNullConflict: ConflictAction? = null var onUniqueConflict: ConflictAction? = null var unique = false @@ -113,14 +114,22 @@ constructor(processorManager: ProcessorManager, element: Element, notNull = true } + if (elementTypeName?.isPrimitive == true) { + isNullableType = false + isNotNullType = true + } + // if specified, usually from Kotlin targets, we will not set null on the field. element.annotation()?.let { isNotNullType = true + isNullableType = false } + // android support annotation element.annotationMirrors - .find { it.annotationType.toTypeElement().toClassName() == ClassNames.NON_NULL }?.let { + .find { it.annotationType.toTypeElement().toClassName() == ClassNames.NON_NULL }?.let { isNotNullType = true + isNullableType = false } column?.let { @@ -144,13 +153,13 @@ constructor(processorManager: ProcessorManager, element: Element, val isString = (elementTypeName == ClassName.get(String::class.java)) if (defaultValue != null - && isString - && !QUOTE_PATTERN.matcher(defaultValue).find()) { + && isString + && !QUOTE_PATTERN.matcher(defaultValue).find()) { defaultValue = "\"" + defaultValue + "\"" } if (isNotNullType && defaultValue == null - && isString) { + && isString) { defaultValue = "\"\"" } @@ -159,19 +168,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 ?: "" @@ -220,7 +229,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) } @@ -234,11 +243,12 @@ constructor(processorManager: ProcessorManager, element: Element, wrapperAccessor = BlobColumnAccessor() wrapperTypeName = ArrayTypeName.of(TypeName.BYTE) } else { - if (elementTypeName is ParameterizedTypeName) { + if (elementTypeName is ParameterizedTypeName || + elementTypeName == ArrayTypeName.of(TypeName.BYTE.unbox())) { // 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. Found $elementTypeName") } else { if (elementTypeName == TypeName.BOOLEAN) { wrapperAccessor = BooleanColumnAccessor() @@ -258,7 +268,7 @@ constructor(processorManager: ProcessorManager, element: Element, } combiner = Combiner(columnAccessor, elementTypeName!!, wrapperAccessor, wrapperTypeName, - subWrapperAccessor) + subWrapperAccessor) } private fun evaluateTypeConverter(typeConverterDefinition: TypeConverterDefinition?, @@ -268,7 +278,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 @@ -312,21 +322,21 @@ constructor(processorManager: ProcessorManager, element: Element, } val fieldBuilder = FieldSpec.builder(propParam, - propertyFieldName, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + propertyFieldName, 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) @@ -375,7 +385,7 @@ constructor(processorManager: ProcessorManager, element: Element, defineProperty: Boolean = true) = code { SqliteStatementAccessCombiner(combiner).apply { addCode(if (useStart) "start" else "", getDefaultValueBlock(), index.get(), modelBlock, - defineProperty) + defineProperty) } this } @@ -390,8 +400,8 @@ constructor(processorManager: ProcessorManager, element: Element, } LoadFromCursorAccessCombiner(combiner, defaultValue != null, - nameAllocator, baseTableDefinition.orderedCursorLookUp, - assignDefaultValue).apply { + nameAllocator, baseTableDefinition.orderedCursorLookUp, + assignDefaultValue).apply { addCode(columnName, getDefaultValueBlock(), index.get(), modelBlock) } this @@ -426,10 +436,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) { @@ -446,9 +456,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") @@ -484,8 +494,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/utils/ElementExtensions.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/utils/ElementExtensions.kt index e0c9151b2..5ff5af720 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 @@ -32,4 +32,4 @@ inline fun Element?.annotation() = this?.getAnnotation( 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 +fun Element?.toClassName(): ClassName? = this?.let { ClassName.get(this as TypeElement) } \ No newline at end of file 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 db1084b79..b13b74690 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 @@ -85,9 +85,10 @@ class TypeConverterModel(@PrimaryKey var id: Int = 0, @Table(database = TestDatabase::class) class EnumTypeConverterModel(@PrimaryKey var id: Int = 0, - @Column var blob: Blob? = null, - @Column(typeConverter = CustomEnumTypeConverter::class) - var difficulty: Difficulty = Difficulty.EASY) + @Column var blob: Blob? = null, + @Column var byteArray: ByteArray? = null, + @Column(typeConverter = CustomEnumTypeConverter::class) + var difficulty: Difficulty = Difficulty.EASY) @Table(database = TestDatabase::class, allFields = true) class FeedEntry(@PrimaryKey var id: Int = 0, @@ -96,18 +97,18 @@ class FeedEntry(@PrimaryKey var id: Int = 0, @Table(database = TestDatabase::class) @ManyToMany( - generatedTableClassName = "Refund", referencedTable = Transfer::class, - referencedTableColumnName = "refund_in", thisTableColumnName = "refund_out", - saveForeignKeyModels = true + generatedTableClassName = "Refund", referencedTable = Transfer::class, + referencedTableColumnName = "refund_in", thisTableColumnName = "refund_out", + saveForeignKeyModels = true ) data class Transfer(@PrimaryKey var transfer_id: UUID = UUID.randomUUID()) @Table(database = TestDatabase::class) data class Transfer2( - @PrimaryKey - var id: UUID = UUID.randomUUID(), - @ForeignKey(stubbedRelationship = true) - var origin: Account? = null + @PrimaryKey + var id: UUID = UUID.randomUUID(), + @ForeignKey(stubbedRelationship = true) + var origin: Account? = null ) @Table(database = TestDatabase::class) @@ -148,7 +149,7 @@ class CustomTypeConverter : TypeConverter() { class CustomEnumTypeConverter : TypeConverter() { override fun getDBValue(model: Difficulty) = model.name.substring(0..0) - override fun getModelValue(data: String) = when(data) { + override fun getModelValue(data: String) = when (data) { "E" -> Difficulty.EASY "M" -> Difficulty.MEDIUM "H" -> Difficulty.HARD 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 b7f64040b..737c0e13f 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 @@ -241,7 +241,7 @@ public Map> getMigrations() { public synchronized OpenHelper getHelper() { if (openHelper == null) { DatabaseConfig config = FlowManager.getConfig().databaseConfigMap() - .get(getAssociatedDatabaseClassFile()); + .get(getAssociatedDatabaseClassFile()); if (config == null || config.helperCreator() == null) { openHelper = new FlowSQLiteOpenHelper(this, helperListener); } else { @@ -261,7 +261,7 @@ public DatabaseWrapper getWritableDatabase() { public ModelNotifier getModelNotifier() { if (modelNotifier == null) { DatabaseConfig config = FlowManager.getConfig().databaseConfigMap() - .get(getAssociatedDatabaseClassFile()); + .get(getAssociatedDatabaseClassFile()); if (config == null || config.modelNotifier() == null) { modelNotifier = new ContentResolverNotifier(); } else { @@ -375,6 +375,26 @@ public void reset(@Nullable DatabaseConfig databaseConfig) { } } + /** + * Reopens the DB with the new {@link DatabaseConfig} specified. + */ + public void reopen(@Nullable DatabaseConfig databaseConfig) { + if (!isResetting) { + close(); + openHelper = null; + applyDatabaseConfig(databaseConfig); + getHelper().getDatabase(); + isResetting = false; + } + } + + /** + * Closes and reopens the database. + */ + public void reopen() { + reopen(databaseConfig); + } + /** * Deletes the underlying database and destroys it. */ 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 1550bd911..836d0d913 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 @@ -60,10 +60,10 @@ public boolean isInitialized() { private static final String DEFAULT_DATABASE_HOLDER_NAME = "GeneratedDatabaseHolder"; private static final String DEFAULT_DATABASE_HOLDER_PACKAGE_NAME = - FlowManager.class.getPackage().getName(); + FlowManager.class.getPackage().getName(); private static final String DEFAULT_DATABASE_HOLDER_CLASSNAME = - DEFAULT_DATABASE_HOLDER_PACKAGE_NAME + "." + DEFAULT_DATABASE_HOLDER_NAME; + DEFAULT_DATABASE_HOLDER_PACKAGE_NAME + "." + DEFAULT_DATABASE_HOLDER_NAME; /** * Returns the table name for the specific model class @@ -101,8 +101,8 @@ public static Class getTableClassForName(String databaseName, String tableNam 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)); + "Did you forget to add the @Table annotation and point it to %1s?", + tableName, databaseName)); } } return modelClass; @@ -120,8 +120,8 @@ public static Class getTableClassForName(Class databaseClass, String table 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, databaseClass)); + "Did you forget to add the @Table annotation and point it to %1s?", + tableName, databaseClass)); } } return modelClass; @@ -137,7 +137,7 @@ public static DatabaseDefinition getDatabaseForTable(Class table) { DatabaseDefinition databaseDefinition = globalDatabaseHolder.getDatabaseForTable(table); if (databaseDefinition == null) { throw new InvalidDBConfiguration("Model object: " + table.getName() + - " is not registered with a Database. " + "Did you forget an annotation?"); + " is not registered with a Database. " + "Did you forget an annotation?"); } return databaseDefinition; } @@ -148,7 +148,7 @@ public static DatabaseDefinition getDatabase(Class databaseClass) { DatabaseDefinition databaseDefinition = globalDatabaseHolder.getDatabase(databaseClass); if (databaseDefinition == null) { throw new InvalidDBConfiguration("Database: " + databaseClass.getName() + " is not a registered Database. " + - "Did you forget the @Database annotation?"); + "Did you forget the @Database annotation?"); } return databaseDefinition; } @@ -177,7 +177,7 @@ public static DatabaseDefinition getDatabase(String databaseName) { } throw new InvalidDBConfiguration("The specified database" + databaseName + " was not found. " + - "Did you forget the @Database annotation?"); + "Did you forget the @Database annotation?"); } @NonNull @@ -204,7 +204,7 @@ public static void initModule(Class generatedClassName public static FlowConfig getConfig() { if (config == null) { throw new IllegalStateException("Configuration is not initialized. " + - "Please call init(FlowConfig) in your application class."); + "Please call init(FlowConfig) in your application class."); } return config; } @@ -245,6 +245,23 @@ public static void reset() { loadedModules.clear(); } + /** + * Close all DB files and resets {@link FlowConfig} and the {@link GlobalDatabaseHolder}. Brings + * DBFlow back to initial application state. + */ + public static synchronized void close() { + Set, DatabaseDefinition>> entrySet = + globalDatabaseHolder.databaseClassLookupMap.entrySet(); + for (Map.Entry, DatabaseDefinition> value : entrySet) { + value.getValue().close(); + } + + config = null; + + globalDatabaseHolder = new GlobalDatabaseHolder(); + loadedModules.clear(); + } + /** * Will throw an exception if this class is not initialized yet in {@link #init(FlowConfig)} * @@ -254,7 +271,7 @@ public static void reset() { public static Context getContext() { if (config == null) { throw new IllegalStateException("You must provide a valid FlowConfig instance. " + - "We recommend calling init() in your application class."); + "We recommend calling init() in your application class."); } return config.getContext(); } @@ -292,7 +309,7 @@ public static void init(@NonNull FlowConfig flowConfig) { FlowLog.log(FlowLog.Level.W, "Could not find the default GeneratedDatabaseHolder"); } - if (flowConfig.databaseHolders() != null && !flowConfig.databaseHolders().isEmpty()) { + if (!flowConfig.databaseHolders().isEmpty()) { for (Class holder : flowConfig.databaseHolders()) { loadDatabaseHolder(holder); } @@ -324,7 +341,7 @@ public static TypeConverter getTypeConverterForClass(Class objectClass) { */ public static synchronized void destroy() { Set, DatabaseDefinition>> entrySet = - globalDatabaseHolder.databaseClassLookupMap.entrySet(); + globalDatabaseHolder.databaseClassLookupMap.entrySet(); for (Map.Entry, DatabaseDefinition> value : entrySet) { value.getValue().destroy(); } @@ -409,7 +426,7 @@ public static ModelAdapter getModelAdapter(Class modelC @SuppressWarnings("unchecked") @NonNull public static ModelViewAdapter getModelViewAdapter( - Class modelViewClass) { + Class modelViewClass) { final ModelViewAdapter modelViewAdapter = getModelViewAdapterOrNull(modelViewClass); if (modelViewAdapter == null) { throwCannotFindAdapter("ModelViewAdapter", modelViewClass); @@ -427,7 +444,7 @@ public static ModelViewAdapter getModelViewAdapter( @SuppressWarnings("unchecked") @NonNull public static QueryModelAdapter getQueryModelAdapter( - Class queryModelClass) { + Class queryModelClass) { final QueryModelAdapter queryModelAdapter = getQueryModelAdapterOrNull(queryModelClass); if (queryModelAdapter == null) { throwCannotFindAdapter("QueryModelAdapter", queryModelClass); @@ -480,13 +497,13 @@ public static boolean isDatabaseIntegrityOk(String databaseName) { private static void throwCannotFindAdapter(String type, Class clazz) { throw new IllegalArgumentException("Cannot find " + type + " for " + clazz + ". Ensure " + - "the class is annotated with proper annotation."); + "the class is annotated with proper annotation."); } private static void checkDatabaseHolder() { if (!globalDatabaseHolder.isInitialized()) { throw new IllegalStateException("The global database holder is not initialized. Ensure you call " + - "FlowManager.init() before accessing the database."); + "FlowManager.init() before accessing the database."); } } diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java index 8c73aa7b4..43d7c1dd6 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java @@ -2,6 +2,7 @@ import android.support.annotation.NonNull; +import com.raizlabs.android.dbflow.config.FlowLog; import com.raizlabs.android.dbflow.runtime.NotifyDistributor; import com.raizlabs.android.dbflow.structure.BaseModel; import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; @@ -21,10 +22,11 @@ public synchronized long insert(@NonNull TModel model) { public synchronized long insert(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { final boolean hasAutoIncrement = getModelAdapter().hasAutoIncrement(model); DatabaseStatement insertStatement = hasAutoIncrement - ? getModelAdapter().getCompiledStatement(wrapper) - : getModelAdapter().getInsertStatement(wrapper); + ? getModelAdapter().getCompiledStatement(wrapper) + : getModelAdapter().getInsertStatement(wrapper); long id; try { + getModelAdapter().saveForeignKeys(model, wrapper); if (hasAutoIncrement) { getModelAdapter().bindToStatement(insertStatement, model); } else { @@ -46,16 +48,11 @@ public synchronized long insert(@NonNull TModel model, @NonNull DatabaseWrapper public synchronized long insert(@NonNull TModel model, @NonNull DatabaseStatement insertStatement, @NonNull DatabaseWrapper wrapper) { - if (getModelAdapter().hasAutoIncrement(model)) { - getModelAdapter().bindToStatement(insertStatement, model); + if (!getModelAdapter().hasAutoIncrement(model)) { + return super.insert(model, insertStatement, wrapper); } else { - getModelAdapter().bindToInsertStatement(insertStatement, model); + FlowLog.log(FlowLog.Level.W, "Ignoring insert statement " + insertStatement + " since an autoincrement column specified in the insert."); + return insert(model, wrapper); } - long id = insertStatement.executeInsert(); - if (id > INSERT_FAILED) { - getModelAdapter().updateAutoIncrement(model, id); - NotifyDistributor.get().notifyModelChanged(model, getModelAdapter(), BaseModel.Action.INSERT); - } - return id; } } 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 3b437769a..69714d2b0 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 @@ -313,7 +313,12 @@ public String getAutoIncrementingColumnName() { } public boolean hasAutoIncrement(TModel model) { - return getAutoIncrementingId(model).longValue() > 0; + Number id = getAutoIncrementingId(model); + //noinspection ConstantConditions + if (id == null) { + throw new IllegalStateException("An autoincrementing column field cannot be null."); + } + return id.longValue() > 0; } /** @@ -452,7 +457,7 @@ protected ListModelSaver createListModelSaver() { * * @param modelSaver The saver to use. */ - public void setModelSaver(ModelSaver modelSaver) { + public void setModelSaver(@NonNull ModelSaver modelSaver) { this.modelSaver = modelSaver; this.modelSaver.setModelAdapter(this); } 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 a1423e8c9..2f30379fd 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 @@ -237,6 +237,18 @@ public Short getShortOrDefault(String columnName, Short defValue) { return getShortOrDefault(cursor.getColumnIndex(columnName), defValue); } + public byte[] getBlobOrDefault(String columnName) { + return getBlobOrDefault(cursor.getColumnIndex(columnName)); + } + + public byte[] getBlobOrDefault(int index) { + if (index != -1 && !cursor.isNull(index)) { + return cursor.getBlob(index); + } else { + return null; + } + } + public byte[] getBlobOrDefault(int index, byte[] defValue) { if (index != -1 && !cursor.isNull(index)) { return cursor.getBlob(index); diff --git a/gradle.properties b/gradle.properties index 626d7d7bf..2d2b9ba60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ bt_licenseUrl=http://opensource.org/licenses/MIT bt_repo=Libraries dbflow_project_prefix=: kotlin.incremental=false -dbflow_build_tools_version=26.0.0 +dbflow_build_tools_version=26.0.2 dbflow_min_sdk=4 dbflow_min_sdk_rx=15