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 <algorithm>
21 #include <ostream>
22 #include <string>
23 #include <vector>
24
25 #include "StringPiece.h"
26
27 namespace aapt {
28
29 class XmlPullParser {
30 public:
31 enum class Event {
32 kBadDocument,
33 kStartDocument,
34 kEndDocument,
35
36 kStartNamespace,
37 kEndNamespace,
38 kStartElement,
39 kEndElement,
40 kText,
41 kComment,
42 };
43
44 static void skipCurrentElement(XmlPullParser* parser);
45 static bool isGoodEvent(Event event);
46
~XmlPullParser()47 virtual ~XmlPullParser() {}
48
49 /**
50 * Returns the current event that is being processed.
51 */
52 virtual Event getEvent() const = 0;
53
54 virtual const std::string& getLastError() const = 0;
55
56 /**
57 * Note, unlike XmlPullParser, the first call to next() will return
58 * StartElement of the first element.
59 */
60 virtual Event next() = 0;
61
62 //
63 // These are available for all nodes.
64 //
65
66 virtual const std::u16string& getComment() const = 0;
67 virtual size_t getLineNumber() const = 0;
68 virtual size_t getDepth() const = 0;
69
70 /**
71 * Returns the character data for a Text event.
72 */
73 virtual const std::u16string& getText() const = 0;
74
75 //
76 // Namespace prefix and URI are available for StartNamespace and EndNamespace.
77 //
78
79 virtual const std::u16string& getNamespacePrefix() const = 0;
80 virtual const std::u16string& getNamespaceUri() const = 0;
81
82 /*
83 * Uses the current stack of namespaces to resolve the package. Eg:
84 * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
85 * ...
86 * android:text="@app:string/message"
87 *
88 * In this case, 'app' will be converted to 'com.android.app'.
89 *
90 * If xmlns:app="http://schemas.android.com/apk/res-auto", then
91 * 'package' will be set to 'defaultPackage'.
92 */
93 virtual bool applyPackageAlias(std::u16string* package,
94 const std::u16string& defaultPackage) const = 0;
95
96 //
97 // These are available for StartElement and EndElement.
98 //
99
100 virtual const std::u16string& getElementNamespace() const = 0;
101 virtual const std::u16string& getElementName() const = 0;
102
103 //
104 // Remaining methods are for retrieving information about attributes
105 // associated with a StartElement.
106 //
107 // Attributes must be in sorted order (according to the less than operator
108 // of struct Attribute).
109 //
110
111 struct Attribute {
112 std::u16string namespaceUri;
113 std::u16string name;
114 std::u16string value;
115
116 int compare(const Attribute& rhs) const;
117 bool operator<(const Attribute& rhs) const;
118 bool operator==(const Attribute& rhs) const;
119 bool operator!=(const Attribute& rhs) const;
120 };
121
122 using const_iterator = std::vector<Attribute>::const_iterator;
123
124 virtual const_iterator beginAttributes() const = 0;
125 virtual const_iterator endAttributes() const = 0;
126 virtual size_t getAttributeCount() const = 0;
127 const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
128 };
129
130 //
131 // Implementation
132 //
133
134 inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
135 switch (event) {
136 case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
137 case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
138 case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
139 case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
140 case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
141 case XmlPullParser::Event::kStartElement: return out << "StartElement";
142 case XmlPullParser::Event::kEndElement: return out << "EndElement";
143 case XmlPullParser::Event::kText: return out << "Text";
144 case XmlPullParser::Event::kComment: return out << "Comment";
145 }
146 return out;
147 }
148
skipCurrentElement(XmlPullParser * parser)149 inline void XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
150 int depth = 1;
151 while (depth > 0) {
152 switch (parser->next()) {
153 case Event::kEndDocument:
154 case Event::kBadDocument:
155 return;
156 case Event::kStartElement:
157 depth++;
158 break;
159 case Event::kEndElement:
160 depth--;
161 break;
162 default:
163 break;
164 }
165 }
166 }
167
isGoodEvent(XmlPullParser::Event event)168 inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
169 return event != Event::kBadDocument && event != Event::kEndDocument;
170 }
171
compare(const Attribute & rhs)172 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
173 int cmp = namespaceUri.compare(rhs.namespaceUri);
174 if (cmp != 0) return cmp;
175 return name.compare(rhs.name);
176 }
177
178 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
179 return compare(rhs) < 0;
180 }
181
182 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
183 return compare(rhs) == 0;
184 }
185
186 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
187 return compare(rhs) != 0;
188 }
189
findAttribute(StringPiece16 namespaceUri,StringPiece16 name)190 inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
191 StringPiece16 name) const {
192 const auto endIter = endAttributes();
193 const auto iter = std::lower_bound(beginAttributes(), endIter,
194 std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
195 [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
196 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
197 rhs.first.data(), rhs.first.size());
198 if (cmp < 0) return true;
199 if (cmp > 0) return false;
200 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
201 if (cmp < 0) return true;
202 return false;
203 }
204 );
205
206 if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
207 return iter;
208 }
209 return endIter;
210 }
211
212 } // namespace aapt
213
214 #endif // AAPT_XML_PULL_PARSER_H
215