XmlPullParser.h revision cacb28f2d60858106e2819cc7d95a65e8bda890b
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 "Resource.h"
21#include "process/IResourceTableConsumer.h"
22#include "util/Maybe.h"
23#include "util/StringPiece.h"
24#include "xml/XmlUtil.h"
25
26#include <expat.h>
27#include <algorithm>
28#include <istream>
29#include <ostream>
30#include <queue>
31#include <stack>
32#include <string>
33#include <vector>
34
35namespace aapt {
36namespace xml {
37
38class XmlPullParser : public IPackageDeclStack {
39 public:
40  enum class Event {
41    kBadDocument,
42    kStartDocument,
43    kEndDocument,
44
45    kStartNamespace,
46    kEndNamespace,
47    kStartElement,
48    kEndElement,
49    kText,
50    kComment,
51  };
52
53  /**
54   * Skips to the next direct descendant node of the given startDepth,
55   * skipping namespace nodes.
56   *
57   * When nextChildNode returns true, you can expect Comments, Text, and
58   * StartElement events.
59   */
60  static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
61  static bool skipCurrentElement(XmlPullParser* parser);
62  static bool isGoodEvent(Event event);
63
64  explicit XmlPullParser(std::istream& in);
65  ~XmlPullParser();
66
67  /**
68   * Returns the current event that is being processed.
69   */
70  Event getEvent() const;
71
72  const std::string& getLastError() const;
73
74  /**
75   * Note, unlike XmlPullParser, the first call to next() will return
76   * StartElement of the first element.
77   */
78  Event next();
79
80  //
81  // These are available for all nodes.
82  //
83
84  const std::string& getComment() const;
85  size_t getLineNumber() const;
86  size_t getDepth() const;
87
88  /**
89   * Returns the character data for a Text event.
90   */
91  const std::string& getText() const;
92
93  //
94  // Namespace prefix and URI are available for StartNamespace and EndNamespace.
95  //
96
97  const std::string& getNamespacePrefix() const;
98  const std::string& getNamespaceUri() const;
99
100  //
101  // These are available for StartElement and EndElement.
102  //
103
104  const std::string& getElementNamespace() const;
105  const std::string& getElementName() const;
106
107  /*
108   * Uses the current stack of namespaces to resolve the package. Eg:
109   * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
110   * ...
111   * android:text="@app:string/message"
112   *
113   * In this case, 'app' will be converted to 'com.android.app'.
114   *
115   * If xmlns:app="http://schemas.android.com/apk/res-auto", then
116   * 'package' will be set to 'defaultPackage'.
117   */
118  Maybe<ExtractedPackage> transformPackageAlias(
119      const StringPiece& alias, const StringPiece& localPackage) const override;
120
121  //
122  // Remaining methods are for retrieving information about attributes
123  // associated with a StartElement.
124  //
125  // Attributes must be in sorted order (according to the less than operator
126  // of struct Attribute).
127  //
128
129  struct Attribute {
130    std::string namespaceUri;
131    std::string name;
132    std::string value;
133
134    int compare(const Attribute& rhs) const;
135    bool operator<(const Attribute& rhs) const;
136    bool operator==(const Attribute& rhs) const;
137    bool operator!=(const Attribute& rhs) const;
138  };
139
140  using const_iterator = std::vector<Attribute>::const_iterator;
141
142  const_iterator beginAttributes() const;
143  const_iterator endAttributes() const;
144  size_t getAttributeCount() const;
145  const_iterator findAttribute(StringPiece namespaceUri,
146                               StringPiece name) const;
147
148 private:
149  static void XMLCALL startNamespaceHandler(void* userData, const char* prefix,
150                                            const char* uri);
151  static void XMLCALL startElementHandler(void* userData, const char* name,
152                                          const char** attrs);
153  static void XMLCALL characterDataHandler(void* userData, const char* s,
154                                           int len);
155  static void XMLCALL endElementHandler(void* userData, const char* name);
156  static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
157  static void XMLCALL commentDataHandler(void* userData, const char* comment);
158
159  struct EventData {
160    Event event;
161    size_t lineNumber;
162    size_t depth;
163    std::string data1;
164    std::string data2;
165    std::vector<Attribute> attributes;
166  };
167
168  std::istream& mIn;
169  XML_Parser mParser;
170  char mBuffer[16384];
171  std::queue<EventData> mEventQueue;
172  std::string mLastError;
173  const std::string mEmpty;
174  size_t mDepth;
175  std::stack<std::string> mNamespaceUris;
176
177  struct PackageDecl {
178    std::string prefix;
179    ExtractedPackage package;
180  };
181  std::vector<PackageDecl> mPackageAliases;
182};
183
184/**
185 * Finds the attribute in the current element within the global namespace.
186 */
187Maybe<StringPiece> findAttribute(const XmlPullParser* parser,
188                                 const StringPiece& name);
189
190/**
191 * Finds the attribute in the current element within the global namespace. The
192 * attribute's value
193 * must not be the empty string.
194 */
195Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser,
196                                         const StringPiece& name);
197
198//
199// Implementation
200//
201
202inline ::std::ostream& operator<<(::std::ostream& out,
203                                  XmlPullParser::Event event) {
204  switch (event) {
205    case XmlPullParser::Event::kBadDocument:
206      return out << "BadDocument";
207    case XmlPullParser::Event::kStartDocument:
208      return out << "StartDocument";
209    case XmlPullParser::Event::kEndDocument:
210      return out << "EndDocument";
211    case XmlPullParser::Event::kStartNamespace:
212      return out << "StartNamespace";
213    case XmlPullParser::Event::kEndNamespace:
214      return out << "EndNamespace";
215    case XmlPullParser::Event::kStartElement:
216      return out << "StartElement";
217    case XmlPullParser::Event::kEndElement:
218      return out << "EndElement";
219    case XmlPullParser::Event::kText:
220      return out << "Text";
221    case XmlPullParser::Event::kComment:
222      return out << "Comment";
223  }
224  return out;
225}
226
227inline bool XmlPullParser::nextChildNode(XmlPullParser* parser,
228                                         size_t startDepth) {
229  Event event;
230
231  // First get back to the start depth.
232  while (isGoodEvent(event = parser->next()) &&
233         parser->getDepth() > startDepth + 1) {
234  }
235
236  // Now look for the first good node.
237  while ((event != Event::kEndElement || parser->getDepth() > startDepth) &&
238         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 = namespaceUri.compare(rhs.namespaceUri);
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    StringPiece namespaceUri, StringPiece name) const {
297  const auto endIter = endAttributes();
298  const auto iter = std::lower_bound(
299      beginAttributes(), endIter,
300      std::pair<StringPiece, StringPiece>(namespaceUri, name),
301      [](const Attribute& attr,
302         const std::pair<StringPiece, StringPiece>& rhs) -> bool {
303        int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
304                                            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 != endIter && namespaceUri == iter->namespaceUri &&
314      name == iter->name) {
315    return iter;
316  }
317  return endIter;
318}
319
320}  // namespace xml
321}  // namespace aapt
322
323#endif  // AAPT_XML_PULL_PARSER_H
324