• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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