blob: f7d7a03a4352b72d10f4b530e8977a1b727c9e59 [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;
161 std::u16string comment;
162 std::vector<Attribute> attributes;
163 };
164
165 std::istream& mIn;
166 XML_Parser mParser;
167 char mBuffer[16384];
168 std::queue<EventData> mEventQueue;
169 std::string mLastError;
170 const std::u16string mEmpty;
171 size_t mDepth;
172 std::stack<std::u16string> mNamespaceUris;
173 std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800174};
175
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800176//
177// Implementation
178//
179
180inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
181 switch (event) {
182 case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
183 case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
184 case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
185 case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
186 case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
187 case XmlPullParser::Event::kStartElement: return out << "StartElement";
188 case XmlPullParser::Event::kEndElement: return out << "EndElement";
189 case XmlPullParser::Event::kText: return out << "Text";
190 case XmlPullParser::Event::kComment: return out << "Comment";
191 }
192 return out;
193}
194
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700195inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
196 Event event;
197
198 // First get back to the start depth.
199 while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
200
201 // Now look for the first good node.
202 while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
203 switch (event) {
204 case Event::kText:
205 case Event::kComment:
206 case Event::kStartElement:
207 return true;
208 default:
209 break;
210 }
211 event = parser->next();
212 }
213 return false;
214}
215
216inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800217 int depth = 1;
218 while (depth > 0) {
219 switch (parser->next()) {
220 case Event::kEndDocument:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800222 case Event::kBadDocument:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700223 return false;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800224 case Event::kStartElement:
225 depth++;
226 break;
227 case Event::kEndElement:
228 depth--;
229 break;
230 default:
231 break;
232 }
233 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700234 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800235}
236
237inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
238 return event != Event::kBadDocument && event != Event::kEndDocument;
239}
240
241inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
242 int cmp = namespaceUri.compare(rhs.namespaceUri);
243 if (cmp != 0) return cmp;
244 return name.compare(rhs.name);
245}
246
247inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
248 return compare(rhs) < 0;
249}
250
251inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
252 return compare(rhs) == 0;
253}
254
255inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
256 return compare(rhs) != 0;
257}
258
259inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
260 StringPiece16 name) const {
261 const auto endIter = endAttributes();
262 const auto iter = std::lower_bound(beginAttributes(), endIter,
263 std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
264 [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
265 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
266 rhs.first.data(), rhs.first.size());
267 if (cmp < 0) return true;
268 if (cmp > 0) return false;
269 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
270 if (cmp < 0) return true;
271 return false;
272 }
273 );
274
275 if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
276 return iter;
277 }
278 return endIter;
279}
280
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800281} // namespace aapt
282
283#endif // AAPT_XML_PULL_PARSER_H