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