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