| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.room.processor |
| |
| import androidx.room.RawQuery |
| import androidx.room.Transaction |
| import androidx.room.ext.SupportDbTypeNames |
| import androidx.room.ext.isEntityElement |
| import androidx.room.parser.SqlParser |
| import androidx.room.compiler.processing.XDeclaredType |
| import androidx.room.compiler.processing.XMethodElement |
| import androidx.room.compiler.processing.XVariableElement |
| import androidx.room.processor.ProcessorErrors.RAW_QUERY_STRING_PARAMETER_REMOVED |
| import androidx.room.vo.RawQueryMethod |
| |
| class RawQueryMethodProcessor( |
| baseContext: Context, |
| val containing: XDeclaredType, |
| val executableElement: XMethodElement |
| ) { |
| val context = baseContext.fork(executableElement) |
| |
| fun process(): RawQueryMethod { |
| val delegate = MethodProcessorDelegate.createFor(context, containing, executableElement) |
| val returnType = delegate.extractReturnType() |
| |
| context.checker.check(executableElement.hasAnnotation(RawQuery::class), executableElement, |
| ProcessorErrors.MISSING_RAWQUERY_ANNOTATION) |
| |
| val returnTypeName = returnType.typeName |
| context.checker.notUnbound(returnTypeName, executableElement, |
| ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS) |
| val observedTableNames = processObservedTables() |
| val query = SqlParser.rawQueryForTables(observedTableNames) |
| // build the query but don't calculate result info since we just guessed it. |
| val resultBinder = delegate.findResultBinder(returnType, query) |
| val runtimeQueryParam = findRuntimeQueryParameter(delegate.extractParams()) |
| val inTransaction = executableElement.hasAnnotation(Transaction::class) |
| val rawQueryMethod = RawQueryMethod( |
| element = executableElement, |
| name = executableElement.name, |
| observedTableNames = observedTableNames, |
| returnType = returnType, |
| runtimeQueryParam = runtimeQueryParam, |
| inTransaction = inTransaction, |
| queryResultBinder = resultBinder |
| ) |
| // TODO: Lift this restriction, to allow for INSERT, UPDATE and DELETE raw statements. |
| context.checker.check(rawQueryMethod.returnsValue, executableElement, |
| ProcessorErrors.RAW_QUERY_BAD_RETURN_TYPE) |
| return rawQueryMethod |
| } |
| |
| private fun processObservedTables(): Set<String> { |
| val annotation = executableElement.toAnnotationBox(RawQuery::class) |
| return annotation?.getAsTypeList("observedEntities") |
| ?.map { |
| it.asTypeElement() |
| } |
| ?.flatMap { |
| if (it.isEntityElement()) { |
| val entity = EntityProcessor( |
| context = context, |
| element = it |
| ).process() |
| arrayListOf(entity.tableName) |
| } else { |
| val pojo = PojoProcessor.createFor( |
| context = context, |
| element = it, |
| bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR, |
| parent = null |
| ).process() |
| val tableNames = pojo.accessedTableNames() |
| // if it is empty, report error as it does not make sense |
| if (tableNames.isEmpty()) { |
| context.logger.e(executableElement, |
| ProcessorErrors.rawQueryBadEntity(it.type.typeName)) |
| } |
| tableNames |
| } |
| }?.toSet() ?: emptySet() |
| } |
| |
| private fun findRuntimeQueryParameter( |
| extractParams: List<XVariableElement> |
| ): RawQueryMethod.RuntimeQueryParameter? { |
| if (extractParams.size == 1 && !executableElement.isVarArgs()) { |
| val param = extractParams.first().asMemberOf(containing) |
| val processingEnv = context.processingEnv |
| val supportQueryType = processingEnv.requireType(SupportDbTypeNames.QUERY) |
| val isSupportSql = supportQueryType.isAssignableFrom(param) |
| if (isSupportSql) { |
| return RawQueryMethod.RuntimeQueryParameter( |
| paramName = extractParams[0].name, |
| type = supportQueryType.typeName) |
| } |
| val stringType = processingEnv.requireType("java.lang.String") |
| val isString = stringType.isAssignableFrom(param) |
| if (isString) { |
| // special error since this was initially allowed but removed in 1.1 beta1 |
| context.logger.e(executableElement, RAW_QUERY_STRING_PARAMETER_REMOVED) |
| return null |
| } |
| } |
| context.logger.e(executableElement, ProcessorErrors.RAW_QUERY_BAD_PARAMS) |
| return null |
| } |
| } |