173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets/* 273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * Copyright 2017 The Android Open Source Project 373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * 473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * Licensed under the Apache License, Version 2.0 (the "License"); 573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * you may not use this file except in compliance with the License. 673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * You may obtain a copy of the License at 773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * 873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * http://www.apache.org/licenses/LICENSE-2.0 973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * 1073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * Unless required by applicable law or agreed to in writing, software 1173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * distributed under the License is distributed on an "AS IS" BASIS, 1273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * See the License for the specific language governing permissions and 1473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets * limitations under the License. 1573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets */ 1673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 1773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinetspackage androidx.navigation.safe.args.generator 1873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 1973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinetsimport org.xmlpull.v1.XmlPullParser 2073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinetsimport org.xmlpull.v1.XmlPullParserFactory 2173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinetsimport java.io.Reader 2273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 2328f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinetsinternal class XmlPositionParser(private val name: String, reader: Reader, val logger: NavLogger) { 2473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets private var startLine = 0 2573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets private var startColumn = 0 2673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets private val parser: XmlPullParser = XmlPullParserFactory.newInstance().newPullParser().apply { 2773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true) 2873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets setInput(reader) 2973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 3073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 3173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets fun name(): String = parser.name 3273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 3373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets fun traverseStartTags(onStartTag: () -> Boolean) { 3473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets while (parser.eventType != XmlPullParser.END_DOCUMENT) { 3573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets val processedLine = parser.lineNumber 3673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets val processedColumn = parser.columnNumber 3773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets if (parser.eventType == XmlPullParser.START_TAG) { 3873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets if (onStartTag()) { 3973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets return 4073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 4173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 4273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 4373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets if (processedLine == parser.lineNumber && processedColumn == parser.columnNumber) { 4473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets // otherwise onStart already called next() and we need to try to process current node 4573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets nextToken() 4673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 4773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 4873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 4973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 5073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets private fun nextToken() { 5173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets startLine = parser.lineNumber 5273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets startColumn = parser.columnNumber 5373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets parser.nextToken() 5473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 5573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 5628f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets fun xmlPosition() = XmlPosition(name, startLine, startColumn - 1) 5773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 5873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets fun traverseInnerStartTags(onStartTag: () -> Unit) { 5973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets val innerDepth = parser.depth + 1 6073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets nextToken() 6173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets traverseStartTags { 6273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets if (innerDepth == parser.depth) { 6373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets onStartTag() 6473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 6573d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets parser.depth < innerDepth 6673d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 6773d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets } 6873d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 6973d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets fun attrValue(namespace: String, name: String): String? = 7073d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets (0 until parser.attributeCount).find { 7173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets parser.getAttributeNamespace(it) == namespace && name == parser.getAttributeName(it) 7273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets }?.let { parser.getAttributeValue(it) } 7373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 7428f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets fun attrValueOrError(namespace: String, attrName: String): String? { 7528f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets val value = attrValue(namespace, attrName) 7628f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets if (value == null) { 7728f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets logger.error(mandatoryAttrMissingError(name(), attrName), xmlPosition()) 7828f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets } 7928f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets return value 8028f10aef67d29479eb0821b63967450e1bcee8e4Sergey Vasilinets } 8173d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets} 8273d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets 8373d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinetsinternal fun mandatoryAttrMissingError(tag: String, attr: String) = 8473d7c77fb8b34198d06bf1d0f672accbdcac61a9Sergey Vasilinets "Mandatory attribute '$attr' for tag '$tag' is missing."