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