blob: a0ce21dd3218f72535a1d2fbb777fe5e30a2d868 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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
Adam Lesinski1ab598f2015-08-14 14:26:04 -070020#include "util/Maybe.h"
21#include "Resource.h"
22#include "util/StringPiece.h"
23
24#include "process/IResourceTableConsumer.h"
25
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080026#include <algorithm>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include <expat.h>
28#include <istream>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080029#include <ostream>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070030#include <queue>
31#include <stack>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080032#include <string>
33#include <vector>
34
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080035namespace aapt {
36
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037class XmlPullParser : public IPackageDeclStack {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080038public:
39 enum class Event {
40 kBadDocument,
41 kStartDocument,
42 kEndDocument,
43
44 kStartNamespace,
45 kEndNamespace,
46 kStartElement,
47 kEndElement,
48 kText,
49 kComment,
50 };
51
Adam Lesinski1ab598f2015-08-14 14:26:04 -070052 /**
53 * Skips to the next direct descendant node of the given startDepth,
54 * skipping namespace nodes.
55 *
56 * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
57 */
58 static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
59 static bool skipCurrentElement(XmlPullParser* parser);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080060 static bool isGoodEvent(Event event);
61
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 XmlPullParser(std::istream& in);
63 virtual ~XmlPullParser();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080064
65 /**
66 * Returns the current event that is being processed.
67 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -070068 Event getEvent() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080069
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070 const std::string& getLastError() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071
72 /**
73 * Note, unlike XmlPullParser, the first call to next() will return
74 * StartElement of the first element.
75 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -070076 Event next();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077
78 //
79 // These are available for all nodes.
80 //
81
Adam Lesinski1ab598f2015-08-14 14:26:04 -070082 const std::u16string& getComment() const;
83 size_t getLineNumber() const;
84 size_t getDepth() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080085
86 /**
87 * Returns the character data for a Text event.
88 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -070089 const std::u16string& getText() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080090
Adam Lesinski24aad162015-04-24 19:19:30 -070091 //
92 // Namespace prefix and URI are available for StartNamespace and EndNamespace.
93 //
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080094
Adam Lesinski1ab598f2015-08-14 14:26:04 -070095 const std::u16string& getNamespacePrefix() const;
96 const std::u16string& getNamespaceUri() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080097
Adam Lesinski24aad162015-04-24 19:19:30 -070098 /*
99 * Uses the current stack of namespaces to resolve the package. Eg:
100 * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
101 * ...
102 * android:text="@app:string/message"
103 *
104 * In this case, 'app' will be converted to 'com.android.app'.
105 *
106 * If xmlns:app="http://schemas.android.com/apk/res-auto", then
107 * 'package' will be set to 'defaultPackage'.
108 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700109 //
Adam Lesinski24aad162015-04-24 19:19:30 -0700110
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800111 //
112 // These are available for StartElement and EndElement.
113 //
114
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700115 const std::u16string& getElementNamespace() const;
116 const std::u16string& getElementName() const;
117
118 Maybe<ResourceName> transformPackage(const ResourceName& name,
119 const StringPiece16& localPackage) const override;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800120
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::u16string namespaceUri;
131 std::u16string name;
132 std::u16string 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
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142 const_iterator beginAttributes() const;
143 const_iterator endAttributes() const;
144 size_t getAttributeCount() const;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800145 const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700146
147private:
148 static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
149 static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
150 static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
151 static void XMLCALL endElementHandler(void* userData, const char* name);
152 static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
153 static void XMLCALL commentDataHandler(void* userData, const char* comment);
154
155 struct EventData {
156 Event event;
157 size_t lineNumber;
158 size_t depth;
159 std::u16string data1;
160 std::u16string data2;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700161 std::vector<Attribute> attributes;
162 };
163
164 std::istream& mIn;
165 XML_Parser mParser;
166 char mBuffer[16384];
167 std::queue<EventData> mEventQueue;
168 std::string mLastError;
169 const std::u16string mEmpty;
170 size_t mDepth;
171 std::stack<std::u16string> mNamespaceUris;
172 std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800173};
174
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800175//
176// Implementation
177//
178
179inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
180 switch (event) {
181 case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
182 case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
183 case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
184 case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
185 case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
186 case XmlPullParser::Event::kStartElement: return out << "StartElement";
187 case XmlPullParser::Event::kEndElement: return out << "EndElement";
188 case XmlPullParser::Event::kText: return out << "Text";
189 case XmlPullParser::Event::kComment: return out << "Comment";
190 }
191 return out;
192}
193
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700194inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
195 Event event;
196
197 // First get back to the start depth.
198 while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
199
200 // Now look for the first good node.
201 while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
202 switch (event) {
203 case Event::kText:
204 case Event::kComment:
205 case Event::kStartElement:
206 return true;
207 default:
208 break;
209 }
210 event = parser->next();
211 }
212 return false;
213}
214
215inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800216 int depth = 1;
217 while (depth > 0) {
218 switch (parser->next()) {
219 case Event::kEndDocument:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800221 case Event::kBadDocument:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700222 return false;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800223 case Event::kStartElement:
224 depth++;
225 break;
226 case Event::kEndElement:
227 depth--;
228 break;
229 default:
230 break;
231 }
232 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800234}
235
236inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
237 return event != Event::kBadDocument && event != Event::kEndDocument;
238}
239
240inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
241 int cmp = namespaceUri.compare(rhs.namespaceUri);
242 if (cmp != 0) return cmp;
243 return name.compare(rhs.name);
244}
245
246inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
247 return compare(rhs) < 0;
248}
249
250inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
251 return compare(rhs) == 0;
252}
253
254inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
255 return compare(rhs) != 0;
256}
257
258inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
259 StringPiece16 name) const {
260 const auto endIter = endAttributes();
261 const auto iter = std::lower_bound(beginAttributes(), endIter,
262 std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
263 [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
264 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
265 rhs.first.data(), rhs.first.size());
266 if (cmp < 0) return true;
267 if (cmp > 0) return false;
268 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
269 if (cmp < 0) return true;
270 return false;
271 }
272 );
273
274 if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
275 return iter;
276 }
277 return endIter;
278}
279
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800280} // namespace aapt
281
282#endif // AAPT_XML_PULL_PARSER_H