Home | History | Annotate | Download | only in processor
      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)
     38     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