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 "process/IResourceTableConsumer.h" 35 #include "util/Maybe.h" 36 #include "xml/XmlUtil.h" 37 38 namespace aapt { 39 namespace xml { 40 41 class XmlPullParser : public IPackageDeclStack { 42 public: 43 enum class Event { 44 kBadDocument, 45 kStartDocument, 46 kEndDocument, 47 48 kStartNamespace, 49 kEndNamespace, 50 kStartElement, 51 kEndElement, 52 kText, 53 kComment, 54 }; 55 56 /** 57 * Skips to the next direct descendant node of the given start_depth, 58 * skipping namespace nodes. 59 * 60 * When NextChildNode() returns true, you can expect Comments, Text, and 61 * StartElement events. 62 */ 63 static bool NextChildNode(XmlPullParser* parser, size_t start_depth); 64 static bool SkipCurrentElement(XmlPullParser* parser); 65 static bool IsGoodEvent(Event event); 66 67 explicit XmlPullParser(std::istream& in); 68 ~XmlPullParser(); 69 70 /** 71 * Returns the current event that is being processed. 72 */ 73 Event event() const; 74 75 const std::string& error() const; 76 77 /** 78 * Note, unlike XmlPullParser, the first call to next() will return 79 * StartElement of the first element. 80 */ 81 Event Next(); 82 83 // 84 // These are available for all nodes. 85 // 86 87 const std::string& comment() const; 88 size_t line_number() const; 89 size_t depth() const; 90 91 /** 92 * Returns the character data for a Text event. 93 */ 94 const std::string& text() const; 95 96 // 97 // Namespace prefix and URI are available for StartNamespace and EndNamespace. 98 // 99 100 const std::string& namespace_prefix() const; 101 const std::string& namespace_uri() const; 102 103 // 104 // These are available for StartElement and EndElement. 105 // 106 107 const std::string& element_namespace() const; 108 const std::string& element_name() const; 109 110 /* 111 * Uses the current stack of namespaces to resolve the package. Eg: 112 * xmlns:app = "http://schemas.android.com/apk/res/com.android.app" 113 * ... 114 * android:text="@app:string/message" 115 * 116 * In this case, 'app' will be converted to 'com.android.app'. 117 * 118 * If xmlns:app="http://schemas.android.com/apk/res-auto", then 119 * 'package' will be set to 'defaultPackage'. 120 */ 121 Maybe<ExtractedPackage> TransformPackageAlias( 122 const android::StringPiece& alias, const android::StringPiece& local_package) 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 std::istream& in_; 173 XML_Parser parser_; 174 char buffer_[16384]; 175 std::queue<EventData> event_queue_; 176 std::string error_; 177 const std::string empty_; 178 size_t depth_; 179 std::stack<std::string> namespace_uris_; 180 181 struct PackageDecl { 182 std::string prefix; 183 ExtractedPackage package; 184 }; 185 std::vector<PackageDecl> package_aliases_; 186 }; 187 188 /** 189 * Finds the attribute in the current element within the global namespace. 190 */ 191 Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser, 192 const android::StringPiece& name); 193 194 /** 195 * Finds the attribute in the current element within the global namespace. The 196 * attribute's value 197 * must not be the empty string. 198 */ 199 Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser, 200 const android::StringPiece& name); 201 202 // 203 // Implementation 204 // 205 206 inline ::std::ostream& operator<<(::std::ostream& out, 207 XmlPullParser::Event event) { 208 switch (event) { 209 case XmlPullParser::Event::kBadDocument: 210 return out << "BadDocument"; 211 case XmlPullParser::Event::kStartDocument: 212 return out << "StartDocument"; 213 case XmlPullParser::Event::kEndDocument: 214 return out << "EndDocument"; 215 case XmlPullParser::Event::kStartNamespace: 216 return out << "StartNamespace"; 217 case XmlPullParser::Event::kEndNamespace: 218 return out << "EndNamespace"; 219 case XmlPullParser::Event::kStartElement: 220 return out << "StartElement"; 221 case XmlPullParser::Event::kEndElement: 222 return out << "EndElement"; 223 case XmlPullParser::Event::kText: 224 return out << "Text"; 225 case XmlPullParser::Event::kComment: 226 return out << "Comment"; 227 } 228 return out; 229 } 230 NextChildNode(XmlPullParser * parser,size_t start_depth)231 inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, 232 size_t start_depth) { 233 Event event; 234 235 // First get back to the start depth. 236 while (IsGoodEvent(event = parser->Next()) && 237 parser->depth() > start_depth + 1) { 238 } 239 240 // Now look for the first good node. 241 while ((event != Event::kEndElement || parser->depth() > start_depth) && 242 IsGoodEvent(event)) { 243 switch (event) { 244 case Event::kText: 245 case Event::kComment: 246 case Event::kStartElement: 247 return true; 248 default: 249 break; 250 } 251 event = parser->Next(); 252 } 253 return false; 254 } 255 SkipCurrentElement(XmlPullParser * parser)256 inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) { 257 int depth = 1; 258 while (depth > 0) { 259 switch (parser->Next()) { 260 case Event::kEndDocument: 261 return true; 262 case Event::kBadDocument: 263 return false; 264 case Event::kStartElement: 265 depth++; 266 break; 267 case Event::kEndElement: 268 depth--; 269 break; 270 default: 271 break; 272 } 273 } 274 return true; 275 } 276 IsGoodEvent(XmlPullParser::Event event)277 inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) { 278 return event != Event::kBadDocument && event != Event::kEndDocument; 279 } 280 compare(const Attribute & rhs)281 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const { 282 int cmp = namespace_uri.compare(rhs.namespace_uri); 283 if (cmp != 0) return cmp; 284 return name.compare(rhs.name); 285 } 286 287 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const { 288 return compare(rhs) < 0; 289 } 290 291 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const { 292 return compare(rhs) == 0; 293 } 294 295 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { 296 return compare(rhs) != 0; 297 } 298 FindAttribute(android::StringPiece namespace_uri,android::StringPiece name)299 inline XmlPullParser::const_iterator XmlPullParser::FindAttribute( 300 android::StringPiece namespace_uri, android::StringPiece name) const { 301 const auto end_iter = end_attributes(); 302 const auto iter = std::lower_bound( 303 begin_attributes(), end_iter, 304 std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name), 305 [](const Attribute& attr, 306 const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool { 307 int cmp = attr.namespace_uri.compare( 308 0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size()); 309 if (cmp < 0) return true; 310 if (cmp > 0) return false; 311 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), 312 rhs.second.size()); 313 if (cmp < 0) return true; 314 return false; 315 }); 316 317 if (iter != end_iter && namespace_uri == iter->namespace_uri && 318 name == iter->name) { 319 return iter; 320 } 321 return end_iter; 322 } 323 324 } // namespace xml 325 } // namespace aapt 326 327 #endif // AAPT_XML_PULL_PARSER_H 328