1 /* 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 17 package androidx.room.processor 18 19 import androidx.room.Entity 20 import androidx.room.ext.extendsBound 21 import androidx.room.ext.hasAnnotation 22 import androidx.room.vo.ShortcutQueryParameter 23 import com.google.auto.common.MoreTypes 24 import javax.lang.model.element.TypeElement 25 import javax.lang.model.element.VariableElement 26 import javax.lang.model.type.ArrayType 27 import javax.lang.model.type.DeclaredType 28 import javax.lang.model.type.TypeMirror 29 import javax.lang.model.util.ElementFilter 30 31 /** 32 * Processes parameters of methods that are annotated with Insert, Delete. 33 */ 34 class ShortcutParameterProcessor(baseContext: Context, 35 val containing: DeclaredType, 36 val element: VariableElement) { 37 val context = baseContext.fork(element) 38 fun process(): ShortcutQueryParameter { 39 val asMember = MoreTypes.asMemberOf(context.processingEnv.typeUtils, containing, element) 40 val name = element.simpleName.toString() 41 context.checker.check(!name.startsWith("_"), element, 42 ProcessorErrors.QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE) 43 44 val (entityType, isMultiple) = extractEntityType(asMember) 45 context.checker.check(entityType != null, element, 46 ProcessorErrors.CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER) 47 48 return ShortcutQueryParameter( 49 name = name, 50 type = asMember, 51 entityType = entityType, 52 isMultiple = isMultiple 53 ) 54 } 55 56 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") 57 fun extractEntityType(typeMirror: TypeMirror): Pair<TypeMirror?, Boolean> { 58 59 val elementUtils = context.processingEnv.elementUtils 60 val typeUtils = context.processingEnv.typeUtils 61 62 fun verifyAndPair(entityType: TypeMirror, isMultiple: Boolean): Pair<TypeMirror?, Boolean> { 63 if (!MoreTypes.isType(entityType)) { 64 // kotlin may generate ? extends T so we should reduce it. 65 val boundedVar = entityType.extendsBound() 66 return boundedVar?.let { 67 verifyAndPair(boundedVar, isMultiple) 68 } ?: Pair(null, isMultiple) 69 } 70 val entityElement = MoreTypes.asElement(entityType) 71 return if (entityElement.hasAnnotation(Entity::class)) { 72 Pair(entityType, isMultiple) 73 } else { 74 Pair(null, isMultiple) 75 } 76 } 77 78 fun extractEntityTypeFromIterator(iterableType: DeclaredType): TypeMirror { 79 ElementFilter.methodsIn(elementUtils 80 .getAllMembers(typeUtils.asElement(iterableType) as TypeElement)).forEach { 81 if (it.simpleName.toString() == "iterator") { 82 return MoreTypes.asDeclared(MoreTypes.asExecutable( 83 typeUtils.asMemberOf(iterableType, it)).returnType) 84 .typeArguments.first() 85 } 86 } 87 throw IllegalArgumentException("iterator() not found in Iterable $iterableType") 88 } 89 90 val iterableType = typeUtils.erasure(elementUtils 91 .getTypeElement("java.lang.Iterable").asType()) 92 if (typeUtils.isAssignable(typeMirror, iterableType)) { 93 val declared = MoreTypes.asDeclared(typeMirror) 94 val entity = extractEntityTypeFromIterator(declared) 95 return verifyAndPair(entity, true) 96 } 97 if (typeMirror is ArrayType) { 98 val entity = typeMirror.componentType 99 return verifyAndPair(entity, true) 100 } 101 return verifyAndPair(typeMirror, false) 102 } 103 } 104