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 <algorithm> 27 #include <expat.h> 28 #include <istream> 29 #include <ostream> 30 #include <queue> 31 #include <stack> 32 #include <string> 33 #include <vector> 34 35 namespace aapt { 36 namespace xml { 37 38 class 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 StartElement events. 58 */ 59 static bool nextChildNode(XmlPullParser* parser, size_t startDepth); 60 static bool skipCurrentElement(XmlPullParser* parser); 61 static bool isGoodEvent(Event event); 62 63 XmlPullParser(std::istream& in); 64 ~XmlPullParser(); 65 66 /** 67 * Returns the current event that is being processed. 68 */ 69 Event getEvent() const; 70 71 const std::string& getLastError() const; 72 73 /** 74 * Note, unlike XmlPullParser, the first call to next() will return 75 * StartElement of the first element. 76 */ 77 Event next(); 78 79 // 80 // These are available for all nodes. 81 // 82 83 const std::u16string& getComment() const; 84 size_t getLineNumber() const; 85 size_t getDepth() const; 86 87 /** 88 * Returns the character data for a Text event. 89 */ 90 const std::u16string& getText() const; 91 92 // 93 // Namespace prefix and URI are available for StartNamespace and EndNamespace. 94 // 95 96 const std::u16string& getNamespacePrefix() const; 97 const std::u16string& getNamespaceUri() const; 98 99 // 100 // These are available for StartElement and EndElement. 101 // 102 103 const std::u16string& getElementNamespace() const; 104 const std::u16string& getElementName() const; 105 106 /* 107 * Uses the current stack of namespaces to resolve the package. Eg: 108 * xmlns:app = "http://schemas.android.com/apk/res/com.android.app" 109 * ... 110 * android:text="@app:string/message" 111 * 112 * In this case, 'app' will be converted to 'com.android.app'. 113 * 114 * If xmlns:app="http://schemas.android.com/apk/res-auto", then 115 * 'package' will be set to 'defaultPackage'. 116 */ 117 Maybe<ExtractedPackage> transformPackageAlias( 118 const StringPiece16& alias, const StringPiece16& localPackage) const override; 119 120 // 121 // Remaining methods are for retrieving information about attributes 122 // associated with a StartElement. 123 // 124 // Attributes must be in sorted order (according to the less than operator 125 // of struct Attribute). 126 // 127 128 struct Attribute { 129 std::u16string namespaceUri; 130 std::u16string name; 131 std::u16string value; 132 133 int compare(const Attribute& rhs) const; 134 bool operator<(const Attribute& rhs) const; 135 bool operator==(const Attribute& rhs) const; 136 bool operator!=(const Attribute& rhs) const; 137 }; 138 139 using const_iterator = std::vector<Attribute>::const_iterator; 140 141 const_iterator beginAttributes() const; 142 const_iterator endAttributes() const; 143 size_t getAttributeCount() const; 144 const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const; 145 146 private: 147 static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri); 148 static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs); 149 static void XMLCALL characterDataHandler(void* userData, const char* s, int len); 150 static void XMLCALL endElementHandler(void* userData, const char* name); 151 static void XMLCALL endNamespaceHandler(void* userData, const char* prefix); 152 static void XMLCALL commentDataHandler(void* userData, const char* comment); 153 154 struct EventData { 155 Event event; 156 size_t lineNumber; 157 size_t depth; 158 std::u16string data1; 159 std::u16string data2; 160 std::vector<Attribute> attributes; 161 }; 162 163 std::istream& mIn; 164 XML_Parser mParser; 165 char mBuffer[16384]; 166 std::queue<EventData> mEventQueue; 167 std::string mLastError; 168 const std::u16string mEmpty; 169 size_t mDepth; 170 std::stack<std::u16string> mNamespaceUris; 171 172 struct PackageDecl { 173 std::u16string prefix; 174 ExtractedPackage package; 175 }; 176 std::vector<PackageDecl> mPackageAliases; 177 }; 178 179 /** 180 * Finds the attribute in the current element within the global namespace. 181 */ 182 Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name); 183 184 /** 185 * Finds the attribute in the current element within the global namespace. The attribute's value 186 * must not be the empty string. 187 */ 188 Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name); 189 190 // 191 // Implementation 192 // 193 194 inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) { 195 switch (event) { 196 case XmlPullParser::Event::kBadDocument: return out << "BadDocument"; 197 case XmlPullParser::Event::kStartDocument: return out << "StartDocument"; 198 case XmlPullParser::Event::kEndDocument: return out << "EndDocument"; 199 case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace"; 200 case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace"; 201 case XmlPullParser::Event::kStartElement: return out << "StartElement"; 202 case XmlPullParser::Event::kEndElement: return out << "EndElement"; 203 case XmlPullParser::Event::kText: return out << "Text"; 204 case XmlPullParser::Event::kComment: return out << "Comment"; 205 } 206 return out; 207 } 208 209 inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) { 210 Event event; 211 212 // First get back to the start depth. 213 while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {} 214 215 // Now look for the first good node. 216 while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) { 217 switch (event) { 218 case Event::kText: 219 case Event::kComment: 220 case Event::kStartElement: 221 return true; 222 default: 223 break; 224 } 225 event = parser->next(); 226 } 227 return false; 228 } 229 230 inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) { 231 int depth = 1; 232 while (depth > 0) { 233 switch (parser->next()) { 234 case Event::kEndDocument: 235 return true; 236 case Event::kBadDocument: 237 return false; 238 case Event::kStartElement: 239 depth++; 240 break; 241 case Event::kEndElement: 242 depth--; 243 break; 244 default: 245 break; 246 } 247 } 248 return true; 249 } 250 251 inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) { 252 return event != Event::kBadDocument && event != Event::kEndDocument; 253 } 254 255 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const { 256 int cmp = namespaceUri.compare(rhs.namespaceUri); 257 if (cmp != 0) return cmp; 258 return name.compare(rhs.name); 259 } 260 261 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const { 262 return compare(rhs) < 0; 263 } 264 265 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const { 266 return compare(rhs) == 0; 267 } 268 269 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { 270 return compare(rhs) != 0; 271 } 272 273 inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri, 274 StringPiece16 name) const { 275 const auto endIter = endAttributes(); 276 const auto iter = std::lower_bound(beginAttributes(), endIter, 277 std::pair<StringPiece16, StringPiece16>(namespaceUri, name), 278 [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool { 279 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(), 280 rhs.first.data(), rhs.first.size()); 281 if (cmp < 0) return true; 282 if (cmp > 0) return false; 283 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size()); 284 if (cmp < 0) return true; 285 return false; 286 } 287 ); 288 289 if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) { 290 return iter; 291 } 292 return endIter; 293 } 294 295 } // namespace xml 296 } // namespace aapt 297 298 #endif // AAPT_XML_PULL_PARSER_H 299