1 /*
<lambda>null2  * 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