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
25 namespace aapt {
26 namespace xml {
27
28 constexpr char kXmlNamespaceSep = 1;
29
XmlPullParser(std::istream & in)30 XmlPullParser::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
~XmlPullParser()40 XmlPullParser::~XmlPullParser() {
41 XML_ParserFree(mParser);
42 }
43
next()44 XmlPullParser::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
getEvent() const93 XmlPullParser::Event XmlPullParser::getEvent() const {
94 return mEventQueue.front().event;
95 }
96
getLastError() const97 const std::string& XmlPullParser::getLastError() const {
98 return mLastError;
99 }
100
getComment() const101 const std::u16string& XmlPullParser::getComment() const {
102 return mEventQueue.front().data1;
103 }
104
getLineNumber() const105 size_t XmlPullParser::getLineNumber() const {
106 return mEventQueue.front().lineNumber;
107 }
108
getDepth() const109 size_t XmlPullParser::getDepth() const {
110 return mEventQueue.front().depth;
111 }
112
getText() const113 const std::u16string& XmlPullParser::getText() const {
114 if (getEvent() != Event::kText) {
115 return mEmpty;
116 }
117 return mEventQueue.front().data1;
118 }
119
getNamespacePrefix() const120 const 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
getNamespaceUri() const128 const 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
transformPackageAlias(const StringPiece16 & alias,const StringPiece16 & localPackage) const136 Maybe<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
getElementNamespace() const155 const 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
getElementName() const163 const 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
beginAttributes() const171 XmlPullParser::const_iterator XmlPullParser::beginAttributes() const {
172 return mEventQueue.front().attributes.begin();
173 }
174
endAttributes() const175 XmlPullParser::const_iterator XmlPullParser::endAttributes() const {
176 return mEventQueue.front().attributes.end();
177 }
178
getAttributeCount() const179 size_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 */
splitName(const char * name,std::u16string & outNs,std::u16string & outName)189 static 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
startNamespaceHandler(void * userData,const char * prefix,const char * uri)204 void 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
startElementHandler(void * userData,const char * name,const char ** attrs)218 void 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
characterDataHandler(void * userData,const char * s,int len)241 void 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
endElementHandler(void * userData,const char * name)252 void 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
endNamespaceHandler(void * userData,const char * prefix)264 void 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
commentDataHandler(void * userData,const char * comment)277 void 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
findAttribute(const XmlPullParser * parser,const StringPiece16 & name)288 Maybe<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
findNonEmptyAttribute(const XmlPullParser * parser,const StringPiece16 & name)296 Maybe<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