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#ifndef AAPT_XML_PULL_PARSER_H
18#define AAPT_XML_PULL_PARSER_H
19
20#include <expat.h>
21
22#include <algorithm>
23#include <istream>
24#include <ostream>
25#include <queue>
26#include <stack>
27#include <string>
28#include <vector>
29
30#include "android-base/macros.h"
31#include "androidfw/StringPiece.h"
32
33#include "Resource.h"
34#include "io/Io.h"
35#include "process/IResourceTableConsumer.h"
36#include "util/Maybe.h"
37#include "xml/XmlUtil.h"
38
39namespace aapt {
40namespace xml {
41
42class XmlPullParser : public IPackageDeclStack {
43 public:
44  enum class Event {
45    kBadDocument,
46    kStartDocument,
47    kEndDocument,
48
49    kStartNamespace,
50    kEndNamespace,
51    kStartElement,
52    kEndElement,
53    kText,
54    kComment,
55  };
56
57  /**
58   * Skips to the next direct descendant node of the given start_depth,
59   * skipping namespace nodes.
60   *
61   * When NextChildNode() returns true, you can expect Comments, Text, and
62   * StartElement events.
63   */
64  static bool NextChildNode(XmlPullParser* parser, size_t start_depth);
65  static bool SkipCurrentElement(XmlPullParser* parser);
66  static bool IsGoodEvent(Event event);
67
68  explicit XmlPullParser(io::InputStream* in);
69  ~XmlPullParser();
70
71  /**
72   * Returns the current event that is being processed.
73   */
74  Event event() const;
75
76  const std::string& error() const;
77
78  /**
79   * Note, unlike XmlPullParser, the first call to next() will return
80   * StartElement of the first element.
81   */
82  Event Next();
83
84  //
85  // These are available for all nodes.
86  //
87
88  const std::string& comment() const;
89  size_t line_number() const;
90  size_t depth() const;
91
92  /**
93   * Returns the character data for a Text event.
94   */
95  const std::string& text() const;
96
97  //
98  // Namespace prefix and URI are available for StartNamespace and EndNamespace.
99  //
100
101  const std::string& namespace_prefix() const;
102  const std::string& namespace_uri() const;
103
104  //
105  // These are available for StartElement and EndElement.
106  //
107
108  const std::string& element_namespace() const;
109  const std::string& element_name() const;
110
111  /*
112   * Uses the current stack of namespaces to resolve the package. Eg:
113   * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
114   * ...
115   * android:text="@app:string/message"
116   *
117   * In this case, 'app' will be converted to 'com.android.app'.
118   *
119   * If xmlns:app="http://schemas.android.com/apk/res-auto", then
120   * 'package' will be set to 'defaultPackage'.
121   */
122  Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
123
124  //
125  // Remaining methods are for retrieving information about attributes
126  // associated with a StartElement.
127  //
128  // Attributes must be in sorted order (according to the less than operator
129  // of struct Attribute).
130  //
131
132  struct Attribute {
133    std::string namespace_uri;
134    std::string name;
135    std::string value;
136
137    int compare(const Attribute& rhs) const;
138    bool operator<(const Attribute& rhs) const;
139    bool operator==(const Attribute& rhs) const;
140    bool operator!=(const Attribute& rhs) const;
141  };
142
143  using const_iterator = std::vector<Attribute>::const_iterator;
144
145  const_iterator begin_attributes() const;
146  const_iterator end_attributes() const;
147  size_t attribute_count() const;
148  const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const;
149
150 private:
151  DISALLOW_COPY_AND_ASSIGN(XmlPullParser);
152
153  static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
154                                            const char* uri);
155  static void XMLCALL StartElementHandler(void* user_data, const char* name,
156                                          const char** attrs);
157  static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
158                                           int len);
159  static void XMLCALL EndElementHandler(void* user_data, const char* name);
160  static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
161  static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
162
163  struct EventData {
164    Event event;
165    size_t line_number;
166    size_t depth;
167    std::string data1;
168    std::string data2;
169    std::vector<Attribute> attributes;
170  };
171
172  io::InputStream* in_;
173  XML_Parser parser_;
174  std::queue<EventData> event_queue_;
175  std::string error_;
176  const std::string empty_;
177  size_t depth_;
178  std::stack<std::string> namespace_uris_;
179
180  struct PackageDecl {
181    std::string prefix;
182    ExtractedPackage package;
183  };
184  std::vector<PackageDecl> package_aliases_;
185};
186
187/**
188 * Finds the attribute in the current element within the global namespace.
189 */
190Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
191                                          const android::StringPiece& name);
192
193/**
194 * Finds the attribute in the current element within the global namespace. The
195 * attribute's value
196 * must not be the empty string.
197 */
198Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
199                                                  const android::StringPiece& name);
200
201//
202// Implementation
203//
204
205inline ::std::ostream& operator<<(::std::ostream& out,
206                                  XmlPullParser::Event event) {
207  switch (event) {
208    case XmlPullParser::Event::kBadDocument:
209      return out << "BadDocument";
210    case XmlPullParser::Event::kStartDocument:
211      return out << "StartDocument";
212    case XmlPullParser::Event::kEndDocument:
213      return out << "EndDocument";
214    case XmlPullParser::Event::kStartNamespace:
215      return out << "StartNamespace";
216    case XmlPullParser::Event::kEndNamespace:
217      return out << "EndNamespace";
218    case XmlPullParser::Event::kStartElement:
219      return out << "StartElement";
220    case XmlPullParser::Event::kEndElement:
221      return out << "EndElement";
222    case XmlPullParser::Event::kText:
223      return out << "Text";
224    case XmlPullParser::Event::kComment:
225      return out << "Comment";
226  }
227  return out;
228}
229
230inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) {
231  Event event;
232
233  // First get back to the start depth.
234  while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) {
235  }
236
237  // Now look for the first good node.
238  while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) {
239    switch (event) {
240      case Event::kText:
241      case Event::kComment:
242      case Event::kStartElement:
243        return true;
244      default:
245        break;
246    }
247    event = parser->Next();
248  }
249  return false;
250}
251
252inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) {
253  int depth = 1;
254  while (depth > 0) {
255    switch (parser->Next()) {
256      case Event::kEndDocument:
257        return true;
258      case Event::kBadDocument:
259        return false;
260      case Event::kStartElement:
261        depth++;
262        break;
263      case Event::kEndElement:
264        depth--;
265        break;
266      default:
267        break;
268    }
269  }
270  return true;
271}
272
273inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) {
274  return event != Event::kBadDocument && event != Event::kEndDocument;
275}
276
277inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
278  int cmp = namespace_uri.compare(rhs.namespace_uri);
279  if (cmp != 0) return cmp;
280  return name.compare(rhs.name);
281}
282
283inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
284  return compare(rhs) < 0;
285}
286
287inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
288  return compare(rhs) == 0;
289}
290
291inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
292  return compare(rhs) != 0;
293}
294
295inline XmlPullParser::const_iterator XmlPullParser::FindAttribute(
296    android::StringPiece namespace_uri, android::StringPiece name) const {
297  const auto end_iter = end_attributes();
298  const auto iter = std::lower_bound(
299      begin_attributes(), end_iter,
300      std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name),
301      [](const Attribute& attr,
302         const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool {
303        int cmp = attr.namespace_uri.compare(
304            0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size());
305        if (cmp < 0) return true;
306        if (cmp > 0) return false;
307        cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(),
308                                rhs.second.size());
309        if (cmp < 0) return true;
310        return false;
311      });
312
313  if (iter != end_iter && namespace_uri == iter->namespace_uri &&
314      name == iter->name) {
315    return iter;
316  }
317  return end_iter;
318}
319
320}  // namespace xml
321}  // namespace aapt
322
323#endif  // AAPT_XML_PULL_PARSER_H
324