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/Maybe.h"
18#include "util/Util.h"
19#include "xml/XmlPullParser.h"
20#include "xml/XmlUtil.h"
21
22#include <iostream>
23#include <string>
24
25namespace aapt {
26namespace xml {
27
28constexpr char kXmlNamespaceSep = 1;
29
30XmlPullParser::XmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
31    mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
32    XML_SetUserData(mParser, this);
33    XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
34    XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
35    XML_SetCharacterDataHandler(mParser, characterDataHandler);
36    XML_SetCommentHandler(mParser, commentDataHandler);
37    mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
38}
39
40XmlPullParser::~XmlPullParser() {
41    XML_ParserFree(mParser);
42}
43
44XmlPullParser::Event XmlPullParser::next() {
45    const Event currentEvent = getEvent();
46    if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
47        return currentEvent;
48    }
49
50    mEventQueue.pop();
51    while (mEventQueue.empty()) {
52        mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
53
54        const bool done = mIn.eof();
55        if (mIn.bad() && !done) {
56            mLastError = strerror(errno);
57            mEventQueue.push(EventData{ Event::kBadDocument });
58            continue;
59        }
60
61        if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
62            mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
63            mEventQueue.push(EventData{ Event::kBadDocument });
64            continue;
65        }
66
67        if (done) {
68            mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
69        }
70    }
71
72    Event event = getEvent();
73
74    // Record namespace prefixes and package names so that we can do our own
75    // handling of references that use namespace aliases.
76    if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
77        Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri());
78        if (event == Event::kStartNamespace) {
79            if (result) {
80                mPackageAliases.emplace_back(
81                        PackageDecl{ getNamespacePrefix(), std::move(result.value()) });
82            }
83        } else {
84            if (result) {
85                mPackageAliases.pop_back();
86            }
87        }
88    }
89
90    return event;
91}
92
93XmlPullParser::Event XmlPullParser::getEvent() const {
94    return mEventQueue.front().event;
95}
96
97const std::string& XmlPullParser::getLastError() const {
98    return mLastError;
99}
100
101const std::u16string& XmlPullParser::getComment() const {
102    return mEventQueue.front().data1;
103}
104
105size_t XmlPullParser::getLineNumber() const {
106    return mEventQueue.front().lineNumber;
107}
108
109size_t XmlPullParser::getDepth() const {
110    return mEventQueue.front().depth;
111}
112
113const std::u16string& XmlPullParser::getText() const {
114    if (getEvent() != Event::kText) {
115        return mEmpty;
116    }
117    return mEventQueue.front().data1;
118}
119
120const std::u16string& XmlPullParser::getNamespacePrefix() const {
121    const Event currentEvent = getEvent();
122    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
123        return mEmpty;
124    }
125    return mEventQueue.front().data1;
126}
127
128const std::u16string& XmlPullParser::getNamespaceUri() const {
129    const Event currentEvent = getEvent();
130    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
131        return mEmpty;
132    }
133    return mEventQueue.front().data2;
134}
135
136Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias(
137        const StringPiece16& alias, const StringPiece16& localPackage) const {
138    if (alias.empty()) {
139        return ExtractedPackage{ localPackage.toString(), false /* private */ };
140    }
141
142    const auto endIter = mPackageAliases.rend();
143    for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
144        if (alias == iter->prefix) {
145            if (iter->package.package.empty()) {
146                return ExtractedPackage{ localPackage.toString(),
147                                         iter->package.privateNamespace };
148            }
149            return iter->package;
150        }
151    }
152    return {};
153}
154
155const std::u16string& XmlPullParser::getElementNamespace() const {
156    const Event currentEvent = getEvent();
157    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
158        return mEmpty;
159    }
160    return mEventQueue.front().data1;
161}
162
163const std::u16string& XmlPullParser::getElementName() const {
164    const Event currentEvent = getEvent();
165    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
166        return mEmpty;
167    }
168    return mEventQueue.front().data2;
169}
170
171XmlPullParser::const_iterator XmlPullParser::beginAttributes() const {
172    return mEventQueue.front().attributes.begin();
173}
174
175XmlPullParser::const_iterator XmlPullParser::endAttributes() const {
176    return mEventQueue.front().attributes.end();
177}
178
179size_t XmlPullParser::getAttributeCount() const {
180    if (getEvent() != Event::kStartElement) {
181        return 0;
182    }
183    return mEventQueue.front().attributes.size();
184}
185
186/**
187 * Extracts the namespace and name of an expanded element or attribute name.
188 */
189static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) {
190    const char* p = name;
191    while (*p != 0 && *p != kXmlNamespaceSep) {
192        p++;
193    }
194
195    if (*p == 0) {
196        outNs = std::u16string();
197        outName = util::utf8ToUtf16(name);
198    } else {
199        outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
200        outName = util::utf8ToUtf16(p + 1);
201    }
202}
203
204void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
205        const char* uri) {
206    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
207    std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string();
208    parser->mNamespaceUris.push(namespaceUri);
209    parser->mEventQueue.push(EventData{
210            Event::kStartNamespace,
211            XML_GetCurrentLineNumber(parser->mParser),
212            parser->mDepth++,
213            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
214            namespaceUri
215    });
216}
217
218void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name,
219        const char** attrs) {
220    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
221
222    EventData data = {
223            Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
224    };
225    splitName(name, data.data1, data.data2);
226
227    while (*attrs) {
228        Attribute attribute;
229        splitName(*attrs++, attribute.namespaceUri, attribute.name);
230        attribute.value = util::utf8ToUtf16(*attrs++);
231
232        // Insert in sorted order.
233        auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
234        data.attributes.insert(iter, std::move(attribute));
235    }
236
237    // Move the structure into the queue (no copy).
238    parser->mEventQueue.push(std::move(data));
239}
240
241void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
242    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
243
244    parser->mEventQueue.push(EventData{
245            Event::kText,
246            XML_GetCurrentLineNumber(parser->mParser),
247            parser->mDepth,
248            util::utf8ToUtf16(StringPiece(s, len))
249    });
250}
251
252void XMLCALL XmlPullParser::endElementHandler(void* userData, const char* name) {
253    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
254
255    EventData data = {
256            Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
257    };
258    splitName(name, data.data1, data.data2);
259
260    // Move the data into the queue (no copy).
261    parser->mEventQueue.push(std::move(data));
262}
263
264void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
265    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
266
267    parser->mEventQueue.push(EventData{
268            Event::kEndNamespace,
269            XML_GetCurrentLineNumber(parser->mParser),
270            --(parser->mDepth),
271            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
272            parser->mNamespaceUris.top()
273    });
274    parser->mNamespaceUris.pop();
275}
276
277void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comment) {
278    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
279
280    parser->mEventQueue.push(EventData{
281            Event::kComment,
282            XML_GetCurrentLineNumber(parser->mParser),
283            parser->mDepth,
284            util::utf8ToUtf16(comment)
285    });
286}
287
288Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) {
289    auto iter = parser->findAttribute(u"", name);
290    if (iter != parser->endAttributes()) {
291        return StringPiece16(util::trimWhitespace(iter->value));
292    }
293    return {};
294}
295
296Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) {
297    auto iter = parser->findAttribute(u"", name);
298    if (iter != parser->endAttributes()) {
299        StringPiece16 trimmed = util::trimWhitespace(iter->value);
300        if (!trimmed.empty()) {
301            return trimmed;
302        }
303    }
304    return {};
305}
306
307} // namespace xml
308} // namespace aapt
309