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 39 namespace aapt { 40 namespace xml { 41 42 class 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( 123 const android::StringPiece& alias, const android::StringPiece& local_package) const override; 124 125 // 126 // Remaining methods are for retrieving information about attributes 127 // associated with a StartElement. 128 // 129 // Attributes must be in sorted order (according to the less than operator 130 // of struct Attribute). 131 // 132 133 struct Attribute { 134 std::string namespace_uri; 135 std::string name; 136 std::string value; 137 138 int compare(const Attribute& rhs) const; 139 bool operator<(const Attribute& rhs) const; 140 bool operator==(const Attribute& rhs) const; 141 bool operator!=(const Attribute& rhs) const; 142 }; 143 144 using const_iterator = std::vector<Attribute>::const_iterator; 145 146 const_iterator begin_attributes() const; 147 const_iterator end_attributes() const; 148 size_t attribute_count() const; 149 const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const; 150 151 private: 152 DISALLOW_COPY_AND_ASSIGN(XmlPullParser); 153 154 static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix, 155 const char* uri); 156 static void XMLCALL StartElementHandler(void* user_data, const char* name, 157 const char** attrs); 158 static void XMLCALL CharacterDataHandler(void* user_data, const char* s, 159 int len); 160 static void XMLCALL EndElementHandler(void* user_data, const char* name); 161 static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix); 162 static void XMLCALL CommentDataHandler(void* user_data, const char* comment); 163 164 struct EventData { 165 Event event; 166 size_t line_number; 167 size_t depth; 168 std::string data1; 169 std::string data2; 170 std::vector<Attribute> attributes; 171 }; 172 173 io::InputStream* in_; 174 XML_Parser parser_; 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 231 inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) { 232 Event event; 233 234 // First get back to the start depth. 235 while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) { 236 } 237 238 // Now look for the first good node. 239 while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) { 240 switch (event) { 241 case Event::kText: 242 case Event::kComment: 243 case Event::kStartElement: 244 return true; 245 default: 246 break; 247 } 248 event = parser->Next(); 249 } 250 return false; 251 } 252 253 inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) { 254 int depth = 1; 255 while (depth > 0) { 256 switch (parser->Next()) { 257 case Event::kEndDocument: 258 return true; 259 case Event::kBadDocument: 260 return false; 261 case Event::kStartElement: 262 depth++; 263 break; 264 case Event::kEndElement: 265 depth--; 266 break; 267 default: 268 break; 269 } 270 } 271 return true; 272 } 273 274 inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) { 275 return event != Event::kBadDocument && event != Event::kEndDocument; 276 } 277 278 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const { 279 int cmp = namespace_uri.compare(rhs.namespace_uri); 280 if (cmp != 0) return cmp; 281 return name.compare(rhs.name); 282 } 283 284 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const { 285 return compare(rhs) < 0; 286 } 287 288 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const { 289 return compare(rhs) == 0; 290 } 291 292 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { 293 return compare(rhs) != 0; 294 } 295 296 inline XmlPullParser::const_iterator XmlPullParser::FindAttribute( 297 android::StringPiece namespace_uri, android::StringPiece name) const { 298 const auto end_iter = end_attributes(); 299 const auto iter = std::lower_bound( 300 begin_attributes(), end_iter, 301 std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name), 302 [](const Attribute& attr, 303 const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool { 304 int cmp = attr.namespace_uri.compare( 305 0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size()); 306 if (cmp < 0) return true; 307 if (cmp > 0) return false; 308 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), 309 rhs.second.size()); 310 if (cmp < 0) return true; 311 return false; 312 }); 313 314 if (iter != end_iter && namespace_uri == iter->namespace_uri && 315 name == iter->name) { 316 return iter; 317 } 318 return end_iter; 319 } 320 321 } // namespace xml 322 } // namespace aapt 323 324 #endif // AAPT_XML_PULL_PARSER_H 325