| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Logger.h" |
| #include "Util.h" |
| #include "XmlDom.h" |
| #include "XmlPullParser.h" |
| |
| #include <cassert> |
| #include <memory> |
| #include <stack> |
| #include <string> |
| #include <tuple> |
| |
| namespace aapt { |
| namespace xml { |
| |
| constexpr char kXmlNamespaceSep = 1; |
| |
| struct Stack { |
| std::unique_ptr<xml::Node> root; |
| std::stack<xml::Node*> nodeStack; |
| std::u16string pendingComment; |
| }; |
| |
| /** |
| * Extracts the namespace and name of an expanded element or attribute name. |
| */ |
| static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) { |
| const char* p = name; |
| while (*p != 0 && *p != kXmlNamespaceSep) { |
| p++; |
| } |
| |
| if (*p == 0) { |
| outNs->clear(); |
| *outName = util::utf8ToUtf16(name); |
| } else { |
| *outNs = util::utf8ToUtf16(StringPiece(name, (p - name))); |
| *outName = util::utf8ToUtf16(p + 1); |
| } |
| } |
| |
| static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) { |
| node->lineNumber = XML_GetCurrentLineNumber(parser); |
| node->columnNumber = XML_GetCurrentColumnNumber(parser); |
| |
| Node* thisNode = node.get(); |
| if (!stack->nodeStack.empty()) { |
| stack->nodeStack.top()->addChild(std::move(node)); |
| } else { |
| stack->root = std::move(node); |
| } |
| |
| if (thisNode->type != NodeType::kText) { |
| stack->nodeStack.push(thisNode); |
| } |
| } |
| |
| static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| std::unique_ptr<Namespace> ns = util::make_unique<Namespace>(); |
| if (prefix) { |
| ns->namespacePrefix = util::utf8ToUtf16(prefix); |
| } |
| |
| if (uri) { |
| ns->namespaceUri = util::utf8ToUtf16(uri); |
| } |
| |
| addToStack(stack, parser, std::move(ns)); |
| } |
| |
| static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| assert(!stack->nodeStack.empty()); |
| stack->nodeStack.pop(); |
| } |
| |
| static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) { |
| return std::tie(lhs.namespaceUri, lhs.name, lhs.value) < |
| std::tie(rhs.namespaceUri, rhs.name, rhs.value); |
| } |
| |
| static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| std::unique_ptr<Element> el = util::make_unique<Element>(); |
| splitName(name, &el->namespaceUri, &el->name); |
| |
| while (*attrs) { |
| Attribute attribute; |
| splitName(*attrs++, &attribute.namespaceUri, &attribute.name); |
| attribute.value = util::utf8ToUtf16(*attrs++); |
| |
| // Insert in sorted order. |
| auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute, |
| lessAttribute); |
| el->attributes.insert(iter, std::move(attribute)); |
| } |
| |
| el->comment = std::move(stack->pendingComment); |
| addToStack(stack, parser, std::move(el)); |
| } |
| |
| static void XMLCALL endElementHandler(void* userData, const char* name) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| assert(!stack->nodeStack.empty()); |
| stack->nodeStack.top()->comment = std::move(stack->pendingComment); |
| stack->nodeStack.pop(); |
| } |
| |
| static void XMLCALL characterDataHandler(void* userData, const char* s, int len) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| if (!s || len <= 0) { |
| return; |
| } |
| |
| // See if we can just append the text to a previous text node. |
| if (!stack->nodeStack.empty()) { |
| Node* currentParent = stack->nodeStack.top(); |
| if (!currentParent->children.empty()) { |
| Node* lastChild = currentParent->children.back().get(); |
| if (lastChild->type == NodeType::kText) { |
| Text* text = static_cast<Text*>(lastChild); |
| text->text += util::utf8ToUtf16(StringPiece(s, len)); |
| return; |
| } |
| } |
| } |
| |
| std::unique_ptr<Text> text = util::make_unique<Text>(); |
| text->text = util::utf8ToUtf16(StringPiece(s, len)); |
| addToStack(stack, parser, std::move(text)); |
| } |
| |
| static void XMLCALL commentDataHandler(void* userData, const char* comment) { |
| XML_Parser parser = reinterpret_cast<XML_Parser>(userData); |
| Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); |
| |
| if (!stack->pendingComment.empty()) { |
| stack->pendingComment += '\n'; |
| } |
| stack->pendingComment += util::utf8ToUtf16(comment); |
| } |
| |
| std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger) { |
| Stack stack; |
| |
| XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep); |
| XML_SetUserData(parser, &stack); |
| XML_UseParserAsHandlerArg(parser); |
| XML_SetElementHandler(parser, startElementHandler, endElementHandler); |
| XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler); |
| XML_SetCharacterDataHandler(parser, characterDataHandler); |
| XML_SetCommentHandler(parser, commentDataHandler); |
| |
| char buffer[1024]; |
| while (!in->eof()) { |
| in->read(buffer, sizeof(buffer) / sizeof(buffer[0])); |
| if (in->bad() && !in->eof()) { |
| stack.root = {}; |
| logger->error() << strerror(errno) << std::endl; |
| break; |
| } |
| |
| if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) { |
| stack.root = {}; |
| logger->error(XML_GetCurrentLineNumber(parser)) |
| << XML_ErrorString(XML_GetErrorCode(parser)) << std::endl; |
| break; |
| } |
| } |
| |
| XML_ParserFree(parser); |
| return std::move(stack.root); |
| } |
| |
| static void copyAttributes(Element* el, android::ResXMLParser* parser) { |
| const size_t attrCount = parser->getAttributeCount(); |
| if (attrCount > 0) { |
| el->attributes.reserve(attrCount); |
| for (size_t i = 0; i < attrCount; i++) { |
| Attribute attr; |
| size_t len; |
| const char16_t* str16 = parser->getAttributeNamespace(i, &len); |
| if (str16) { |
| attr.namespaceUri.assign(str16, len); |
| } |
| |
| str16 = parser->getAttributeName(i, &len); |
| if (str16) { |
| attr.name.assign(str16, len); |
| } |
| |
| str16 = parser->getAttributeStringValue(i, &len); |
| if (str16) { |
| attr.value.assign(str16, len); |
| } |
| el->attributes.push_back(std::move(attr)); |
| } |
| } |
| } |
| |
| std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger) { |
| std::unique_ptr<Node> root; |
| std::stack<Node*> nodeStack; |
| |
| android::ResXMLTree tree; |
| if (tree.setTo(data, dataLen) != android::NO_ERROR) { |
| return {}; |
| } |
| |
| android::ResXMLParser::event_code_t code; |
| while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT && |
| code != android::ResXMLParser::END_DOCUMENT) { |
| std::unique_ptr<Node> newNode; |
| switch (code) { |
| case android::ResXMLParser::START_NAMESPACE: { |
| std::unique_ptr<Namespace> node = util::make_unique<Namespace>(); |
| size_t len; |
| const char16_t* str16 = tree.getNamespacePrefix(&len); |
| if (str16) { |
| node->namespacePrefix.assign(str16, len); |
| } |
| |
| str16 = tree.getNamespaceUri(&len); |
| if (str16) { |
| node->namespaceUri.assign(str16, len); |
| } |
| newNode = std::move(node); |
| break; |
| } |
| |
| case android::ResXMLParser::START_TAG: { |
| std::unique_ptr<Element> node = util::make_unique<Element>(); |
| size_t len; |
| const char16_t* str16 = tree.getElementNamespace(&len); |
| if (str16) { |
| node->namespaceUri.assign(str16, len); |
| } |
| |
| str16 = tree.getElementName(&len); |
| if (str16) { |
| node->name.assign(str16, len); |
| } |
| |
| copyAttributes(node.get(), &tree); |
| |
| newNode = std::move(node); |
| break; |
| } |
| |
| case android::ResXMLParser::TEXT: { |
| std::unique_ptr<Text> node = util::make_unique<Text>(); |
| size_t len; |
| const char16_t* str16 = tree.getText(&len); |
| if (str16) { |
| node->text.assign(str16, len); |
| } |
| newNode = std::move(node); |
| break; |
| } |
| |
| case android::ResXMLParser::END_NAMESPACE: |
| case android::ResXMLParser::END_TAG: |
| assert(!nodeStack.empty()); |
| nodeStack.pop(); |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| if (newNode) { |
| newNode->lineNumber = tree.getLineNumber(); |
| |
| Node* thisNode = newNode.get(); |
| if (!root) { |
| assert(nodeStack.empty()); |
| root = std::move(newNode); |
| } else { |
| assert(!nodeStack.empty()); |
| nodeStack.top()->addChild(std::move(newNode)); |
| } |
| |
| if (thisNode->type != NodeType::kText) { |
| nodeStack.push(thisNode); |
| } |
| } |
| } |
| return root; |
| } |
| |
| Node::Node(NodeType type) : type(type), parent(nullptr), lineNumber(0), columnNumber(0) { |
| } |
| |
| void Node::addChild(std::unique_ptr<Node> child) { |
| child->parent = this; |
| children.push_back(std::move(child)); |
| } |
| |
| Namespace::Namespace() : BaseNode(NodeType::kNamespace) { |
| } |
| |
| std::unique_ptr<Node> Namespace::clone() const { |
| Namespace* ns = new Namespace(); |
| ns->lineNumber = lineNumber; |
| ns->columnNumber = columnNumber; |
| ns->comment = comment; |
| ns->namespacePrefix = namespacePrefix; |
| ns->namespaceUri = namespaceUri; |
| for (auto& child : children) { |
| ns->addChild(child->clone()); |
| } |
| return std::unique_ptr<Node>(ns); |
| } |
| |
| Element::Element() : BaseNode(NodeType::kElement) { |
| } |
| |
| std::unique_ptr<Node> Element::clone() const { |
| Element* el = new Element(); |
| el->lineNumber = lineNumber; |
| el->columnNumber = columnNumber; |
| el->comment = comment; |
| el->namespaceUri = namespaceUri; |
| el->name = name; |
| el->attributes = attributes; |
| for (auto& child : children) { |
| el->addChild(child->clone()); |
| } |
| return std::unique_ptr<Node>(el); |
| } |
| |
| Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) { |
| for (auto& attr : attributes) { |
| if (ns == attr.namespaceUri && name == attr.name) { |
| return &attr; |
| } |
| } |
| return nullptr; |
| } |
| |
| Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) { |
| return findChildWithAttribute(ns, name, nullptr); |
| } |
| |
| Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name, |
| const Attribute* reqAttr) { |
| for (auto& childNode : children) { |
| Node* child = childNode.get(); |
| while (child->type == NodeType::kNamespace) { |
| if (child->children.empty()) { |
| break; |
| } |
| child = child->children[0].get(); |
| } |
| |
| if (child->type == NodeType::kElement) { |
| Element* el = static_cast<Element*>(child); |
| if (ns == el->namespaceUri && name == el->name) { |
| if (!reqAttr) { |
| return el; |
| } |
| |
| Attribute* attrName = el->findAttribute(reqAttr->namespaceUri, reqAttr->name); |
| if (attrName && attrName->value == reqAttr->value) { |
| return el; |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| std::vector<Element*> Element::getChildElements() { |
| std::vector<Element*> elements; |
| for (auto& childNode : children) { |
| Node* child = childNode.get(); |
| while (child->type == NodeType::kNamespace) { |
| if (child->children.empty()) { |
| break; |
| } |
| child = child->children[0].get(); |
| } |
| |
| if (child->type == NodeType::kElement) { |
| elements.push_back(static_cast<Element*>(child)); |
| } |
| } |
| return elements; |
| } |
| |
| Text::Text() : BaseNode(NodeType::kText) { |
| } |
| |
| std::unique_ptr<Node> Text::clone() const { |
| Text* el = new Text(); |
| el->lineNumber = lineNumber; |
| el->columnNumber = columnNumber; |
| el->comment = comment; |
| el->text = text; |
| return std::unique_ptr<Node>(el); |
| } |
| |
| } // namespace xml |
| } // namespace aapt |