1 /* <lambda>null2 * 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.parser 18 19 import androidx.room.parser.SectionType.BIND_VAR 20 import androidx.room.parser.SectionType.NEWLINE 21 import androidx.room.parser.SectionType.TEXT 22 import androidx.room.verifier.QueryResultInfo 23 import org.antlr.v4.runtime.tree.TerminalNode 24 25 enum class SectionType { 26 BIND_VAR, 27 TEXT, 28 NEWLINE 29 } 30 31 data class Section(val text: String, val type: SectionType) { 32 companion object { textnull33 fun text(text: String) = Section(text, SectionType.TEXT) 34 fun newline() = Section("", SectionType.NEWLINE) 35 fun bindVar(text: String) = Section(text, SectionType.BIND_VAR) 36 } 37 } 38 39 data class Table(val name: String, val alias: String) 40 41 data class ParsedQuery( 42 val original: String, 43 val type: QueryType, 44 val inputs: List<TerminalNode>, 45 // pairs of table name and alias, 46 val tables: Set<Table>, 47 val syntaxErrors: List<String>, 48 val runtimeQueryPlaceholder: Boolean) { 49 companion object { 50 val STARTS_WITH_NUMBER = "^\\?[0-9]".toRegex() 51 val MISSING = ParsedQuery("missing query", QueryType.UNKNOWN, emptyList(), emptySet(), 52 emptyList(), false) 53 } 54 55 /** 56 * Optional data that might be assigned when the query is parsed inside an annotation processor. 57 * User may turn this off or it might be disabled for any reason so generated code should 58 * always handle not having it. 59 */ 60 var resultInfo: QueryResultInfo? = null 61 62 val sections by lazy { 63 val lines = original.lines() 64 val inputsByLine = inputs.groupBy { it.symbol.line } 65 val sections = arrayListOf<Section>() 66 lines.forEachIndexed { index, line -> 67 var charInLine = 0 68 inputsByLine[index + 1]?.forEach { bindVar -> 69 if (charInLine < bindVar.symbol.charPositionInLine) { 70 sections.add(Section.text(line.substring(charInLine, 71 bindVar.symbol.charPositionInLine))) 72 } 73 sections.add(Section.bindVar(bindVar.text)) 74 charInLine = bindVar.symbol.charPositionInLine + bindVar.symbol.text.length 75 } 76 if (charInLine < line.length) { 77 sections.add(Section.text(line.substring(charInLine))) 78 } 79 if (index + 1 < lines.size) { 80 sections.add(Section.newline()) 81 } 82 } 83 sections 84 } 85 86 val bindSections by lazy { sections.filter { it.type == BIND_VAR } } 87 88 private fun unnamedVariableErrors(): List<String> { 89 val anonymousBindError = if (inputs.any { it.text == "?" }) { 90 arrayListOf(ParserErrors.ANONYMOUS_BIND_ARGUMENT) 91 } else { 92 emptyList<String>() 93 } 94 return anonymousBindError + inputs.filter { 95 it.text.matches(STARTS_WITH_NUMBER) 96 }.map { 97 ParserErrors.cannotUseVariableIndices(it.text, it.symbol.charPositionInLine) 98 } 99 } 100 101 private fun unknownQueryTypeErrors(): List<String> { 102 return if (QueryType.SUPPORTED.contains(type)) { 103 emptyList() 104 } else { 105 listOf(ParserErrors.invalidQueryType(type)) 106 } 107 } 108 109 val errors by lazy { 110 if (syntaxErrors.isNotEmpty()) { 111 // if there is a syntax error, don't report others since they might be misleading. 112 syntaxErrors 113 } else { 114 unnamedVariableErrors() + unknownQueryTypeErrors() 115 } 116 } 117 118 val queryWithReplacedBindParams by lazy { 119 sections.joinToString("") { 120 when (it.type) { 121 TEXT -> it.text 122 BIND_VAR -> "?" 123 NEWLINE -> "\n" 124 } 125 } 126 } 127 } 128