diff options
Diffstat (limited to 'tools/aapt2/XmlDom.cpp')
-rw-r--r-- | tools/aapt2/XmlDom.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp new file mode 100644 index 0000000..763029f --- /dev/null +++ b/tools/aapt2/XmlDom.cpp @@ -0,0 +1,431 @@ +/* + * 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 std::move(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 |