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.processor
18 
19 import androidx.room.ColumnInfo
20 import androidx.room.ext.getAsBoolean
21 import androidx.room.ext.getAsInt
22 import androidx.room.ext.getAsString
23 import androidx.room.parser.Collate
24 import androidx.room.parser.SQLTypeAffinity
25 import androidx.room.vo.EmbeddedField
26 import androidx.room.vo.Field
27 import com.google.auto.common.AnnotationMirrors
28 import com.google.auto.common.MoreElements
29 import com.squareup.javapoet.TypeName
30 import javax.lang.model.element.Element
31 import javax.lang.model.type.DeclaredType
32 
33 class FieldProcessor(baseContext: Context, val containing: DeclaredType, val element: Element,
34                      val bindingScope: BindingScope,
35                      // pass only if this is processed as a child of Embedded field
36                      val fieldParent: EmbeddedField?) {
37     val context = baseContext.fork(element)
processnull38     fun process(): Field {
39         val member = context.processingEnv.typeUtils.asMemberOf(containing, element)
40         val type = TypeName.get(member)
41         val columnInfoAnnotation = MoreElements.getAnnotationMirror(element,
42                 ColumnInfo::class.java)
43         val name = element.simpleName.toString()
44         val columnName: String
45         val affinity: SQLTypeAffinity?
46         val collate: Collate?
47         val fieldPrefix = fieldParent?.prefix ?: ""
48         val indexed: Boolean
49         if (columnInfoAnnotation.isPresent) {
50             val nameInAnnotation = AnnotationMirrors
51                     .getAnnotationValue(columnInfoAnnotation.get(), "name")
52                     .getAsString(ColumnInfo.INHERIT_FIELD_NAME)
53             columnName = fieldPrefix + if (nameInAnnotation == ColumnInfo.INHERIT_FIELD_NAME) {
54                 name
55             } else {
56                 nameInAnnotation
57             }
58 
59             affinity = try {
60                 val userDefinedAffinity = AnnotationMirrors
61                         .getAnnotationValue(columnInfoAnnotation.get(), "typeAffinity")
62                         .getAsInt(ColumnInfo.UNDEFINED)!!
63                 SQLTypeAffinity.fromAnnotationValue(userDefinedAffinity)
64             } catch (ex: NumberFormatException) {
65                 null
66             }
67 
68             collate = Collate.fromAnnotationValue(AnnotationMirrors.getAnnotationValue(
69                     columnInfoAnnotation.get(), "collate").getAsInt(ColumnInfo.UNSPECIFIED)!!)
70 
71             indexed = AnnotationMirrors
72                     .getAnnotationValue(columnInfoAnnotation.get(), "index")
73                     .getAsBoolean(false)
74         } else {
75             columnName = fieldPrefix + name
76             affinity = null
77             collate = null
78             indexed = false
79         }
80         context.checker.notBlank(columnName, element,
81                 ProcessorErrors.COLUMN_NAME_CANNOT_BE_EMPTY)
82         context.checker.notUnbound(type, element,
83                 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS)
84 
85         val field = Field(name = name,
86                 type = member,
87                 element = element,
88                 columnName = columnName,
89                 affinity = affinity,
90                 collate = collate,
91                 parent = fieldParent,
92                 indexed = indexed)
93 
94         when (bindingScope) {
95             BindingScope.TWO_WAY -> {
96                 val adapter = context.typeAdapterStore.findColumnTypeAdapter(field.type,
97                         field.affinity)
98                 field.statementBinder = adapter
99                 field.cursorValueReader = adapter
100                 field.affinity = adapter?.typeAffinity ?: field.affinity
101                 context.checker.check(adapter != null, field.element,
102                         ProcessorErrors.CANNOT_FIND_COLUMN_TYPE_ADAPTER)
103             }
104             BindingScope.BIND_TO_STMT -> {
105                 field.statementBinder = context.typeAdapterStore
106                         .findStatementValueBinder(field.type, field.affinity)
107                 context.checker.check(field.statementBinder != null, field.element,
108                         ProcessorErrors.CANNOT_FIND_STMT_BINDER)
109             }
110             BindingScope.READ_FROM_CURSOR -> {
111                 field.cursorValueReader = context.typeAdapterStore
112                         .findCursorValueReader(field.type, field.affinity)
113                 context.checker.check(field.cursorValueReader != null, field.element,
114                         ProcessorErrors.CANNOT_FIND_CURSOR_READER)
115             }
116         }
117         return field
118     }
119 
120     /**
121      * Defines what we need to assign
122      */
123     enum class BindingScope {
124         TWO_WAY, // both bind and read.
125         BIND_TO_STMT, // just value to statement
126         READ_FROM_CURSOR // just cursor to value
127     }
128 }
129