• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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