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