Home | History | Annotate | Download | only in solver
      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.solver
     18 
     19 import androidx.room.Entity
     20 import androidx.room.ext.CommonTypeNames
     21 import androidx.room.ext.GuavaBaseTypeNames
     22 import androidx.room.ext.hasAnnotation
     23 import androidx.room.ext.typeName
     24 import androidx.room.parser.ParsedQuery
     25 import androidx.room.parser.SQLTypeAffinity
     26 import androidx.room.processor.Context
     27 import androidx.room.processor.EntityProcessor
     28 import androidx.room.processor.FieldProcessor
     29 import androidx.room.processor.PojoProcessor
     30 import androidx.room.solver.binderprovider.CursorQueryResultBinderProvider
     31 import androidx.room.solver.binderprovider.DataSourceFactoryQueryResultBinderProvider
     32 import androidx.room.solver.binderprovider.DataSourceQueryResultBinderProvider
     33 import androidx.room.solver.binderprovider.FlowableQueryResultBinderProvider
     34 import androidx.room.solver.binderprovider.GuavaListenableFutureQueryResultBinderProvider
     35 import androidx.room.solver.binderprovider.InstantQueryResultBinderProvider
     36 import androidx.room.solver.binderprovider.LiveDataQueryResultBinderProvider
     37 import androidx.room.solver.binderprovider.RxMaybeQueryResultBinderProvider
     38 import androidx.room.solver.binderprovider.RxSingleQueryResultBinderProvider
     39 import androidx.room.solver.query.parameter.ArrayQueryParameterAdapter
     40 import androidx.room.solver.query.parameter.BasicQueryParameterAdapter
     41 import androidx.room.solver.query.parameter.CollectionQueryParameterAdapter
     42 import androidx.room.solver.query.parameter.QueryParameterAdapter
     43 import androidx.room.solver.query.result.ArrayQueryResultAdapter
     44 import androidx.room.solver.query.result.EntityRowAdapter
     45 import androidx.room.solver.query.result.GuavaOptionalQueryResultAdapter
     46 import androidx.room.solver.query.result.InstantQueryResultBinder
     47 import androidx.room.solver.query.result.ListQueryResultAdapter
     48 import androidx.room.solver.query.result.OptionalQueryResultAdapter
     49 import androidx.room.solver.query.result.PojoRowAdapter
     50 import androidx.room.solver.query.result.QueryResultAdapter
     51 import androidx.room.solver.query.result.QueryResultBinder
     52 import androidx.room.solver.query.result.RowAdapter
     53 import androidx.room.solver.query.result.SingleColumnRowAdapter
     54 import androidx.room.solver.query.result.SingleEntityQueryResultAdapter
     55 import androidx.room.solver.types.BoxedBooleanToBoxedIntConverter
     56 import androidx.room.solver.types.BoxedPrimitiveColumnTypeAdapter
     57 import androidx.room.solver.types.ByteArrayColumnTypeAdapter
     58 import androidx.room.solver.types.ColumnTypeAdapter
     59 import androidx.room.solver.types.CompositeAdapter
     60 import androidx.room.solver.types.CompositeTypeConverter
     61 import androidx.room.solver.types.CursorValueReader
     62 import androidx.room.solver.types.NoOpConverter
     63 import androidx.room.solver.types.PrimitiveBooleanToIntConverter
     64 import androidx.room.solver.types.PrimitiveColumnTypeAdapter
     65 import androidx.room.solver.types.StatementValueBinder
     66 import androidx.room.solver.types.StringColumnTypeAdapter
     67 import androidx.room.solver.types.TypeConverter
     68 import com.google.auto.common.MoreElements
     69 import com.google.auto.common.MoreTypes
     70 import com.google.common.annotations.VisibleForTesting
     71 import java.util.LinkedList
     72 import javax.lang.model.type.ArrayType
     73 import javax.lang.model.type.TypeKind
     74 import javax.lang.model.type.TypeMirror
     75 
     76 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
     77 /**
     78  * Holds all type adapters and can create on demand composite type adapters to convert a type into a
     79  * database column.
     80  */
     81 class TypeAdapterStore private constructor(
     82         val context: Context,
     83         /**
     84          * first type adapter has the highest priority
     85          */
     86         private val columnTypeAdapters: List<ColumnTypeAdapter>,
     87         /**
     88          * first converter has the highest priority
     89          */
     90         private val typeConverters: List<TypeConverter>) {
     91 
     92     companion object {
     93         fun copy(context: Context, store: TypeAdapterStore): TypeAdapterStore {
     94             return TypeAdapterStore(context = context,
     95                     columnTypeAdapters = store.columnTypeAdapters,
     96                     typeConverters = store.typeConverters)
     97         }
     98 
     99         fun create(context: Context, vararg extras: Any): TypeAdapterStore {
    100             val adapters = arrayListOf<ColumnTypeAdapter>()
    101             val converters = arrayListOf<TypeConverter>()
    102 
    103             fun addAny(extra: Any?) {
    104                 when (extra) {
    105                     is TypeConverter -> converters.add(extra)
    106                     is ColumnTypeAdapter -> adapters.add(extra)
    107                     is List<*> -> extra.forEach(::addAny)
    108                     else -> throw IllegalArgumentException("unknown extra $extra")
    109                 }
    110             }
    111 
    112             extras.forEach(::addAny)
    113             fun addTypeConverter(converter: TypeConverter) {
    114                 converters.add(converter)
    115             }
    116 
    117             fun addColumnAdapter(adapter: ColumnTypeAdapter) {
    118                 adapters.add(adapter)
    119             }
    120 
    121             val primitives = PrimitiveColumnTypeAdapter
    122                     .createPrimitiveAdapters(context.processingEnv)
    123             primitives.forEach(::addColumnAdapter)
    124             BoxedPrimitiveColumnTypeAdapter
    125                     .createBoxedPrimitiveAdapters(context.processingEnv, primitives)
    126                     .forEach(::addColumnAdapter)
    127             addColumnAdapter(StringColumnTypeAdapter(context.processingEnv))
    128             addColumnAdapter(ByteArrayColumnTypeAdapter(context.processingEnv))
    129             PrimitiveBooleanToIntConverter.create(context.processingEnv).forEach(::addTypeConverter)
    130             BoxedBooleanToBoxedIntConverter.create(context.processingEnv)
    131                     .forEach(::addTypeConverter)
    132             return TypeAdapterStore(context = context, columnTypeAdapters = adapters,
    133                     typeConverters = converters)
    134         }
    135     }
    136 
    137     val queryResultBinderProviders = listOf(
    138             CursorQueryResultBinderProvider(context),
    139             LiveDataQueryResultBinderProvider(context),
    140             FlowableQueryResultBinderProvider(context),
    141             GuavaListenableFutureQueryResultBinderProvider(context),
    142             RxMaybeQueryResultBinderProvider(context),
    143             RxSingleQueryResultBinderProvider(context),
    144             DataSourceQueryResultBinderProvider(context),
    145             DataSourceFactoryQueryResultBinderProvider(context),
    146             InstantQueryResultBinderProvider(context)
    147     )
    148 
    149     // type mirrors that be converted into columns w/o an extra converter
    150     private val knownColumnTypeMirrors by lazy {
    151         columnTypeAdapters.map { it.out }
    152     }
    153 
    154     /**
    155      * Searches 1 way to bind a value into a statement.
    156      */
    157     fun findStatementValueBinder(
    158             input: TypeMirror,
    159             affinity: SQLTypeAffinity?
    160     ): StatementValueBinder? {
    161         if (input.kind == TypeKind.ERROR) {
    162             return null
    163         }
    164         val adapter = findDirectAdapterFor(input, affinity)
    165         if (adapter != null) {
    166             return adapter
    167         }
    168         val targetTypes = targetTypeMirrorsFor(affinity)
    169         val binder = findTypeConverter(input, targetTypes) ?: return null
    170         // columnAdapter should not be null but we are receiving errors on crash in `first()` so
    171         // this safeguard allows us to dispatch the real problem to the user (e.g. why we couldn't
    172         // find the right adapter)
    173         val columnAdapter = getAllColumnAdapters(binder.to).firstOrNull() ?: return null
    174         return CompositeAdapter(input, columnAdapter, binder, null)
    175     }
    176 
    177     /**
    178      * Returns which entities targets the given affinity.
    179      */
    180     private fun targetTypeMirrorsFor(affinity: SQLTypeAffinity?): List<TypeMirror> {
    181         val specifiedTargets = affinity?.getTypeMirrors(context.processingEnv)
    182         return if (specifiedTargets == null || specifiedTargets.isEmpty()) {
    183             knownColumnTypeMirrors
    184         } else {
    185             specifiedTargets
    186         }
    187     }
    188 
    189     /**
    190      * Searches 1 way to read it from cursor
    191      */
    192     fun findCursorValueReader(output: TypeMirror, affinity: SQLTypeAffinity?): CursorValueReader? {
    193         if (output.kind == TypeKind.ERROR) {
    194             return null
    195         }
    196         val adapter = findColumnTypeAdapter(output, affinity)
    197         if (adapter != null) {
    198             // two way is better
    199             return adapter
    200         }
    201         // we could not find a two way version, search for anything
    202         val targetTypes = targetTypeMirrorsFor(affinity)
    203         val converter = findTypeConverter(targetTypes, output) ?: return null
    204         return CompositeAdapter(output,
    205                 getAllColumnAdapters(converter.from).first(), null, converter)
    206     }
    207 
    208     /**
    209      * Tries to reverse the converter going through the same nodes, if possible.
    210      */
    211     @VisibleForTesting
    212     fun reverse(converter: TypeConverter): TypeConverter? {
    213         return when (converter) {
    214             is NoOpConverter -> converter
    215             is CompositeTypeConverter -> {
    216                 val r1 = reverse(converter.conv1) ?: return null
    217                 val r2 = reverse(converter.conv2) ?: return null
    218                 CompositeTypeConverter(r2, r1)
    219             }
    220             else -> {
    221                 val types = context.processingEnv.typeUtils
    222                 typeConverters.firstOrNull {
    223                     types.isSameType(it.from, converter.to) && types
    224                             .isSameType(it.to, converter.from)
    225                 }
    226             }
    227         }
    228     }
    229 
    230     /**
    231      * Finds a two way converter, if you need 1 way, use findStatementValueBinder or
    232      * findCursorValueReader.
    233      */
    234     fun findColumnTypeAdapter(out: TypeMirror, affinity: SQLTypeAffinity?): ColumnTypeAdapter? {
    235         if (out.kind == TypeKind.ERROR) {
    236             return null
    237         }
    238         val adapter = findDirectAdapterFor(out, affinity)
    239         if (adapter != null) {
    240             return adapter
    241         }
    242         val targetTypes = targetTypeMirrorsFor(affinity)
    243         val intoStatement = findTypeConverter(out, targetTypes) ?: return null
    244         // ok found a converter, try the reverse now
    245         val fromCursor = reverse(intoStatement) ?: findTypeConverter(intoStatement.to, out)
    246                 ?: return null
    247         return CompositeAdapter(out, getAllColumnAdapters(intoStatement.to).first(), intoStatement,
    248                 fromCursor)
    249     }
    250 
    251     private fun findDirectAdapterFor(
    252             out: TypeMirror, affinity: SQLTypeAffinity?): ColumnTypeAdapter? {
    253         return getAllColumnAdapters(out).firstOrNull {
    254             affinity == null || it.typeAffinity == affinity
    255         }
    256     }
    257 
    258     fun findTypeConverter(input: TypeMirror, output: TypeMirror): TypeConverter? {
    259         return findTypeConverter(listOf(input), listOf(output))
    260     }
    261 
    262     fun findQueryResultBinder(typeMirror: TypeMirror, query: ParsedQuery): QueryResultBinder {
    263         return if (typeMirror.kind == TypeKind.DECLARED) {
    264             val declared = MoreTypes.asDeclared(typeMirror)
    265             return queryResultBinderProviders.first {
    266                 it.matches(declared)
    267             }.provide(declared, query)
    268         } else {
    269             InstantQueryResultBinder(findQueryResultAdapter(typeMirror, query))
    270         }
    271     }
    272 
    273     fun findQueryResultAdapter(typeMirror: TypeMirror, query: ParsedQuery): QueryResultAdapter? {
    274         if (typeMirror.kind == TypeKind.ERROR) {
    275             return null
    276         }
    277         if (typeMirror.kind == TypeKind.DECLARED) {
    278             val declared = MoreTypes.asDeclared(typeMirror)
    279 
    280             if (declared.typeArguments.isEmpty()) {
    281                 val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
    282                 return SingleEntityQueryResultAdapter(rowAdapter)
    283             } else if (
    284                     context.processingEnv.typeUtils.erasure(typeMirror).typeName() ==
    285                     GuavaBaseTypeNames.OPTIONAL) {
    286                 // Handle Guava Optional by unpacking its generic type argument and adapting that.
    287                 // The Optional adapter will reappend the Optional type.
    288                 val typeArg = declared.typeArguments.first()
    289                 val rowAdapter = findRowAdapter(typeArg, query) ?: return null
    290                 return GuavaOptionalQueryResultAdapter(SingleEntityQueryResultAdapter(rowAdapter))
    291             } else if (
    292                     context.processingEnv.typeUtils.erasure(typeMirror).typeName() ==
    293                     CommonTypeNames.OPTIONAL) {
    294                 // Handle java.util.Optional similarly.
    295                 val typeArg = declared.typeArguments.first()
    296                 val rowAdapter = findRowAdapter(typeArg, query) ?: return null
    297                 return OptionalQueryResultAdapter(SingleEntityQueryResultAdapter(rowAdapter))
    298             } else if (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)) {
    299                 val typeArg = declared.typeArguments.first()
    300                 val rowAdapter = findRowAdapter(typeArg, query) ?: return null
    301                 return ListQueryResultAdapter(rowAdapter)
    302             }
    303             return null
    304         } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
    305             val rowAdapter =
    306                     findRowAdapter(typeMirror.componentType, query) ?: return null
    307             return ArrayQueryResultAdapter(rowAdapter)
    308         } else {
    309             val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
    310             return SingleEntityQueryResultAdapter(rowAdapter)
    311         }
    312     }
    313 
    314     /**
    315      * Find a converter from cursor to the given type mirror.
    316      * If there is information about the query result, we try to use it to accept *any* POJO.
    317      */
    318     fun findRowAdapter(typeMirror: TypeMirror, query: ParsedQuery): RowAdapter? {
    319         if (typeMirror.kind == TypeKind.ERROR) {
    320             return null
    321         }
    322         if (typeMirror.kind == TypeKind.DECLARED) {
    323             val declared = MoreTypes.asDeclared(typeMirror)
    324             if (declared.typeArguments.isNotEmpty()) {
    325                 // TODO one day support this
    326                 return null
    327             }
    328             val resultInfo = query.resultInfo
    329 
    330             val (rowAdapter, rowAdapterLogs) = if (resultInfo != null && query.errors.isEmpty()
    331                     && resultInfo.error == null) {
    332                 // if result info is not null, first try a pojo row adapter
    333                 context.collectLogs { subContext ->
    334                     val pojo = PojoProcessor(
    335                             baseContext = subContext,
    336                             element = MoreTypes.asTypeElement(typeMirror),
    337                             bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
    338                             parent = null
    339                     ).process()
    340                     PojoRowAdapter(
    341                             context = subContext,
    342                             info = resultInfo,
    343                             pojo = pojo,
    344                             out = typeMirror)
    345                 }
    346             } else {
    347                 Pair(null, null)
    348             }
    349 
    350             if (rowAdapter == null && query.resultInfo == null) {
    351                 // we don't know what query returns. Check for entity.
    352                 val asElement = MoreTypes.asElement(typeMirror)
    353                 if (asElement.hasAnnotation(Entity::class)) {
    354                     return EntityRowAdapter(EntityProcessor(context,
    355                             MoreElements.asType(asElement)).process())
    356                 }
    357             }
    358 
    359             if (rowAdapter != null && rowAdapterLogs?.hasErrors() != true) {
    360                 rowAdapterLogs?.writeTo(context.processingEnv)
    361                 return rowAdapter
    362             }
    363 
    364             if ((resultInfo?.columns?.size ?: 1) == 1) {
    365                 val singleColumn = findCursorValueReader(typeMirror,
    366                         resultInfo?.columns?.get(0)?.type)
    367                 if (singleColumn != null) {
    368                     return SingleColumnRowAdapter(singleColumn)
    369                 }
    370             }
    371             // if we tried, return its errors
    372             if (rowAdapter != null) {
    373                 rowAdapterLogs?.writeTo(context.processingEnv)
    374                 return rowAdapter
    375             }
    376             if (query.runtimeQueryPlaceholder) {
    377                 // just go w/ pojo and hope for the best. this happens for @RawQuery where we
    378                 // try to guess user's intention and hope that their query fits the result.
    379                 val pojo = PojoProcessor(
    380                         baseContext = context,
    381                         element = MoreTypes.asTypeElement(typeMirror),
    382                         bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
    383                         parent = null
    384                 ).process()
    385                 return PojoRowAdapter(
    386                         context = context,
    387                         info = null,
    388                         pojo = pojo,
    389                         out = typeMirror)
    390             }
    391             return null
    392         } else {
    393             val singleColumn = findCursorValueReader(typeMirror, null) ?: return null
    394             return SingleColumnRowAdapter(singleColumn)
    395         }
    396     }
    397 
    398     fun findQueryParameterAdapter(typeMirror: TypeMirror): QueryParameterAdapter? {
    399         if (MoreTypes.isType(typeMirror)
    400                 && (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)
    401                 || MoreTypes.isTypeOf(java.util.Set::class.java, typeMirror))) {
    402             val declared = MoreTypes.asDeclared(typeMirror)
    403             val binder = findStatementValueBinder(declared.typeArguments.first(),
    404                     null)
    405             if (binder != null) {
    406                 return CollectionQueryParameterAdapter(binder)
    407             } else {
    408                 // maybe user wants to convert this collection themselves. look for a match
    409                 val collectionBinder = findStatementValueBinder(typeMirror, null) ?: return null
    410                 return BasicQueryParameterAdapter(collectionBinder)
    411             }
    412         } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
    413             val component = typeMirror.componentType
    414             val binder = findStatementValueBinder(component, null) ?: return null
    415             return ArrayQueryParameterAdapter(binder)
    416         } else {
    417             val binder = findStatementValueBinder(typeMirror, null) ?: return null
    418             return BasicQueryParameterAdapter(binder)
    419         }
    420     }
    421 
    422     private fun findTypeConverter(input: TypeMirror, outputs: List<TypeMirror>): TypeConverter? {
    423         return findTypeConverter(listOf(input), outputs)
    424     }
    425 
    426     private fun findTypeConverter(input: List<TypeMirror>, output: TypeMirror): TypeConverter? {
    427         return findTypeConverter(input, listOf(output))
    428     }
    429 
    430     private fun findTypeConverter(
    431             inputs: List<TypeMirror>, outputs: List<TypeMirror>): TypeConverter? {
    432         if (inputs.isEmpty()) {
    433             return null
    434         }
    435         val types = context.processingEnv.typeUtils
    436         inputs.forEach { input ->
    437             if (outputs.any { output -> types.isSameType(input, output) }) {
    438                 return NoOpConverter(input)
    439             }
    440         }
    441 
    442         val excludes = arrayListOf<TypeMirror>()
    443 
    444         val queue = LinkedList<TypeConverter>()
    445         fun exactMatch(candidates: List<TypeConverter>): TypeConverter? {
    446             return candidates.firstOrNull {
    447                 outputs.any { output -> types.isSameType(output, it.to) }
    448             }
    449         }
    450         inputs.forEach { input ->
    451             val candidates = getAllTypeConverters(input, excludes)
    452             val match = exactMatch(candidates)
    453             if (match != null) {
    454                 return match
    455             }
    456             candidates.forEach {
    457                 excludes.add(it.to)
    458                 queue.add(it)
    459             }
    460         }
    461         excludes.addAll(inputs)
    462         while (queue.isNotEmpty()) {
    463             val prev = queue.pop()
    464             val from = prev.to
    465             val candidates = getAllTypeConverters(from, excludes)
    466             val match = exactMatch(candidates)
    467             if (match != null) {
    468                 return CompositeTypeConverter(prev, match)
    469             }
    470             candidates.forEach {
    471                 excludes.add(it.to)
    472                 queue.add(CompositeTypeConverter(prev, it))
    473             }
    474         }
    475         return null
    476     }
    477 
    478     private fun getAllColumnAdapters(input: TypeMirror): List<ColumnTypeAdapter> {
    479         return columnTypeAdapters.filter {
    480             context.processingEnv.typeUtils.isSameType(input, it.out)
    481         }
    482     }
    483 
    484     /**
    485      * Returns all type converters that can receive input type and return into another type.
    486      * The returned list is ordered by priority such that if we have an exact match, it is
    487      * prioritized.
    488      */
    489     private fun getAllTypeConverters(input: TypeMirror, excludes: List<TypeMirror>):
    490             List<TypeConverter> {
    491         val types = context.processingEnv.typeUtils
    492         // for input, check assignability because it defines whether we can use the method or not.
    493         // for excludes, use exact match
    494         return typeConverters.filter { converter ->
    495             types.isAssignable(input, converter.from) &&
    496                     !excludes.any { types.isSameType(it, converter.to) }
    497         }.sortedByDescending {
    498                     // if it is the same, prioritize
    499                     if (types.isSameType(it.from, input)) {
    500                         2
    501                     } else {
    502                         1
    503                     }
    504                 }
    505     }
    506 }
    507