1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.room.vo 18 19 import androidx.room.ext.isNonNull 20 import androidx.room.ext.typeName 21 import androidx.room.migration.bundle.FieldBundle 22 import androidx.room.parser.Collate 23 import androidx.room.parser.SQLTypeAffinity 24 import androidx.room.solver.types.CursorValueReader 25 import androidx.room.solver.types.StatementValueBinder 26 import com.squareup.javapoet.TypeName 27 import javax.lang.model.element.Element 28 import javax.lang.model.type.TypeMirror 29 // used in cache matching, must stay as a data class or implement equals 30 data class Field(val element: Element, val name: String, val type: TypeMirror, 31 var affinity: SQLTypeAffinity?, 32 val collate: Collate? = null, 33 val columnName: String = name, 34 /* means that this field does not belong to parent, instead, it belongs to a 35 * embedded child of the main Pojo*/ 36 val parent: EmbeddedField? = null, 37 // index might be removed when being merged into an Entity 38 var indexed: Boolean = false) : HasSchemaIdentity { 39 lateinit var getter: FieldGetter 40 lateinit var setter: FieldSetter 41 // binds the field into a statement 42 var statementBinder: StatementValueBinder? = null 43 // reads this field from a cursor column 44 var cursorValueReader: CursorValueReader? = null <lambda>null45 val typeName: TypeName by lazy { type.typeName() } 46 47 /** Whether the table column for this field should be NOT NULL */ 48 val nonNull = element.isNonNull() && (parent == null || parent.isNonNullRecursively()) 49 getIdKeynull50 override fun getIdKey(): String { 51 // we don't get the collate information from sqlite so ignoring it here. 52 return "$columnName-${affinity?.name ?: SQLTypeAffinity.TEXT.name}-$nonNull" 53 } 54 55 /** 56 * Used when reporting errors on duplicate names 57 */ getPathnull58 fun getPath(): String { 59 return if (parent == null) { 60 name 61 } else { 62 "${parent.field.getPath()} > $name" 63 } 64 } 65 <lambda>null66 private val pathWithDotNotation: String by lazy { 67 if (parent == null) { 68 name 69 } else { 70 "${parent.field.pathWithDotNotation}.$name" 71 } 72 } 73 74 /** 75 * List of names that include variations. 76 * e.g. if it is mUser, user is added to the list 77 * or if it is isAdmin, admin is added to the list 78 */ <lambda>null79 val nameWithVariations by lazy { 80 val result = arrayListOf(name) 81 if (name.length > 1) { 82 if (name.startsWith('_')) { 83 result.add(name.substring(1)) 84 } 85 if (name.startsWith("m") && name[1].isUpperCase()) { 86 result.add(name.substring(1).decapitalize()) 87 } 88 89 if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) { 90 if (name.length > 2 && name.startsWith("is") && name[2].isUpperCase()) { 91 result.add(name.substring(2).decapitalize()) 92 } 93 if (name.length > 3 && name.startsWith("has") && name[3].isUpperCase()) { 94 result.add(name.substring(3).decapitalize()) 95 } 96 } 97 } 98 result 99 } 100 <lambda>null101 val getterNameWithVariations by lazy { 102 nameWithVariations.map { "get${it.capitalize()}" } + 103 if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) { 104 nameWithVariations.flatMap { 105 listOf("is${it.capitalize()}", "has${it.capitalize()}") 106 } 107 } else { 108 emptyList() 109 } 110 } 111 <lambda>null112 val setterNameWithVariations by lazy { 113 nameWithVariations.map { "set${it.capitalize()}" } 114 } 115 116 /** 117 * definition to be used in create query 118 */ databaseDefinitionnull119 fun databaseDefinition(autoIncrementPKey: Boolean): String { 120 val columnSpec = StringBuilder("") 121 if (autoIncrementPKey) { 122 columnSpec.append(" PRIMARY KEY AUTOINCREMENT") 123 } 124 if (nonNull) { 125 columnSpec.append(" NOT NULL") 126 } 127 if (collate != null) { 128 columnSpec.append(" COLLATE ${collate.name}") 129 } 130 return "`$columnName` ${(affinity ?: SQLTypeAffinity.TEXT).name}$columnSpec" 131 } 132 toBundlenull133 fun toBundle(): FieldBundle = FieldBundle(pathWithDotNotation, columnName, 134 affinity?.name ?: SQLTypeAffinity.TEXT.name, nonNull 135 ) 136 } 137