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 @file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") 18 19 package androidx.room.processor 20 21 import androidx.annotation.VisibleForTesting 22 import androidx.room.Insert 23 import androidx.room.OnConflictStrategy.IGNORE 24 import androidx.room.OnConflictStrategy.REPLACE 25 import androidx.room.vo.InsertionMethod 26 import androidx.room.vo.InsertionMethod.Type 27 import androidx.room.vo.ShortcutQueryParameter 28 import com.google.auto.common.MoreTypes 29 import com.squareup.javapoet.TypeName 30 import javax.lang.model.element.ExecutableElement 31 import javax.lang.model.type.DeclaredType 32 import javax.lang.model.type.TypeKind 33 import javax.lang.model.type.TypeKind.LONG 34 import javax.lang.model.type.TypeKind.VOID 35 import javax.lang.model.type.TypeMirror 36 37 class InsertionMethodProcessor(baseContext: Context, 38 val containing: DeclaredType, 39 val executableElement: ExecutableElement) { 40 val context = baseContext.fork(executableElement) 41 fun process(): InsertionMethod { 42 val delegate = ShortcutMethodProcessor(context, containing, executableElement) 43 val annotation = delegate.extractAnnotation(Insert::class, 44 ProcessorErrors.MISSING_INSERT_ANNOTATION) 45 46 val onConflict = OnConflictProcessor.extractFrom(annotation) 47 context.checker.check(onConflict <= IGNORE && onConflict >= REPLACE, 48 executableElement, ProcessorErrors.INVALID_ON_CONFLICT_VALUE) 49 50 val returnType = delegate.extractReturnType() 51 val returnTypeName = TypeName.get(returnType) 52 context.checker.notUnbound(returnTypeName, executableElement, 53 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_INSERTION_METHODS) 54 55 val (entities, params) = delegate.extractParams( 56 missingParamError = ProcessorErrors 57 .INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT 58 ) 59 60 // TODO we can support more types 61 var insertionType = getInsertionType(returnType) 62 context.checker.check(insertionType != null, executableElement, 63 ProcessorErrors.INVALID_INSERTION_METHOD_RETURN_TYPE) 64 65 if (insertionType != null) { 66 val acceptable = acceptableTypes(params) 67 if (insertionType !in acceptable) { 68 context.logger.e(executableElement, 69 ProcessorErrors.insertionMethodReturnTypeMismatch( 70 insertionType.returnTypeName, 71 acceptable.map { it.returnTypeName })) 72 // clear it, no reason to generate code for it. 73 insertionType = null 74 } 75 } 76 return InsertionMethod( 77 element = executableElement, 78 name = executableElement.simpleName.toString(), 79 returnType = returnType, 80 entities = entities, 81 parameters = params, 82 onConflict = onConflict, 83 insertionType = insertionType 84 ) 85 } 86 87 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") 88 private fun getInsertionType(returnType: TypeMirror): InsertionMethod.Type? { 89 // TODO we need to support more types here. 90 fun isLongPrimitiveType(typeMirror: TypeMirror) = typeMirror.kind == LONG 91 92 fun isLongBoxType(typeMirror: TypeMirror) = 93 MoreTypes.isType(typeMirror) && 94 MoreTypes.isTypeOf(java.lang.Long::class.java, typeMirror) 95 96 fun isLongType(typeMirror: TypeMirror) = 97 isLongPrimitiveType(typeMirror) || isLongBoxType(typeMirror) 98 99 return if (returnType.kind == VOID) { 100 Type.INSERT_VOID 101 } else if (returnType.kind == TypeKind.ARRAY) { 102 val arrayType = MoreTypes.asArray(returnType) 103 val param = arrayType.componentType 104 if (isLongPrimitiveType(param)) { 105 Type.INSERT_ID_ARRAY 106 } else if (isLongBoxType(param)) { 107 Type.INSERT_ID_ARRAY_BOX 108 } else { 109 null 110 } 111 } else if (MoreTypes.isType(returnType) 112 && MoreTypes.isTypeOf(List::class.java, returnType)) { 113 val declared = MoreTypes.asDeclared(returnType) 114 val param = declared.typeArguments.first() 115 if (isLongBoxType(param)) { 116 Type.INSERT_ID_LIST 117 } else { 118 null 119 } 120 } else if (isLongType(returnType)) { 121 Type.INSERT_SINGLE_ID 122 } else { 123 null 124 } 125 } 126 127 companion object { 128 @VisibleForTesting 129 val VOID_SET by lazy { setOf(Type.INSERT_VOID) } 130 @VisibleForTesting 131 val SINGLE_ITEM_SET by lazy { setOf(Type.INSERT_VOID, Type.INSERT_SINGLE_ID) } 132 @VisibleForTesting 133 val MULTIPLE_ITEM_SET by lazy { 134 setOf(Type.INSERT_VOID, Type.INSERT_ID_ARRAY, Type.INSERT_ID_ARRAY_BOX, 135 Type.INSERT_ID_LIST) 136 } 137 fun acceptableTypes(params: List<ShortcutQueryParameter>): Set<InsertionMethod.Type> { 138 if (params.isEmpty()) { 139 return VOID_SET 140 } 141 if (params.size > 1) { 142 return VOID_SET 143 } 144 if (params.first().isMultiple) { 145 return MULTIPLE_ITEM_SET 146 } else { 147 return SINGLE_ITEM_SET 148 } 149 } 150 } 151 } 152