blob: 62a24adf604273564831ae904a8f84c368c8f2d9 [file] [log] [blame]
Yigit Boyar2259e4d2016-11-25 18:26:10 -08001/*
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
Yigit Boyar64db0cc2017-04-17 13:18:56 -070017package android.arch.persistence.room.solver
Yigit Boyar2259e4d2016-11-25 18:26:10 -080018
Yigit Boyar64db0cc2017-04-17 13:18:56 -070019import android.arch.persistence.room.Entity
Yigit Boyar64db0cc2017-04-17 13:18:56 -070020import android.arch.persistence.room.ext.hasAnnotation
21import android.arch.persistence.room.parser.ParsedQuery
22import android.arch.persistence.room.parser.SQLTypeAffinity
23import android.arch.persistence.room.processor.Context
24import android.arch.persistence.room.processor.EntityProcessor
25import android.arch.persistence.room.processor.FieldProcessor
26import android.arch.persistence.room.processor.PojoProcessor
Chris Craik9fd8e612017-06-23 14:07:04 -070027import android.arch.persistence.room.solver.binderprovider.CursorQueryResultBinderProvider
Yigit Boyarde23b912018-01-03 17:36:40 -080028import android.arch.persistence.room.solver.binderprovider.DataSourceQueryResultBinderProvider
Chris Craik9fd8e612017-06-23 14:07:04 -070029import android.arch.persistence.room.solver.binderprovider.FlowableQueryResultBinderProvider
30import android.arch.persistence.room.solver.binderprovider.InstantQueryResultBinderProvider
31import android.arch.persistence.room.solver.binderprovider.LiveDataQueryResultBinderProvider
Chris Craikdc60cac2018-01-10 10:19:39 -080032import android.arch.persistence.room.solver.binderprovider.DataSourceFactoryQueryResultBinderProvider
Yigit Boyar0cf0bfe2017-07-16 18:21:55 -070033import android.arch.persistence.room.solver.binderprovider.RxMaybeQueryResultBinderProvider
34import android.arch.persistence.room.solver.binderprovider.RxSingleQueryResultBinderProvider
Yigit Boyar64db0cc2017-04-17 13:18:56 -070035import android.arch.persistence.room.solver.query.parameter.ArrayQueryParameterAdapter
36import android.arch.persistence.room.solver.query.parameter.BasicQueryParameterAdapter
37import android.arch.persistence.room.solver.query.parameter.CollectionQueryParameterAdapter
38import android.arch.persistence.room.solver.query.parameter.QueryParameterAdapter
39import android.arch.persistence.room.solver.query.result.ArrayQueryResultAdapter
Yigit Boyar64db0cc2017-04-17 13:18:56 -070040import android.arch.persistence.room.solver.query.result.EntityRowAdapter
Yigit Boyar64db0cc2017-04-17 13:18:56 -070041import android.arch.persistence.room.solver.query.result.InstantQueryResultBinder
42import android.arch.persistence.room.solver.query.result.ListQueryResultAdapter
Yigit Boyar64db0cc2017-04-17 13:18:56 -070043import android.arch.persistence.room.solver.query.result.PojoRowAdapter
44import android.arch.persistence.room.solver.query.result.QueryResultAdapter
45import android.arch.persistence.room.solver.query.result.QueryResultBinder
46import android.arch.persistence.room.solver.query.result.RowAdapter
47import android.arch.persistence.room.solver.query.result.SingleColumnRowAdapter
48import android.arch.persistence.room.solver.query.result.SingleEntityQueryResultAdapter
49import android.arch.persistence.room.solver.types.BoxedBooleanToBoxedIntConverter
50import android.arch.persistence.room.solver.types.BoxedPrimitiveColumnTypeAdapter
Yigit Boyar64db0cc2017-04-17 13:18:56 -070051import android.arch.persistence.room.solver.types.ByteArrayColumnTypeAdapter
52import android.arch.persistence.room.solver.types.ColumnTypeAdapter
53import android.arch.persistence.room.solver.types.CompositeAdapter
54import android.arch.persistence.room.solver.types.CompositeTypeConverter
55import android.arch.persistence.room.solver.types.CursorValueReader
56import android.arch.persistence.room.solver.types.NoOpConverter
57import android.arch.persistence.room.solver.types.PrimitiveBooleanToIntConverter
58import android.arch.persistence.room.solver.types.PrimitiveColumnTypeAdapter
Yigit Boyar64db0cc2017-04-17 13:18:56 -070059import android.arch.persistence.room.solver.types.StatementValueBinder
60import android.arch.persistence.room.solver.types.StringColumnTypeAdapter
61import android.arch.persistence.room.solver.types.TypeConverter
Yigit Boyar846dfcf2017-01-16 18:05:20 -080062import com.google.auto.common.MoreElements
Yigit Boyar250a3e62016-11-29 18:06:52 -080063import com.google.auto.common.MoreTypes
Yigit Boyar8e543c42016-11-29 16:49:46 -080064import com.google.common.annotations.VisibleForTesting
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -080065import java.util.LinkedList
Yigit Boyar250a3e62016-11-29 18:06:52 -080066import javax.lang.model.type.ArrayType
Yigit Boyarefaf86a2016-12-02 15:16:35 -080067import javax.lang.model.type.TypeKind
Yigit Boyar2259e4d2016-11-25 18:26:10 -080068import javax.lang.model.type.TypeMirror
69
Yigit Boyar250a3e62016-11-29 18:06:52 -080070@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
Yigit Boyar2259e4d2016-11-25 18:26:10 -080071/**
72 * Holds all type adapters and can create on demand composite type adapters to convert a type into a
73 * database column.
74 */
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -080075class TypeAdapterStore private constructor(
76 val context: Context,
77 /**
78 * first type adapter has the highest priority
79 */
80 private val columnTypeAdapters: List<ColumnTypeAdapter>,
81 /**
82 * first converter has the highest priority
83 */
84 private val typeConverters: List<TypeConverter>) {
Yigit Boyaraa82fce2017-02-02 00:08:36 -080085
Yigit Boyar7a705bc2017-04-10 09:32:55 -070086 companion object {
Chris Craik9fd8e612017-06-23 14:07:04 -070087 fun copy(context: Context, store: TypeAdapterStore): TypeAdapterStore {
Yigit Boyar7a705bc2017-04-10 09:32:55 -070088 return TypeAdapterStore(context = context,
89 columnTypeAdapters = store.columnTypeAdapters,
90 typeConverters = store.typeConverters)
91 }
92
Chris Craik9fd8e612017-06-23 14:07:04 -070093 fun create(context: Context, @VisibleForTesting vararg extras: Any): TypeAdapterStore {
Yigit Boyar7a705bc2017-04-10 09:32:55 -070094 val adapters = arrayListOf<ColumnTypeAdapter>()
95 val converters = arrayListOf<TypeConverter>()
96
97 fun addAny(extra: Any?) {
98 when (extra) {
99 is TypeConverter -> converters.add(extra)
100 is ColumnTypeAdapter -> adapters.add(extra)
101 is List<*> -> extra.forEach(::addAny)
102 else -> throw IllegalArgumentException("unknown extra $extra")
103 }
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800104 }
Yigit Boyaraa82fce2017-02-02 00:08:36 -0800105
Yigit Boyar7a705bc2017-04-10 09:32:55 -0700106 extras.forEach(::addAny)
107 fun addTypeConverter(converter: TypeConverter) {
108 converters.add(converter)
109 }
Yigit Boyar8e543c42016-11-29 16:49:46 -0800110
Yigit Boyar7a705bc2017-04-10 09:32:55 -0700111 fun addColumnAdapter(adapter: ColumnTypeAdapter) {
112 adapters.add(adapter)
113 }
Yigit Boyar8e543c42016-11-29 16:49:46 -0800114
Yigit Boyar7a705bc2017-04-10 09:32:55 -0700115 val primitives = PrimitiveColumnTypeAdapter
116 .createPrimitiveAdapters(context.processingEnv)
117 primitives.forEach(::addColumnAdapter)
118 BoxedPrimitiveColumnTypeAdapter
119 .createBoxedPrimitiveAdapters(context.processingEnv, primitives)
120 .forEach(::addColumnAdapter)
121 addColumnAdapter(StringColumnTypeAdapter(context.processingEnv))
122 addColumnAdapter(ByteArrayColumnTypeAdapter(context.processingEnv))
123 PrimitiveBooleanToIntConverter.create(context.processingEnv).forEach(::addTypeConverter)
Yigit Boyar7a705bc2017-04-10 09:32:55 -0700124 BoxedBooleanToBoxedIntConverter.create(context.processingEnv)
125 .forEach(::addTypeConverter)
126 return TypeAdapterStore(context = context, columnTypeAdapters = adapters,
127 typeConverters = converters)
128 }
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800129 }
130
Chris Craik9fd8e612017-06-23 14:07:04 -0700131 val queryResultBinderProviders = listOf(
132 CursorQueryResultBinderProvider(context),
133 LiveDataQueryResultBinderProvider(context),
134 FlowableQueryResultBinderProvider(context),
Yigit Boyar0cf0bfe2017-07-16 18:21:55 -0700135 RxMaybeQueryResultBinderProvider(context),
136 RxSingleQueryResultBinderProvider(context),
Chris Craik24418e92017-08-18 10:25:44 -0700137 DataSourceQueryResultBinderProvider(context),
Chris Craikdc60cac2018-01-10 10:19:39 -0800138 DataSourceFactoryQueryResultBinderProvider(context),
Chris Craik9fd8e612017-06-23 14:07:04 -0700139 InstantQueryResultBinderProvider(context)
140 )
Yigit Boyar3c592c42017-04-04 14:53:04 -0700141
Yigit Boyar8e543c42016-11-29 16:49:46 -0800142 // type mirrors that be converted into columns w/o an extra converter
143 private val knownColumnTypeMirrors by lazy {
144 columnTypeAdapters.map { it.out }
145 }
146
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800147 /**
148 * Searches 1 way to bind a value into a statement.
149 */
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800150 fun findStatementValueBinder(
151 input: TypeMirror,
152 affinity: SQLTypeAffinity?
153 ): StatementValueBinder? {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800154 if (input.kind == TypeKind.ERROR) {
155 return null
156 }
Yigit Boyar275e7082017-02-02 10:00:11 -0800157 val adapter = findDirectAdapterFor(input, affinity)
158 if (adapter != null) {
159 return adapter
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800160 }
Yigit Boyar275e7082017-02-02 10:00:11 -0800161 val targetTypes = targetTypeMirrorsFor(affinity)
162 val binder = findTypeConverter(input, targetTypes) ?: return null
Yigit Boyar8af88c12018-01-11 11:09:19 -0800163 // columnAdapter should not be null but we are receiving errors on crash in `first()` so
164 // this safeguard allows us to dispatch the real problem to the user (e.g. why we couldn't
165 // find the right adapter)
166 val columnAdapter = getAllColumnAdapters(binder.to).firstOrNull() ?: return null
167 return CompositeAdapter(input, columnAdapter, binder, null)
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800168 }
169
170 /**
Yigit Boyar275e7082017-02-02 10:00:11 -0800171 * Returns which entities targets the given affinity.
172 */
Chris Craik9fd8e612017-06-23 14:07:04 -0700173 private fun targetTypeMirrorsFor(affinity: SQLTypeAffinity?): List<TypeMirror> {
Yigit Boyar275e7082017-02-02 10:00:11 -0800174 val specifiedTargets = affinity?.getTypeMirrors(context.processingEnv)
Chris Craik9fd8e612017-06-23 14:07:04 -0700175 return if (specifiedTargets == null || specifiedTargets.isEmpty()) {
Yigit Boyar275e7082017-02-02 10:00:11 -0800176 knownColumnTypeMirrors
177 } else {
178 specifiedTargets
179 }
180 }
181
182 /**
183 * Searches 1 way to read it from cursor
184 */
Chris Craik9fd8e612017-06-23 14:07:04 -0700185 fun findCursorValueReader(output: TypeMirror, affinity: SQLTypeAffinity?): CursorValueReader? {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800186 if (output.kind == TypeKind.ERROR) {
187 return null
188 }
Yigit Boyar275e7082017-02-02 10:00:11 -0800189 val adapter = findColumnTypeAdapter(output, affinity)
190 if (adapter != null) {
191 // two way is better
192 return adapter
193 }
194 // we could not find a two way version, search for anything
195 val targetTypes = targetTypeMirrorsFor(affinity)
196 val converter = findTypeConverter(targetTypes, output) ?: return null
197 return CompositeAdapter(output,
198 getAllColumnAdapters(converter.from).first(), null, converter)
199 }
200
201 /**
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800202 * Tries to reverse the converter going through the same nodes, if possible.
203 */
204 @VisibleForTesting
Chris Craik9fd8e612017-06-23 14:07:04 -0700205 fun reverse(converter: TypeConverter): TypeConverter? {
206 return when (converter) {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800207 is NoOpConverter -> converter
Chris Craik9fd8e612017-06-23 14:07:04 -0700208 is CompositeTypeConverter -> {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800209 val r1 = reverse(converter.conv1) ?: return null
210 val r2 = reverse(converter.conv2) ?: return null
211 CompositeTypeConverter(r2, r1)
212 }
213 else -> {
214 val types = context.processingEnv.typeUtils
215 typeConverters.firstOrNull {
216 types.isSameType(it.from, converter.to) && types
217 .isSameType(it.to, converter.from)
218 }
219 }
220 }
221 }
222
223 /**
224 * Finds a two way converter, if you need 1 way, use findStatementValueBinder or
225 * findCursorValueReader.
226 */
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800227 fun findColumnTypeAdapter(out: TypeMirror, affinity: SQLTypeAffinity?): ColumnTypeAdapter? {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800228 if (out.kind == TypeKind.ERROR) {
229 return null
230 }
Yigit Boyar275e7082017-02-02 10:00:11 -0800231 val adapter = findDirectAdapterFor(out, affinity)
232 if (adapter != null) {
233 return adapter
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800234 }
Yigit Boyar275e7082017-02-02 10:00:11 -0800235 val targetTypes = targetTypeMirrorsFor(affinity)
236 val intoStatement = findTypeConverter(out, targetTypes) ?: return null
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800237 // ok found a converter, try the reverse now
238 val fromCursor = reverse(intoStatement) ?: findTypeConverter(intoStatement.to, out)
239 ?: return null
240 return CompositeAdapter(out, getAllColumnAdapters(intoStatement.to).first(), intoStatement,
241 fromCursor)
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800242 }
243
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800244 private fun findDirectAdapterFor(
245 out: TypeMirror, affinity: SQLTypeAffinity?): ColumnTypeAdapter? {
Yigit Boyar275e7082017-02-02 10:00:11 -0800246 val adapter = getAllColumnAdapters(out).firstOrNull {
247 affinity == null || it.typeAffinity == affinity
248 }
249 return adapter
250 }
251
Yigit Boyar8e543c42016-11-29 16:49:46 -0800252 fun findTypeConverter(input: TypeMirror, output: TypeMirror): TypeConverter? {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800253 return findTypeConverter(listOf(input), listOf(output))
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800254 }
255
Yigit Boyar13a20482017-01-25 17:52:09 -0800256 fun findQueryResultBinder(typeMirror: TypeMirror, query: ParsedQuery): QueryResultBinder {
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800257 return if (typeMirror.kind == TypeKind.DECLARED) {
258 val declared = MoreTypes.asDeclared(typeMirror)
Chris Craik9fd8e612017-06-23 14:07:04 -0700259 return queryResultBinderProviders.first {
260 it.matches(declared)
261 }.provide(declared, query)
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800262 } else {
Yigit Boyar13a20482017-01-25 17:52:09 -0800263 InstantQueryResultBinder(findQueryResultAdapter(typeMirror, query))
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800264 }
265 }
266
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800267 fun findQueryResultAdapter(typeMirror: TypeMirror, query: ParsedQuery): QueryResultAdapter? {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800268 if (typeMirror.kind == TypeKind.ERROR) {
269 return null
270 }
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800271 if (typeMirror.kind == TypeKind.DECLARED) {
272 val declared = MoreTypes.asDeclared(typeMirror)
273 if (declared.typeArguments.isEmpty()) {
Yigit Boyar13a20482017-01-25 17:52:09 -0800274 val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800275 return SingleEntityQueryResultAdapter(rowAdapter)
276 }
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800277 if (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)) {
278 val typeArg = declared.typeArguments.first()
Yigit Boyar13a20482017-01-25 17:52:09 -0800279 val rowAdapter = findRowAdapter(typeArg, query) ?: return null
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800280 return ListQueryResultAdapter(rowAdapter)
281 }
282 return null
Yuichi Arakibccb62d2017-06-26 18:19:52 +0900283 } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
Yigit Boyar13a20482017-01-25 17:52:09 -0800284 val rowAdapter =
Yuichi Arakibccb62d2017-06-26 18:19:52 +0900285 findRowAdapter(typeMirror.componentType, query) ?: return null
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800286 return ArrayQueryResultAdapter(rowAdapter)
287 } else {
Yigit Boyar13a20482017-01-25 17:52:09 -0800288 val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800289 return SingleEntityQueryResultAdapter(rowAdapter)
290 }
291 }
292
Yigit Boyar13a20482017-01-25 17:52:09 -0800293 /**
294 * Find a converter from cursor to the given type mirror.
295 * If there is information about the query result, we try to use it to accept *any* POJO.
296 */
297 @VisibleForTesting
298 fun findRowAdapter(typeMirror: TypeMirror, query: ParsedQuery): RowAdapter? {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800299 if (typeMirror.kind == TypeKind.ERROR) {
300 return null
301 }
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800302 if (typeMirror.kind == TypeKind.DECLARED) {
303 val declared = MoreTypes.asDeclared(typeMirror)
304 if (declared.typeArguments.isNotEmpty()) {
305 // TODO one day support this
306 return null
307 }
Yigit Boyar092164e2017-02-26 13:25:17 -0800308 val resultInfo = query.resultInfo
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800309
310 val (rowAdapter, rowAdapterLogs) = if (resultInfo != null && query.errors.isEmpty()
311 && resultInfo.error == null) {
312 // if result info is not null, first try a pojo row adapter
313 context.collectLogs { subContext ->
314 val pojo = PojoProcessor(
315 baseContext = subContext,
316 element = MoreTypes.asTypeElement(typeMirror),
317 bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
318 parent = null
319 ).process()
320 PojoRowAdapter(
321 context = subContext,
322 info = resultInfo,
323 pojo = pojo,
324 out = typeMirror)
325 }
326 } else {
327 Pair(null, null)
328 }
329
Yigit Boyarf8c36242017-04-10 14:28:17 -0700330 if (rowAdapter == null && query.resultInfo == null) {
331 // we don't know what query returns. Check for entity.
332 val asElement = MoreTypes.asElement(typeMirror)
333 if (asElement.hasAnnotation(Entity::class)) {
334 return EntityRowAdapter(EntityProcessor(context,
335 MoreElements.asType(asElement)).process())
336 }
337 }
338
Yigit Boyarde23b912018-01-03 17:36:40 -0800339 if (rowAdapter != null && rowAdapterLogs?.hasErrors() != true) {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800340 rowAdapterLogs?.writeTo(context.processingEnv)
341 return rowAdapter
342 }
343
Yigit Boyar13a20482017-01-25 17:52:09 -0800344 if ((resultInfo?.columns?.size ?: 1) == 1) {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800345 val singleColumn = findCursorValueReader(typeMirror,
Yigit Boyar275e7082017-02-02 10:00:11 -0800346 resultInfo?.columns?.get(0)?.type)
Yigit Boyar13a20482017-01-25 17:52:09 -0800347 if (singleColumn != null) {
348 return SingleColumnRowAdapter(singleColumn)
349 }
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800350 }
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800351 // if we tried, return its errors
352 if (rowAdapter != null) {
353 rowAdapterLogs?.writeTo(context.processingEnv)
354 return rowAdapter
Yigit Boyar13a20482017-01-25 17:52:09 -0800355 }
Yigit Boyarde23b912018-01-03 17:36:40 -0800356 if (query.runtimeQueryPlaceholder) {
357 // just go w/ pojo and hope for the best. this happens for @RawQuery where we
358 // try to guess user's intention and hope that their query fits the result.
359 val pojo = PojoProcessor(
360 baseContext = context,
361 element = MoreTypes.asTypeElement(typeMirror),
362 bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
363 parent = null
364 ).process()
365 return PojoRowAdapter(
366 context = context,
367 info = null,
368 pojo = pojo,
369 out = typeMirror)
370 }
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800371 return null
372 } else {
Yigit Boyar8a9e8c02017-02-28 15:55:33 -0800373 val singleColumn = findCursorValueReader(typeMirror, null) ?: return null
Yigit Boyarefaf86a2016-12-02 15:16:35 -0800374 return SingleColumnRowAdapter(singleColumn)
375 }
376 }
377
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800378 fun findQueryParameterAdapter(typeMirror: TypeMirror): QueryParameterAdapter? {
Yigit Boyar250a3e62016-11-29 18:06:52 -0800379 if (MoreTypes.isType(typeMirror)
380 && (MoreTypes.isTypeOf(java.util.List::class.java, typeMirror)
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800381 || MoreTypes.isTypeOf(java.util.Set::class.java, typeMirror))) {
Yigit Boyar250a3e62016-11-29 18:06:52 -0800382 val declared = MoreTypes.asDeclared(typeMirror)
Yigit Boyar275e7082017-02-02 10:00:11 -0800383 val binder = findStatementValueBinder(declared.typeArguments.first(),
Yigit Boyar2fb00f12017-12-15 13:18:33 -0800384 null)
385 if (binder != null) {
386 return CollectionQueryParameterAdapter(binder)
387 } else {
388 // maybe user wants to convert this collection themselves. look for a match
389 val collectionBinder = findStatementValueBinder(typeMirror, null) ?: return null
390 return BasicQueryParameterAdapter(collectionBinder)
391 }
Yuichi Arakibccb62d2017-06-26 18:19:52 +0900392 } else if (typeMirror is ArrayType && typeMirror.componentType.kind != TypeKind.BYTE) {
Yigit Boyar250a3e62016-11-29 18:06:52 -0800393 val component = typeMirror.componentType
Yigit Boyar275e7082017-02-02 10:00:11 -0800394 val binder = findStatementValueBinder(component, null) ?: return null
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800395 return ArrayQueryParameterAdapter(binder)
Yigit Boyar250a3e62016-11-29 18:06:52 -0800396 } else {
Yigit Boyar275e7082017-02-02 10:00:11 -0800397 val binder = findStatementValueBinder(typeMirror, null) ?: return null
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800398 return BasicQueryParameterAdapter(binder)
Yigit Boyar250a3e62016-11-29 18:06:52 -0800399 }
400 }
401
Yigit Boyar8e543c42016-11-29 16:49:46 -0800402 private fun findTypeConverter(input: TypeMirror, outputs: List<TypeMirror>): TypeConverter? {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800403 return findTypeConverter(listOf(input), outputs)
404 }
405
Chris Craik9fd8e612017-06-23 14:07:04 -0700406 private fun findTypeConverter(input: List<TypeMirror>, output: TypeMirror): TypeConverter? {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800407 return findTypeConverter(input, listOf(output))
408 }
409
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800410 private fun findTypeConverter(
411 inputs: List<TypeMirror>, outputs: List<TypeMirror>): TypeConverter? {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800412 if (inputs.isEmpty()) {
413 return null
Yigit Boyar250a3e62016-11-29 18:06:52 -0800414 }
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800415 val types = context.processingEnv.typeUtils
416 inputs.forEach { input ->
417 if (outputs.any { output -> types.isSameType(input, output) }) {
418 return NoOpConverter(input)
419 }
420 }
421
Yigit Boyar8e543c42016-11-29 16:49:46 -0800422 val excludes = arrayListOf<TypeMirror>()
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800423
Yigit Boyar8e543c42016-11-29 16:49:46 -0800424 val queue = LinkedList<TypeConverter>()
Aurimas Liutikas6f1f5562017-11-17 10:05:57 -0800425 fun exactMatch(candidates: List<TypeConverter>): TypeConverter? {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800426 return candidates.firstOrNull {
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800427 outputs.any { output -> types.isSameType(output, it.to) }
428 }
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800429 }
430 inputs.forEach { input ->
431 val candidates = getAllTypeConverters(input, excludes)
Chris Craik9fd8e612017-06-23 14:07:04 -0700432 val match = exactMatch(candidates)
Yigit Boyar8e543c42016-11-29 16:49:46 -0800433 if (match != null) {
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800434 return match
Yigit Boyar8e543c42016-11-29 16:49:46 -0800435 }
436 candidates.forEach {
437 excludes.add(it.to)
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800438 queue.add(it)
Yigit Boyar8e543c42016-11-29 16:49:46 -0800439 }
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800440 }
441 excludes.addAll(inputs)
442 while (queue.isNotEmpty()) {
443 val prev = queue.pop()
444 val from = prev.to
445 val candidates = getAllTypeConverters(from, excludes)
Chris Craik9fd8e612017-06-23 14:07:04 -0700446 val match = exactMatch(candidates)
Yigit Boyarfb4fcc82017-02-03 15:22:01 -0800447 if (match != null) {
448 return CompositeTypeConverter(prev, match)
449 }
450 candidates.forEach {
451 excludes.add(it.to)
452 queue.add(CompositeTypeConverter(prev, it))
453 }
454 }
Yigit Boyar8e543c42016-11-29 16:49:46 -0800455 return null
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800456 }
457
Yigit Boyar8e543c42016-11-29 16:49:46 -0800458 private fun getAllColumnAdapters(input: TypeMirror): List<ColumnTypeAdapter> {
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800459 return columnTypeAdapters.filter {
Yigit Boyar250a3e62016-11-29 18:06:52 -0800460 context.processingEnv.typeUtils.isSameType(input, it.out)
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800461 }
462 }
463
Yigit Boyar846dfcf2017-01-16 18:05:20 -0800464 private fun getAllTypeConverters(input: TypeMirror, excludes: List<TypeMirror>):
Yigit Boyar8e543c42016-11-29 16:49:46 -0800465 List<TypeConverter> {
Yigit Boyar250a3e62016-11-29 18:06:52 -0800466 val types = context.processingEnv.typeUtils
Yigit Boyar2fb00f12017-12-15 13:18:33 -0800467 // for input, check assignability because it defines whether we can use the method or not.
468 // for excludes, use exact match
Yigit Boyar8e543c42016-11-29 16:49:46 -0800469 return typeConverters.filter { converter ->
Yigit Boyar2fb00f12017-12-15 13:18:33 -0800470 types.isAssignable(input, converter.from) &&
Yigit Boyar8e543c42016-11-29 16:49:46 -0800471 !excludes.any { types.isSameType(it, converter.to) }
Yigit Boyar2259e4d2016-11-25 18:26:10 -0800472 }
473 }
474}