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