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 <algorithm> 21 #include <ostream> 22 #include <string> 23 #include <vector> 24 25 #include "StringPiece.h" 26 27 namespace aapt { 28 29 class XmlPullParser { 30 public: 31 enum class Event { 32 kBadDocument, 33 kStartDocument, 34 kEndDocument, 35 36 kStartNamespace, 37 kEndNamespace, 38 kStartElement, 39 kEndElement, 40 kText, 41 kComment, 42 }; 43 44 static void skipCurrentElement(XmlPullParser* parser); 45 static bool isGoodEvent(Event event); 46 47 virtual ~XmlPullParser() {} 48 49 /** 50 * Returns the current event that is being processed. 51 */ 52 virtual Event getEvent() const = 0; 53 54 virtual const std::string& getLastError() const = 0; 55 56 /** 57 * Note, unlike XmlPullParser, the first call to next() will return 58 * StartElement of the first element. 59 */ 60 virtual Event next() = 0; 61 62 // 63 // These are available for all nodes. 64 // 65 66 virtual const std::u16string& getComment() const = 0; 67 virtual size_t getLineNumber() const = 0; 68 virtual size_t getDepth() const = 0; 69 70 /** 71 * Returns the character data for a Text event. 72 */ 73 virtual const std::u16string& getText() const = 0; 74 75 // 76 // Namespace prefix and URI are available for StartNamespace and EndNamespace. 77 // 78 79 virtual const std::u16string& getNamespacePrefix() const = 0; 80 virtual const std::u16string& getNamespaceUri() const = 0; 81 82 /* 83 * Uses the current stack of namespaces to resolve the package. Eg: 84 * xmlns:app = "http://schemas.android.com/apk/res/com.android.app" 85 * ... 86 * android:text="@app:string/message" 87 * 88 * In this case, 'app' will be converted to 'com.android.app'. 89 * 90 * If xmlns:app="http://schemas.android.com/apk/res-auto", then 91 * 'package' will be set to 'defaultPackage'. 92 */ 93 virtual bool applyPackageAlias(std::u16string* package, 94 const std::u16string& defaultPackage) const = 0; 95 96 // 97 // These are available for StartElement and EndElement. 98 // 99 100 virtual const std::u16string& getElementNamespace() const = 0; 101 virtual const std::u16string& getElementName() const = 0; 102 103 // 104 // Remaining methods are for retrieving information about attributes 105 // associated with a StartElement. 106 // 107 // Attributes must be in sorted order (according to the less than operator 108 // of struct Attribute). 109 // 110 111 struct Attribute { 112 std::u16string namespaceUri; 113 std::u16string name; 114 std::u16string value; 115 116 int compare(const Attribute& rhs) const; 117 bool operator<(const Attribute& rhs) const; 118 bool operator==(const Attribute& rhs) const; 119 bool operator!=(const Attribute& rhs) const; 120 }; 121 122 using const_iterator = std::vector<Attribute>::const_iterator; 123 124 virtual const_iterator beginAttributes() const = 0; 125 virtual const_iterator endAttributes() const = 0; 126 virtual size_t getAttributeCount() const = 0; 127 const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const; 128 }; 129 130 // 131 // Implementation 132 // 133 134 inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) { 135 switch (event) { 136 case XmlPullParser::Event::kBadDocument: return out << "BadDocument"; 137 case XmlPullParser::Event::kStartDocument: return out << "StartDocument"; 138 case XmlPullParser::Event::kEndDocument: return out << "EndDocument"; 139 case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace"; 140 case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace"; 141 case XmlPullParser::Event::kStartElement: return out << "StartElement"; 142 case XmlPullParser::Event::kEndElement: return out << "EndElement"; 143 case XmlPullParser::Event::kText: return out << "Text"; 144 case XmlPullParser::Event::kComment: return out << "Comment"; 145 } 146 return out; 147 } 148 149 inline void XmlPullParser::skipCurrentElement(XmlPullParser* parser) { 150 int depth = 1; 151 while (depth > 0) { 152 switch (parser->next()) { 153 case Event::kEndDocument: 154 case Event::kBadDocument: 155 return; 156 case Event::kStartElement: 157 depth++; 158 break; 159 case Event::kEndElement: 160 depth--; 161 break; 162 default: 163 break; 164 } 165 } 166 } 167 168 inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) { 169 return event != Event::kBadDocument && event != Event::kEndDocument; 170 } 171 172 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const { 173 int cmp = namespaceUri.compare(rhs.namespaceUri); 174 if (cmp != 0) return cmp; 175 return name.compare(rhs.name); 176 } 177 178 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const { 179 return compare(rhs) < 0; 180 } 181 182 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const { 183 return compare(rhs) == 0; 184 } 185 186 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { 187 return compare(rhs) != 0; 188 } 189 190 inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri, 191 StringPiece16 name) const { 192 const auto endIter = endAttributes(); 193 const auto iter = std::lower_bound(beginAttributes(), endIter, 194 std::pair<StringPiece16, StringPiece16>(namespaceUri, name), 195 [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool { 196 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(), 197 rhs.first.data(), rhs.first.size()); 198 if (cmp < 0) return true; 199 if (cmp > 0) return false; 200 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size()); 201 if (cmp < 0) return true; 202 return false; 203 } 204 ); 205 206 if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) { 207 return iter; 208 } 209 return endIter; 210 } 211 212 } // namespace aapt 213 214 #endif // AAPT_XML_PULL_PARSER_H 215