/* * 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 "XmlDom.h" #include <expat.h> #include <memory> #include <stack> #include <string> #include <tuple> #include "android-base/logging.h" #include "ResourceUtils.h" #include "XmlPullParser.h" #include "util/Util.h" using android::StringPiece; using android::StringPiece16; namespace aapt { namespace xml { constexpr char kXmlNamespaceSep = 1; struct Stack { std::unique_ptr<xml::Node> root; std::stack<xml::Node*> node_stack; std::string pending_comment; std::unique_ptr<xml::Text> last_text_node; }; /** * Extracts the namespace and name of an expanded element or attribute name. */ static void SplitName(const char* name, std::string* out_ns, std::string* out_name) { const char* p = name; while (*p != 0 && *p != kXmlNamespaceSep) { p++; } if (*p == 0) { out_ns->clear(); out_name->assign(name); } else { out_ns->assign(name, (p - name)); out_name->assign(p + 1); } } static void FinishPendingText(Stack* stack) { if (stack->last_text_node != nullptr) { if (!stack->last_text_node->text.empty()) { stack->node_stack.top()->AppendChild(std::move(stack->last_text_node)); } else { // Drop an empty text node. } stack->last_text_node = nullptr; } } static void AddToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) { node->line_number = XML_GetCurrentLineNumber(parser); node->column_number = XML_GetCurrentColumnNumber(parser); Node* this_node = node.get(); if (!stack->node_stack.empty()) { stack->node_stack.top()->AppendChild(std::move(node)); } else { stack->root = std::move(node); } if (!NodeCast<Text>(this_node)) { stack->node_stack.push(this_node); } } static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix, const char* uri) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); FinishPendingText(stack); std::unique_ptr<Namespace> ns = util::make_unique<Namespace>(); if (prefix) { ns->namespace_prefix = prefix; } if (uri) { ns->namespace_uri = uri; } AddToStack(stack, parser, std::move(ns)); } static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); FinishPendingText(stack); CHECK(!stack->node_stack.empty()); stack->node_stack.pop(); } static bool less_attribute(const Attribute& lhs, const Attribute& rhs) { return std::tie(lhs.namespace_uri, lhs.name, lhs.value) < std::tie(rhs.namespace_uri, rhs.name, rhs.value); } static void XMLCALL StartElementHandler(void* user_data, const char* name, const char** attrs) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); FinishPendingText(stack); std::unique_ptr<Element> el = util::make_unique<Element>(); SplitName(name, &el->namespace_uri, &el->name); while (*attrs) { Attribute attribute; SplitName(*attrs++, &attribute.namespace_uri, &attribute.name); attribute.value = *attrs++; // Insert in sorted order. auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute, less_attribute); el->attributes.insert(iter, std::move(attribute)); } el->comment = std::move(stack->pending_comment); AddToStack(stack, parser, std::move(el)); } static void XMLCALL EndElementHandler(void* user_data, const char* name) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); FinishPendingText(stack); CHECK(!stack->node_stack.empty()); // stack->nodeStack.top()->comment = std::move(stack->pendingComment); stack->node_stack.pop(); } static void XMLCALL CharacterDataHandler(void* user_data, const char* s, int len) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); const StringPiece str(s, len); if (str.empty()) { return; } // See if we can just append the text to a previous text node. if (stack->last_text_node != nullptr) { stack->last_text_node->text.append(str.data(), str.size()); return; } stack->last_text_node = util::make_unique<Text>(); stack->last_text_node->line_number = XML_GetCurrentLineNumber(parser); stack->last_text_node->column_number = XML_GetCurrentColumnNumber(parser); stack->last_text_node->text = str.to_string(); } static void XMLCALL CommentDataHandler(void* user_data, const char* comment) { XML_Parser parser = reinterpret_cast<XML_Parser>(user_data); Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser)); FinishPendingText(stack); if (!stack->pending_comment.empty()) { stack->pending_comment += '\n'; } stack->pending_comment += comment; } std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source) { 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 = {}; diag->Error(DiagMessage(source) << strerror(errno)); break; } if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) { stack.root = {}; diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser))) << XML_ErrorString(XML_GetErrorCode(parser))); break; } } XML_ParserFree(parser); if (stack.root) { return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{}, std::move(stack.root)); } return {}; } static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) { const size_t attr_count = parser->getAttributeCount(); if (attr_count > 0) { el->attributes.reserve(attr_count); for (size_t i = 0; i < attr_count; i++) { Attribute attr; size_t len; const char16_t* str16 = parser->getAttributeNamespace(i, &len); if (str16) { attr.namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len)); } str16 = parser->getAttributeName(i, &len); if (str16) { attr.name = util::Utf16ToUtf8(StringPiece16(str16, len)); } str16 = parser->getAttributeStringValue(i, &len); if (str16) { attr.value = util::Utf16ToUtf8(StringPiece16(str16, len)); } android::Res_value res_value; if (parser->getAttributeValue(i, &res_value) > 0) { attr.compiled_value = ResourceUtils::ParseBinaryResValue( ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool); } el->attributes.push_back(std::move(attr)); } } } std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, const Source& source) { // We import the android namespace because on Windows NO_ERROR is a macro, not // an enum, which // causes errors when qualifying it with android:: using namespace android; StringPool string_pool; std::unique_ptr<Node> root; std::stack<Node*> node_stack; ResXMLTree tree; if (tree.setTo(data, data_len) != NO_ERROR) { return {}; } ResXMLParser::event_code_t code; while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT && code != ResXMLParser::END_DOCUMENT) { std::unique_ptr<Node> new_node; switch (code) { case 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->namespace_prefix = util::Utf16ToUtf8(StringPiece16(str16, len)); } str16 = tree.getNamespaceUri(&len); if (str16) { node->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len)); } new_node = std::move(node); break; } case 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->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len)); } str16 = tree.getElementName(&len); if (str16) { node->name = util::Utf16ToUtf8(StringPiece16(str16, len)); } CopyAttributes(node.get(), &tree, &string_pool); new_node = std::move(node); break; } case 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 = util::Utf16ToUtf8(StringPiece16(str16, len)); } new_node = std::move(node); break; } case ResXMLParser::END_NAMESPACE: case ResXMLParser::END_TAG: CHECK(!node_stack.empty()); node_stack.pop(); break; default: LOG(FATAL) << "unhandled XML chunk type"; break; } if (new_node) { new_node->line_number = tree.getLineNumber(); Node* this_node = new_node.get(); if (!root) { CHECK(node_stack.empty()) << "node stack should be empty"; root = std::move(new_node); } else { CHECK(!node_stack.empty()) << "node stack should not be empty"; node_stack.top()->AppendChild(std::move(new_node)); } if (!NodeCast<Text>(this_node)) { node_stack.push(this_node); } } } return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root)); } std::unique_ptr<Node> Namespace::Clone(const ElementCloneFunc& el_cloner) { auto ns = util::make_unique<Namespace>(); ns->comment = comment; ns->line_number = line_number; ns->column_number = column_number; ns->namespace_prefix = namespace_prefix; ns->namespace_uri = namespace_uri; ns->children.reserve(children.size()); for (const std::unique_ptr<xml::Node>& child : children) { ns->AppendChild(child->Clone(el_cloner)); } return std::move(ns); } Element* FindRootElement(XmlResource* doc) { return FindRootElement(doc->root.get()); } Element* FindRootElement(Node* node) { if (!node) { return nullptr; } Element* el = nullptr; while ((el = NodeCast<Element>(node)) == nullptr) { if (node->children.empty()) { return nullptr; } // We are looking for the first element, and namespaces can only have one // child. node = node->children.front().get(); } return el; } void Node::AppendChild(std::unique_ptr<Node> child) { child->parent = this; children.push_back(std::move(child)); } void Node::InsertChild(size_t index, std::unique_ptr<Node> child) { child->parent = this; children.insert(children.begin() + index, std::move(child)); } Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) { for (auto& attr : attributes) { if (ns == attr.namespace_uri && name == attr.name) { return &attr; } } return nullptr; } const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) const { for (const auto& attr : attributes) { if (ns == attr.namespace_uri && name == attr.name) { return &attr; } } return nullptr; } Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) { return FindChildWithAttribute(ns, name, {}, {}, {}); } Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name, const StringPiece& attr_ns, const StringPiece& attr_name, const StringPiece& attr_value) { for (auto& child_node : children) { Node* child = child_node.get(); while (NodeCast<Namespace>(child)) { if (child->children.empty()) { break; } child = child->children[0].get(); } if (Element* el = NodeCast<Element>(child)) { if (ns == el->namespace_uri && name == el->name) { if (attr_ns.empty() && attr_name.empty()) { return el; } Attribute* attr = el->FindAttribute(attr_ns, attr_name); if (attr && attr_value == attr->value) { return el; } } } } return nullptr; } std::vector<Element*> Element::GetChildElements() { std::vector<Element*> elements; for (auto& child_node : children) { Node* child = child_node.get(); while (NodeCast<Namespace>(child)) { if (child->children.empty()) { break; } child = child->children[0].get(); } if (Element* el = NodeCast<Element>(child)) { elements.push_back(el); } } return elements; } std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) { auto el = util::make_unique<Element>(); el->comment = comment; el->line_number = line_number; el->column_number = column_number; el->name = name; el->namespace_uri = namespace_uri; el->attributes.reserve(attributes.size()); el_cloner(*this, el.get()); el->children.reserve(children.size()); for (const std::unique_ptr<xml::Node>& child : children) { el->AppendChild(child->Clone(el_cloner)); } return std::move(el); } std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) { auto t = util::make_unique<Text>(); t->comment = comment; t->line_number = line_number; t->column_number = column_number; t->text = text; return std::move(t); } void PackageAwareVisitor::Visit(Namespace* ns) { bool added = false; if (Maybe<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(ns->namespace_uri)) { ExtractedPackage& package = maybe_package.value(); package_decls_.push_back( PackageDecl{ns->namespace_prefix, std::move(package)}); added = true; } Visitor::Visit(ns); if (added) { package_decls_.pop_back(); } } Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias( const StringPiece& alias, const StringPiece& local_package) const { if (alias.empty()) { return ExtractedPackage{local_package.to_string(), false /* private */}; } const auto rend = package_decls_.rend(); for (auto iter = package_decls_.rbegin(); iter != rend; ++iter) { if (alias == iter->prefix) { if (iter->package.package.empty()) { return ExtractedPackage{local_package.to_string(), iter->package.private_namespace}; } return iter->package; } } return {}; } } // namespace xml } // namespace aapt