1 /*
2  * Copyright 2017 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.navigation.safe.args.generator
18 
19 import java.io.Reader
20 import org.xmlpull.v1.XmlPullParser
21 import org.xmlpull.v1.XmlPullParserFactory
22 
23 internal class XmlPositionParser(private val name: String, reader: Reader, val logger: NavLogger) {
24     private var startLine = 0
25     private var startColumn = 0
26     private val parser: XmlPullParser =
<lambda>null27         XmlPullParserFactory.newInstance().newPullParser().apply {
28             setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true)
29             setInput(reader)
30         }
31 
namenull32     fun name(): String = parser.name
33 
34     fun traverseStartTags(onStartTag: () -> Boolean) {
35         while (parser.eventType != XmlPullParser.END_DOCUMENT) {
36             val processedLine = parser.lineNumber
37             val processedColumn = parser.columnNumber
38             if (parser.eventType == XmlPullParser.START_TAG) {
39                 if (onStartTag()) {
40                     return
41                 }
42             }
43 
44             if (processedLine == parser.lineNumber && processedColumn == parser.columnNumber) {
45                 // otherwise onStart already called next() and we need to try to process current
46                 // node
47                 nextToken()
48             }
49         }
50     }
51 
nextTokennull52     private fun nextToken() {
53         startLine = parser.lineNumber
54         startColumn = parser.columnNumber
55         parser.nextToken()
56     }
57 
xmlPositionnull58     fun xmlPosition() = XmlPosition(name, startLine, startColumn - 1)
59 
60     fun traverseInnerStartTags(onStartTag: () -> Unit) {
61         val innerDepth = parser.depth + 1
62         nextToken()
63         traverseStartTags {
64             if (innerDepth == parser.depth) {
65                 onStartTag()
66             }
67             parser.depth < innerDepth
68         }
69     }
70 
attrValuenull71     fun attrValue(namespace: String, name: String): String? =
72         (0 until parser.attributeCount)
73             .find {
74                 parser.getAttributeNamespace(it) == namespace && name == parser.getAttributeName(it)
75             }
<lambda>null76             ?.let { parser.getAttributeValue(it) }
77 
attrValueOrErrornull78     fun attrValueOrError(namespace: String, attrName: String): String? {
79         val value = attrValue(namespace, attrName)
80         if (value == null) {
81             logger.error(mandatoryAttrMissingError(name(), attrName), xmlPosition())
82         }
83         return value
84     }
85 }
86 
mandatoryAttrMissingErrornull87 internal fun mandatoryAttrMissingError(tag: String, attr: String) =
88     "Mandatory attribute '$attr' for tag '$tag' is missing."
89