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 <iostream>
18 #include <string>
19
20 #include "util/Maybe.h"
21 #include "util/Util.h"
22 #include "xml/XmlPullParser.h"
23 #include "xml/XmlUtil.h"
24
25 using android::StringPiece;
26
27 namespace aapt {
28 namespace xml {
29
30 constexpr char kXmlNamespaceSep = 1;
31
XmlPullParser(std::istream & in)32 XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) {
33 parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
34 XML_SetUserData(parser_, this);
35 XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler);
36 XML_SetNamespaceDeclHandler(parser_, StartNamespaceHandler,
37 EndNamespaceHandler);
38 XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
39 XML_SetCommentHandler(parser_, CommentDataHandler);
40 event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
41 }
42
~XmlPullParser()43 XmlPullParser::~XmlPullParser() { XML_ParserFree(parser_); }
44
Next()45 XmlPullParser::Event XmlPullParser::Next() {
46 const Event currentEvent = event();
47 if (currentEvent == Event::kBadDocument ||
48 currentEvent == Event::kEndDocument) {
49 return currentEvent;
50 }
51
52 event_queue_.pop();
53 while (event_queue_.empty()) {
54 in_.read(buffer_, sizeof(buffer_) / sizeof(*buffer_));
55
56 const bool done = in_.eof();
57 if (in_.bad() && !done) {
58 error_ = strerror(errno);
59 event_queue_.push(EventData{Event::kBadDocument});
60 continue;
61 }
62
63 if (XML_Parse(parser_, buffer_, in_.gcount(), done) == XML_STATUS_ERROR) {
64 error_ = XML_ErrorString(XML_GetErrorCode(parser_));
65 event_queue_.push(EventData{Event::kBadDocument});
66 continue;
67 }
68
69 if (done) {
70 event_queue_.push(EventData{Event::kEndDocument, 0, 0});
71 }
72 }
73
74 Event next_event = event();
75
76 // Record namespace prefixes and package names so that we can do our own
77 // handling of references that use namespace aliases.
78 if (next_event == Event::kStartNamespace ||
79 next_event == Event::kEndNamespace) {
80 Maybe<ExtractedPackage> result =
81 ExtractPackageFromNamespace(namespace_uri());
82 if (next_event == Event::kStartNamespace) {
83 if (result) {
84 package_aliases_.emplace_back(
85 PackageDecl{namespace_prefix(), std::move(result.value())});
86 }
87 } else {
88 if (result) {
89 package_aliases_.pop_back();
90 }
91 }
92 }
93
94 return next_event;
95 }
96
event() const97 XmlPullParser::Event XmlPullParser::event() const {
98 return event_queue_.front().event;
99 }
100
error() const101 const std::string& XmlPullParser::error() const { return error_; }
102
comment() const103 const std::string& XmlPullParser::comment() const {
104 return event_queue_.front().data1;
105 }
106
line_number() const107 size_t XmlPullParser::line_number() const {
108 return event_queue_.front().line_number;
109 }
110
depth() const111 size_t XmlPullParser::depth() const { return event_queue_.front().depth; }
112
text() const113 const std::string& XmlPullParser::text() const {
114 if (event() != Event::kText) {
115 return empty_;
116 }
117 return event_queue_.front().data1;
118 }
119
namespace_prefix() const120 const std::string& XmlPullParser::namespace_prefix() const {
121 const Event current_event = event();
122 if (current_event != Event::kStartNamespace &&
123 current_event != Event::kEndNamespace) {
124 return empty_;
125 }
126 return event_queue_.front().data1;
127 }
128
namespace_uri() const129 const std::string& XmlPullParser::namespace_uri() const {
130 const Event current_event = event();
131 if (current_event != Event::kStartNamespace &&
132 current_event != Event::kEndNamespace) {
133 return empty_;
134 }
135 return event_queue_.front().data2;
136 }
137
TransformPackageAlias(const StringPiece & alias,const StringPiece & local_package) const138 Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
139 const StringPiece& alias, const StringPiece& local_package) const {
140 if (alias.empty()) {
141 return ExtractedPackage{local_package.to_string(), false /* private */};
142 }
143
144 const auto end_iter = package_aliases_.rend();
145 for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
146 if (alias == iter->prefix) {
147 if (iter->package.package.empty()) {
148 return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
149 }
150 return iter->package;
151 }
152 }
153 return {};
154 }
155
element_namespace() const156 const std::string& XmlPullParser::element_namespace() const {
157 const Event current_event = event();
158 if (current_event != Event::kStartElement &&
159 current_event != Event::kEndElement) {
160 return empty_;
161 }
162 return event_queue_.front().data1;
163 }
164
element_name() const165 const std::string& XmlPullParser::element_name() const {
166 const Event current_event = event();
167 if (current_event != Event::kStartElement &&
168 current_event != Event::kEndElement) {
169 return empty_;
170 }
171 return event_queue_.front().data2;
172 }
173
begin_attributes() const174 XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
175 return event_queue_.front().attributes.begin();
176 }
177
end_attributes() const178 XmlPullParser::const_iterator XmlPullParser::end_attributes() const {
179 return event_queue_.front().attributes.end();
180 }
181
attribute_count() const182 size_t XmlPullParser::attribute_count() const {
183 if (event() != Event::kStartElement) {
184 return 0;
185 }
186 return event_queue_.front().attributes.size();
187 }
188
189 /**
190 * Extracts the namespace and name of an expanded element or attribute name.
191 */
SplitName(const char * name,std::string * out_ns,std::string * out_name)192 static void SplitName(const char* name, std::string* out_ns, std::string* out_name) {
193 const char* p = name;
194 while (*p != 0 && *p != kXmlNamespaceSep) {
195 p++;
196 }
197
198 if (*p == 0) {
199 out_ns->clear();
200 out_name->assign(name);
201 } else {
202 out_ns->assign(name, (p - name));
203 out_name->assign(p + 1);
204 }
205 }
206
StartNamespaceHandler(void * user_data,const char * prefix,const char * uri)207 void XMLCALL XmlPullParser::StartNamespaceHandler(void* user_data,
208 const char* prefix,
209 const char* uri) {
210 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
211 std::string namespace_uri = uri != nullptr ? uri : std::string();
212 parser->namespace_uris_.push(namespace_uri);
213 parser->event_queue_.push(
214 EventData{Event::kStartNamespace,
215 XML_GetCurrentLineNumber(parser->parser_), parser->depth_++,
216 prefix != nullptr ? prefix : std::string(), namespace_uri});
217 }
218
StartElementHandler(void * user_data,const char * name,const char ** attrs)219 void XMLCALL XmlPullParser::StartElementHandler(void* user_data,
220 const char* name,
221 const char** attrs) {
222 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
223
224 EventData data = {Event::kStartElement,
225 XML_GetCurrentLineNumber(parser->parser_),
226 parser->depth_++};
227 SplitName(name, &data.data1, &data.data2);
228
229 while (*attrs) {
230 Attribute attribute;
231 SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
232 attribute.value = *attrs++;
233
234 // Insert in sorted order.
235 auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(),
236 attribute);
237 data.attributes.insert(iter, std::move(attribute));
238 }
239
240 // Move the structure into the queue (no copy).
241 parser->event_queue_.push(std::move(data));
242 }
243
CharacterDataHandler(void * user_data,const char * s,int len)244 void XMLCALL XmlPullParser::CharacterDataHandler(void* user_data, const char* s,
245 int len) {
246 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
247
248 parser->event_queue_.push(EventData{Event::kText, XML_GetCurrentLineNumber(parser->parser_),
249 parser->depth_, std::string(s, len)});
250 }
251
EndElementHandler(void * user_data,const char * name)252 void XMLCALL XmlPullParser::EndElementHandler(void* user_data,
253 const char* name) {
254 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
255
256 EventData data = {Event::kEndElement,
257 XML_GetCurrentLineNumber(parser->parser_),
258 --(parser->depth_)};
259 SplitName(name, &data.data1, &data.data2);
260
261 // Move the data into the queue (no copy).
262 parser->event_queue_.push(std::move(data));
263 }
264
EndNamespaceHandler(void * user_data,const char * prefix)265 void XMLCALL XmlPullParser::EndNamespaceHandler(void* user_data,
266 const char* prefix) {
267 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
268
269 parser->event_queue_.push(
270 EventData{Event::kEndNamespace, XML_GetCurrentLineNumber(parser->parser_),
271 --(parser->depth_), prefix != nullptr ? prefix : std::string(),
272 parser->namespace_uris_.top()});
273 parser->namespace_uris_.pop();
274 }
275
CommentDataHandler(void * user_data,const char * comment)276 void XMLCALL XmlPullParser::CommentDataHandler(void* user_data,
277 const char* comment) {
278 XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
279
280 parser->event_queue_.push(EventData{Event::kComment,
281 XML_GetCurrentLineNumber(parser->parser_),
282 parser->depth_, comment});
283 }
284
FindAttribute(const XmlPullParser * parser,const StringPiece & name)285 Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
286 const StringPiece& name) {
287 auto iter = parser->FindAttribute("", name);
288 if (iter != parser->end_attributes()) {
289 return StringPiece(util::TrimWhitespace(iter->value));
290 }
291 return {};
292 }
293
FindNonEmptyAttribute(const XmlPullParser * parser,const StringPiece & name)294 Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
295 const StringPiece& name) {
296 auto iter = parser->FindAttribute("", name);
297 if (iter != parser->end_attributes()) {
298 StringPiece trimmed = util::TrimWhitespace(iter->value);
299 if (!trimmed.empty()) {
300 return trimmed;
301 }
302 }
303 return {};
304 }
305
306 } // namespace xml
307 } // namespace aapt
308