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