1275e7088223c097c1a2df718455bede42bc9efedYigit Boyar/*
2275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * Copyright (C) 2017 The Android Open Source Project
3275e7088223c097c1a2df718455bede42bc9efedYigit Boyar *
4275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * you may not use this file except in compliance with the License.
6275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * You may obtain a copy of the License at
7275e7088223c097c1a2df718455bede42bc9efedYigit Boyar *
8275e7088223c097c1a2df718455bede42bc9efedYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9275e7088223c097c1a2df718455bede42bc9efedYigit Boyar *
10275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * Unless required by applicable law or agreed to in writing, software
11275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * See the License for the specific language governing permissions and
14275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * limitations under the License.
15275e7088223c097c1a2df718455bede42bc9efedYigit Boyar */
16275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
1764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarpackage android.arch.persistence.room.processor
18275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
1964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.TypeConverter
2064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.TypeConverters
2164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.hasAnnotation
2264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.hasAnyOf
2364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.toListOfClassTypes
2464db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.ext.typeName
2564db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_BAD_RETURN_TYPE
2664db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_EMPTY_CLASS
2764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors
2864db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyar        .TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR
2964db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_BE_PUBLIC
3064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_RECEIVE_1_PARAM
3164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC
3264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.solver.types.CustomTypeConverterWrapper
3364db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.CustomTypeConverter
34275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport com.google.auto.common.AnnotationMirrors
35275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport com.google.auto.common.MoreElements
36275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport com.google.auto.common.MoreTypes
375124503104860e68981cc3e3092b95932586f66fYigit Boyarimport java.util.LinkedHashSet
38275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.element.Element
39275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.element.ExecutableElement
40275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.element.Modifier
41275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.element.TypeElement
42275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.type.DeclaredType
43275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.type.TypeKind
445124503104860e68981cc3e3092b95932586f66fYigit Boyarimport javax.lang.model.type.TypeMirror
45275e7088223c097c1a2df718455bede42bc9efedYigit Boyarimport javax.lang.model.util.ElementFilter
46275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
47275e7088223c097c1a2df718455bede42bc9efedYigit Boyar/**
48275e7088223c097c1a2df718455bede42bc9efedYigit Boyar * Processes classes that are referenced in TypeConverters annotations.
49275e7088223c097c1a2df718455bede42bc9efedYigit Boyar */
50275e7088223c097c1a2df718455bede42bc9efedYigit Boyarclass CustomConverterProcessor(val context: Context, val element: TypeElement) {
51275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    companion object {
52275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        private val INVALID_RETURN_TYPES = setOf(TypeKind.ERROR, TypeKind.VOID, TypeKind.NONE)
535124503104860e68981cc3e3092b95932586f66fYigit Boyar        fun findConverters(context: Context, element: Element): ProcessResult {
54275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            val annotation = MoreElements.getAnnotationMirror(element,
55275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                    TypeConverters::class.java).orNull()
56275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            return annotation?.let {
575124503104860e68981cc3e3092b95932586f66fYigit Boyar                val classes = AnnotationMirrors.getAnnotationValue(annotation, "value")
58275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                        ?.toListOfClassTypes()
59275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                        ?.filter {
60275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                            MoreTypes.isType(it)
615124503104860e68981cc3e3092b95932586f66fYigit Boyar                        }?.mapTo(LinkedHashSet(), {it}) ?: LinkedHashSet<TypeMirror>()
625124503104860e68981cc3e3092b95932586f66fYigit Boyar                val converters = classes
635124503104860e68981cc3e3092b95932586f66fYigit Boyar                        .flatMap {
64275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                            CustomConverterProcessor(context, MoreTypes.asTypeElement(it))
65275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                                    .process()
66275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                        }
675124503104860e68981cc3e3092b95932586f66fYigit Boyar                converters.let {
68275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                    reportDuplicates(context, converters)
69275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                }
705124503104860e68981cc3e3092b95932586f66fYigit Boyar                ProcessResult(classes, converters.map(::CustomTypeConverterWrapper))
715124503104860e68981cc3e3092b95932586f66fYigit Boyar            } ?: ProcessResult.EMPTY
72275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }
73275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
74275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        fun reportDuplicates(context: Context, converters : List<CustomTypeConverter>) {
75275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            val groupedByFrom = converters.groupBy { it.from.typeName() }
76275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            groupedByFrom.forEach {
77275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                it.value.groupBy { it.to.typeName() }.forEach {
78275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                    if (it.value.size > 1) {
79275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                        it.value.forEach { converter ->
80275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                            context.logger.e(converter.method, ProcessorErrors
81275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                                    .duplicateTypeConverters(it.value.minus(converter)))
82275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                        }
83275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                    }
84275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                }
85275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            }
86275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }
87275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    }
88275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
89275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    fun process(): List<CustomTypeConverter> {
90275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        // using element utils instead of MoreElements to include statics.
91275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val methods = ElementFilter
92275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                .methodsIn(context.processingEnv.elementUtils.getAllMembers(element))
93275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val declaredType = MoreTypes.asDeclared(element.asType())
94275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val converterMethods = methods.filter {
95275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            it.hasAnnotation(TypeConverter::class)
96275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }
97275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        context.checker.check(converterMethods.isNotEmpty(), element, TYPE_CONVERTER_EMPTY_CLASS)
98275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val allStatic = converterMethods.all { it.modifiers.contains(Modifier.STATIC) }
99275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val constructors = ElementFilter.constructorsIn(
100275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                context.processingEnv.elementUtils.getAllMembers(element))
101275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        context.checker.check(allStatic || constructors.isEmpty() || constructors.any {
102275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            it.parameters.isEmpty()
103275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }, element, TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR)
104275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        return converterMethods.map {
105275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            processMethod(declaredType, it)
106275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }.filterNotNull()
107275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    }
108275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
109275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    private fun processMethod(container: DeclaredType, methodElement: ExecutableElement)
110275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            : CustomTypeConverter? {
111275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val asMember = context.processingEnv.typeUtils.asMemberOf(container, methodElement)
112275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val executableType = MoreTypes.asExecutable(asMember)
113275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val returnType = executableType.returnType
114275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val invalidReturnType = INVALID_RETURN_TYPES.contains(returnType.kind)
115275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        context.checker.check(methodElement.hasAnyOf(Modifier.PUBLIC), methodElement,
116275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                TYPE_CONVERTER_MUST_BE_PUBLIC)
117275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        if (invalidReturnType) {
118275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            context.logger.e(methodElement, TYPE_CONVERTER_BAD_RETURN_TYPE)
119275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            return null
120275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }
121275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val returnTypeName = returnType.typeName()
122275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        context.checker.notUnbound(returnTypeName, methodElement,
123275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                TYPE_CONVERTER_UNBOUND_GENERIC)
124275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val params = methodElement.parameters
125275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        if (params.size != 1) {
126275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            context.logger.e(methodElement, TYPE_CONVERTER_MUST_RECEIVE_1_PARAM)
127275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            return null
128275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }
129275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        val param = params.map {
130275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            MoreTypes.asMemberOf(context.processingEnv.typeUtils, container, it)
131275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        }.first()
132275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        context.checker.notUnbound(param.typeName(), params[0], TYPE_CONVERTER_UNBOUND_GENERIC)
133275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        return CustomTypeConverter(container, methodElement, param, returnType)
134275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    }
1355124503104860e68981cc3e3092b95932586f66fYigit Boyar
1365124503104860e68981cc3e3092b95932586f66fYigit Boyar    /**
1375124503104860e68981cc3e3092b95932586f66fYigit Boyar     * Order of classes is important hence they are a LinkedHashSet not a set.
1385124503104860e68981cc3e3092b95932586f66fYigit Boyar     */
1395124503104860e68981cc3e3092b95932586f66fYigit Boyar    open class ProcessResult(val classes: LinkedHashSet<TypeMirror>,
1405124503104860e68981cc3e3092b95932586f66fYigit Boyar                             val converters: List<CustomTypeConverterWrapper>) {
1415124503104860e68981cc3e3092b95932586f66fYigit Boyar        object EMPTY : ProcessResult(LinkedHashSet(), emptyList())
1425124503104860e68981cc3e3092b95932586f66fYigit Boyar        operator fun plus(other : ProcessResult) : ProcessResult {
1435124503104860e68981cc3e3092b95932586f66fYigit Boyar            val newClasses = LinkedHashSet<TypeMirror>()
1445124503104860e68981cc3e3092b95932586f66fYigit Boyar            newClasses.addAll(classes)
1455124503104860e68981cc3e3092b95932586f66fYigit Boyar            newClasses.addAll(other.classes)
1465124503104860e68981cc3e3092b95932586f66fYigit Boyar            return ProcessResult(newClasses, converters + other.converters)
1475124503104860e68981cc3e3092b95932586f66fYigit Boyar        }
1485124503104860e68981cc3e3092b95932586f66fYigit Boyar    }
149275e7088223c097c1a2df718455bede42bc9efedYigit Boyar}
150