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