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 android.arch.persistence.room.vo 18 19 import android.arch.persistence.room.ext.isNonNull 20 import android.arch.persistence.room.ext.typeName 21 import android.arch.persistence.room.migration.bundle.FieldBundle 22 import android.arch.persistence.room.parser.Collate 23 import android.arch.persistence.room.parser.SQLTypeAffinity 24 import android.arch.persistence.room.solver.types.CursorValueReader 25 import android.arch.persistence.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) { 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 50 /** 51 * Used when reporting errors on duplicate names 52 */ getPathnull53 fun getPath() : String { 54 return if (parent == null) { 55 name 56 } else { 57 "${parent.field.getPath()} > $name" 58 } 59 } 60 <lambda>null61 private val pathWithDotNotation : String by lazy { 62 if (parent == null) { 63 name 64 } else { 65 "${parent.field.pathWithDotNotation}.$name" 66 } 67 } 68 69 /** 70 * List of names that include variations. 71 * e.g. if it is mUser, user is added to the list 72 * or if it is isAdmin, admin is added to the list 73 */ <lambda>null74 val nameWithVariations by lazy { 75 val result = arrayListOf(name) 76 if (name.length > 1) { 77 if (name.startsWith('_')) { 78 result.add(name.substring(1)) 79 } 80 if (name.startsWith("m") && name[1].isUpperCase()) { 81 result.add(name.substring(1).decapitalize()) 82 } 83 84 if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) { 85 if (name.length > 2 && name.startsWith("is") && name[2].isUpperCase()) { 86 result.add(name.substring(2).decapitalize()) 87 } 88 if (name.length > 3 && name.startsWith("has") && name[3].isUpperCase()) { 89 result.add(name.substring(3).decapitalize()) 90 } 91 } 92 } 93 result 94 } 95 <lambda>null96 val getterNameWithVariations by lazy { 97 nameWithVariations.map { "get${it.capitalize()}" } + 98 if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) { 99 nameWithVariations.flatMap { 100 listOf("is${it.capitalize()}", "has${it.capitalize()}") 101 } 102 } else { 103 emptyList() 104 } 105 } 106 <lambda>null107 val setterNameWithVariations by lazy { 108 nameWithVariations.map { "set${it.capitalize()}" } 109 } 110 111 /** 112 * definition to be used in create query 113 */ databaseDefinitionnull114 fun databaseDefinition(autoIncrementPKey : Boolean) : String { 115 val columnSpec = StringBuilder("") 116 if (autoIncrementPKey) { 117 columnSpec.append(" PRIMARY KEY AUTOINCREMENT") 118 } 119 if (nonNull) { 120 columnSpec.append(" NOT NULL") 121 } 122 if (collate != null) { 123 columnSpec.append(" COLLATE ${collate.name}") 124 } 125 return "`$columnName` ${(affinity ?: SQLTypeAffinity.TEXT).name}$columnSpec" 126 } 127 toBundlenull128 fun toBundle(): FieldBundle = FieldBundle(pathWithDotNotation, columnName, 129 affinity?.name ?: SQLTypeAffinity.TEXT.name, nonNull 130 ) 131 } 132