1 /* <lambda>null2 * Copyright (C) 2017 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.processor 18 19 import androidx.room.TypeConverter 20 import androidx.room.TypeConverters 21 import androidx.room.ext.hasAnnotation 22 import androidx.room.ext.hasAnyOf 23 import androidx.room.ext.toListOfClassTypes 24 import androidx.room.ext.typeName 25 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_BAD_RETURN_TYPE 26 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_EMPTY_CLASS 27 import androidx.room.processor.ProcessorErrors 28 .TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR 29 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_BE_PUBLIC 30 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_MUST_RECEIVE_1_PARAM 31 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC 32 import androidx.room.solver.types.CustomTypeConverterWrapper 33 import androidx.room.vo.CustomTypeConverter 34 import com.google.auto.common.AnnotationMirrors 35 import com.google.auto.common.MoreElements 36 import com.google.auto.common.MoreTypes 37 import java.util.LinkedHashSet 38 import javax.lang.model.element.Element 39 import javax.lang.model.element.ExecutableElement 40 import javax.lang.model.element.Modifier 41 import javax.lang.model.element.TypeElement 42 import javax.lang.model.type.DeclaredType 43 import javax.lang.model.type.TypeKind 44 import javax.lang.model.type.TypeMirror 45 import javax.lang.model.util.ElementFilter 46 47 /** 48 * Processes classes that are referenced in TypeConverters annotations. 49 */ 50 class CustomConverterProcessor(val context: Context, val element: TypeElement) { 51 companion object { 52 private val INVALID_RETURN_TYPES = setOf(TypeKind.ERROR, TypeKind.VOID, TypeKind.NONE) 53 fun findConverters(context: Context, element: Element): ProcessResult { 54 val annotation = MoreElements.getAnnotationMirror(element, 55 TypeConverters::class.java).orNull() 56 return annotation?.let { 57 val classes = AnnotationMirrors.getAnnotationValue(annotation, "value") 58 ?.toListOfClassTypes() 59 ?.filter { 60 MoreTypes.isType(it) 61 }?.mapTo(LinkedHashSet(), { it }) ?: LinkedHashSet<TypeMirror>() 62 val converters = classes 63 .flatMap { 64 CustomConverterProcessor(context, MoreTypes.asTypeElement(it)) 65 .process() 66 } 67 converters.let { 68 reportDuplicates(context, converters) 69 } 70 ProcessResult(classes, converters.map(::CustomTypeConverterWrapper)) 71 } ?: ProcessResult.EMPTY 72 } 73 74 fun reportDuplicates(context: Context, converters: List<CustomTypeConverter>) { 75 val groupedByFrom = converters.groupBy { it.from.typeName() } 76 groupedByFrom.forEach { 77 it.value.groupBy { it.to.typeName() }.forEach { 78 if (it.value.size > 1) { 79 it.value.forEach { converter -> 80 context.logger.e(converter.method, ProcessorErrors 81 .duplicateTypeConverters(it.value.minus(converter))) 82 } 83 } 84 } 85 } 86 } 87 } 88 89 fun process(): List<CustomTypeConverter> { 90 // using element utils instead of MoreElements to include statics. 91 val methods = ElementFilter 92 .methodsIn(context.processingEnv.elementUtils.getAllMembers(element)) 93 val declaredType = MoreTypes.asDeclared(element.asType()) 94 val converterMethods = methods.filter { 95 it.hasAnnotation(TypeConverter::class) 96 } 97 context.checker.check(converterMethods.isNotEmpty(), element, TYPE_CONVERTER_EMPTY_CLASS) 98 val allStatic = converterMethods.all { it.modifiers.contains(Modifier.STATIC) } 99 val constructors = ElementFilter.constructorsIn( 100 context.processingEnv.elementUtils.getAllMembers(element)) 101 context.checker.check(allStatic || constructors.isEmpty() || constructors.any { 102 it.parameters.isEmpty() 103 }, element, TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR) 104 return converterMethods.mapNotNull { processMethod(declaredType, it) } 105 } 106 107 private fun processMethod( 108 container: DeclaredType, methodElement: ExecutableElement): CustomTypeConverter? { 109 val asMember = context.processingEnv.typeUtils.asMemberOf(container, methodElement) 110 val executableType = MoreTypes.asExecutable(asMember) 111 val returnType = executableType.returnType 112 val invalidReturnType = INVALID_RETURN_TYPES.contains(returnType.kind) 113 context.checker.check(methodElement.hasAnyOf(Modifier.PUBLIC), methodElement, 114 TYPE_CONVERTER_MUST_BE_PUBLIC) 115 if (invalidReturnType) { 116 context.logger.e(methodElement, TYPE_CONVERTER_BAD_RETURN_TYPE) 117 return null 118 } 119 val returnTypeName = returnType.typeName() 120 context.checker.notUnbound(returnTypeName, methodElement, 121 TYPE_CONVERTER_UNBOUND_GENERIC) 122 val params = methodElement.parameters 123 if (params.size != 1) { 124 context.logger.e(methodElement, TYPE_CONVERTER_MUST_RECEIVE_1_PARAM) 125 return null 126 } 127 val param = params.map { 128 MoreTypes.asMemberOf(context.processingEnv.typeUtils, container, it) 129 }.first() 130 context.checker.notUnbound(param.typeName(), params[0], TYPE_CONVERTER_UNBOUND_GENERIC) 131 return CustomTypeConverter(container, methodElement, param, returnType) 132 } 133 134 /** 135 * Order of classes is important hence they are a LinkedHashSet not a set. 136 */ 137 open class ProcessResult( 138 val classes: LinkedHashSet<TypeMirror>, 139 val converters: List<CustomTypeConverterWrapper> 140 ) { 141 object EMPTY : ProcessResult(LinkedHashSet(), emptyList()) 142 operator fun plus(other: ProcessResult): ProcessResult { 143 val newClasses = LinkedHashSet<TypeMirror>() 144 newClasses.addAll(classes) 145 newClasses.addAll(other.classes) 146 return ProcessResult(newClasses, converters + other.converters) 147 } 148 } 149 } 150