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