XmlDom.cpp revision ca5638fd85098c3d0a699492751043545f75553a
1/*
2 * Copyright (C) 2015 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#include "util/Util.h"
18#include "XmlDom.h"
19#include "XmlPullParser.h"
20
21#include <cassert>
22#include <memory>
23#include <stack>
24#include <string>
25#include <tuple>
26
27namespace aapt {
28namespace xml {
29
30constexpr char kXmlNamespaceSep = 1;
31
32struct Stack {
33    std::unique_ptr<xml::Node> root;
34    std::stack<xml::Node*> nodeStack;
35    std::u16string pendingComment;
36};
37
38/**
39 * Extracts the namespace and name of an expanded element or attribute name.
40 */
41static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) {
42    const char* p = name;
43    while (*p != 0 && *p != kXmlNamespaceSep) {
44        p++;
45    }
46
47    if (*p == 0) {
48        outNs->clear();
49        *outName = util::utf8ToUtf16(name);
50    } else {
51        *outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
52        *outName = util::utf8ToUtf16(p + 1);
53    }
54}
55
56static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) {
57    node->lineNumber = XML_GetCurrentLineNumber(parser);
58    node->columnNumber = XML_GetCurrentColumnNumber(parser);
59
60    Node* thisNode = node.get();
61    if (!stack->nodeStack.empty()) {
62        stack->nodeStack.top()->addChild(std::move(node));
63    } else {
64        stack->root = std::move(node);
65    }
66
67    if (!nodeCast<Text>(thisNode)) {
68        stack->nodeStack.push(thisNode);
69    }
70}
71
72static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) {
73    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
74    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
75
76    std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
77    if (prefix) {
78        ns->namespacePrefix = util::utf8ToUtf16(prefix);
79    }
80
81    if (uri) {
82        ns->namespaceUri = util::utf8ToUtf16(uri);
83    }
84
85    addToStack(stack, parser, std::move(ns));
86}
87
88static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) {
89    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
90    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
91
92    assert(!stack->nodeStack.empty());
93    stack->nodeStack.pop();
94}
95
96static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) {
97    return std::tie(lhs.namespaceUri, lhs.name, lhs.value) <
98            std::tie(rhs.namespaceUri, rhs.name, rhs.value);
99}
100
101static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) {
102    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
103    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
104
105    std::unique_ptr<Element> el = util::make_unique<Element>();
106    splitName(name, &el->namespaceUri, &el->name);
107
108    while (*attrs) {
109        Attribute attribute;
110        splitName(*attrs++, &attribute.namespaceUri, &attribute.name);
111        attribute.value = util::utf8ToUtf16(*attrs++);
112
113        // Insert in sorted order.
114        auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
115                                     lessAttribute);
116        el->attributes.insert(iter, std::move(attribute));
117    }
118
119    el->comment = std::move(stack->pendingComment);
120    addToStack(stack, parser, std::move(el));
121}
122
123static void XMLCALL endElementHandler(void* userData, const char* name) {
124    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
125    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
126
127    assert(!stack->nodeStack.empty());
128    //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
129    stack->nodeStack.pop();
130}
131
132static void XMLCALL characterDataHandler(void* userData, const char* s, int len) {
133    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
134    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
135
136    if (!s || len <= 0) {
137        return;
138    }
139
140    // See if we can just append the text to a previous text node.
141    if (!stack->nodeStack.empty()) {
142        Node* currentParent = stack->nodeStack.top();
143        if (!currentParent->children.empty()) {
144            Node* lastChild = currentParent->children.back().get();
145            if (Text* text = nodeCast<Text>(lastChild)) {
146                text->text += util::utf8ToUtf16(StringPiece(s, len));
147                return;
148            }
149        }
150    }
151
152    std::unique_ptr<Text> text = util::make_unique<Text>();
153    text->text = util::utf8ToUtf16(StringPiece(s, len));
154    addToStack(stack, parser, std::move(text));
155}
156
157static void XMLCALL commentDataHandler(void* userData, const char* comment) {
158    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
159    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
160
161    if (!stack->pendingComment.empty()) {
162        stack->pendingComment += '\n';
163    }
164    stack->pendingComment += util::utf8ToUtf16(comment);
165}
166
167std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
168    Stack stack;
169
170    XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
171    XML_SetUserData(parser, &stack);
172    XML_UseParserAsHandlerArg(parser);
173    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
174    XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler);
175    XML_SetCharacterDataHandler(parser, characterDataHandler);
176    XML_SetCommentHandler(parser, commentDataHandler);
177
178    char buffer[1024];
179    while (!in->eof()) {
180        in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
181        if (in->bad() && !in->eof()) {
182            stack.root = {};
183            diag->error(DiagMessage(source) << strerror(errno));
184            break;
185        }
186
187        if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
188            stack.root = {};
189            diag->error(DiagMessage(source.withLine(XML_GetCurrentLineNumber(parser)))
190                        << XML_ErrorString(XML_GetErrorCode(parser)));
191            break;
192        }
193    }
194
195    XML_ParserFree(parser);
196    if (stack.root) {
197        return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
198    }
199    return {};
200}
201
202static void copyAttributes(Element* el, android::ResXMLParser* parser) {
203    const size_t attrCount = parser->getAttributeCount();
204    if (attrCount > 0) {
205        el->attributes.reserve(attrCount);
206        for (size_t i = 0; i < attrCount; i++) {
207            Attribute attr;
208            size_t len;
209            const char16_t* str16 = parser->getAttributeNamespace(i, &len);
210            if (str16) {
211                attr.namespaceUri.assign(str16, len);
212            }
213
214            str16 = parser->getAttributeName(i, &len);
215            if (str16) {
216                attr.name.assign(str16, len);
217            }
218
219            str16 = parser->getAttributeStringValue(i, &len);
220            if (str16) {
221                attr.value.assign(str16, len);
222            }
223            el->attributes.push_back(std::move(attr));
224        }
225    }
226}
227
228std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
229                                     const Source& source) {
230    std::unique_ptr<Node> root;
231    std::stack<Node*> nodeStack;
232
233    android::ResXMLTree tree;
234    if (tree.setTo(data, dataLen) != android::NO_ERROR) {
235        return {};
236    }
237
238    android::ResXMLParser::event_code_t code;
239    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
240            code != android::ResXMLParser::END_DOCUMENT) {
241        std::unique_ptr<Node> newNode;
242        switch (code) {
243            case android::ResXMLParser::START_NAMESPACE: {
244                std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
245                size_t len;
246                const char16_t* str16 = tree.getNamespacePrefix(&len);
247                if (str16) {
248                    node->namespacePrefix.assign(str16, len);
249                }
250
251                str16 = tree.getNamespaceUri(&len);
252                if (str16) {
253                    node->namespaceUri.assign(str16, len);
254                }
255                newNode = std::move(node);
256                break;
257            }
258
259            case android::ResXMLParser::START_TAG: {
260                std::unique_ptr<Element> node = util::make_unique<Element>();
261                size_t len;
262                const char16_t* str16 = tree.getElementNamespace(&len);
263                if (str16) {
264                    node->namespaceUri.assign(str16, len);
265                }
266
267                str16 = tree.getElementName(&len);
268                if (str16) {
269                    node->name.assign(str16, len);
270                }
271
272                copyAttributes(node.get(), &tree);
273
274                newNode = std::move(node);
275                break;
276            }
277
278            case android::ResXMLParser::TEXT: {
279                std::unique_ptr<Text> node = util::make_unique<Text>();
280                size_t len;
281                const char16_t* str16 = tree.getText(&len);
282                if (str16) {
283                    node->text.assign(str16, len);
284                }
285                newNode = std::move(node);
286                break;
287            }
288
289            case android::ResXMLParser::END_NAMESPACE:
290            case android::ResXMLParser::END_TAG:
291                assert(!nodeStack.empty());
292                nodeStack.pop();
293                break;
294
295            default:
296                assert(false);
297                break;
298        }
299
300        if (newNode) {
301            newNode->lineNumber = tree.getLineNumber();
302
303            Node* thisNode = newNode.get();
304            if (!root) {
305                assert(nodeStack.empty());
306                root = std::move(newNode);
307            } else {
308                assert(!nodeStack.empty());
309                nodeStack.top()->addChild(std::move(newNode));
310            }
311
312            if (!nodeCast<Text>(thisNode)) {
313                nodeStack.push(thisNode);
314            }
315        }
316    }
317    return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
318}
319
320Element* findRootElement(Node* node) {
321    if (!node) {
322        return nullptr;
323    }
324
325    Element* el = nullptr;
326    while ((el = nodeCast<Element>(node)) == nullptr) {
327        if (node->children.empty()) {
328            return nullptr;
329        }
330        // We are looking for the first element, and namespaces can only have one child.
331        node = node->children.front().get();
332    }
333    return el;
334}
335
336void Node::addChild(std::unique_ptr<Node> child) {
337    child->parent = this;
338    children.push_back(std::move(child));
339}
340
341Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) {
342    for (auto& attr : attributes) {
343        if (ns == attr.namespaceUri && name == attr.name) {
344            return &attr;
345        }
346    }
347    return nullptr;
348}
349
350Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) {
351    return findChildWithAttribute(ns, name, {}, {}, {});
352}
353
354Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
355                                         const StringPiece16& attrNs, const StringPiece16& attrName,
356                                         const StringPiece16& attrValue) {
357    for (auto& childNode : children) {
358        Node* child = childNode.get();
359        while (nodeCast<Namespace>(child)) {
360            if (child->children.empty()) {
361                break;
362            }
363            child = child->children[0].get();
364        }
365
366        if (Element* el = nodeCast<Element>(child)) {
367            if (ns == el->namespaceUri && name == el->name) {
368                if (attrNs.empty() && attrName.empty()) {
369                    return el;
370                }
371
372                Attribute* attr = el->findAttribute(attrNs, attrName);
373                if (attr && attrValue == attr->value) {
374                    return el;
375                }
376            }
377        }
378    }
379    return nullptr;
380}
381
382std::vector<Element*> Element::getChildElements() {
383    std::vector<Element*> elements;
384    for (auto& childNode : children) {
385        Node* child = childNode.get();
386        while (nodeCast<Namespace>(child)) {
387            if (child->children.empty()) {
388                break;
389            }
390            child = child->children[0].get();
391        }
392
393        if (Element* el = nodeCast<Element>(child)) {
394            elements.push_back(el);
395        }
396    }
397    return elements;
398}
399
400} // namespace xml
401} // namespace aapt
402