| /* | |
| * Copyright (C) 2015 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef AAPT_XML_PULL_PARSER_H | |
| #define AAPT_XML_PULL_PARSER_H | |
| #include <expat.h> | |
| #include <algorithm> | |
| #include <istream> | |
| #include <ostream> | |
| #include <queue> | |
| #include <stack> | |
| #include <string> | |
| #include <vector> | |
| #include "android-base/macros.h" | |
| #include "androidfw/StringPiece.h" | |
| #include "Resource.h" | |
| #include "io/Io.h" | |
| #include "process/IResourceTableConsumer.h" | |
| #include "util/Maybe.h" | |
| #include "xml/XmlUtil.h" | |
| namespace aapt { | |
| namespace xml { | |
| class XmlPullParser : public IPackageDeclStack { | |
| public: | |
| enum class Event { | |
| kBadDocument, | |
| kStartDocument, | |
| kEndDocument, | |
| kStartNamespace, | |
| kEndNamespace, | |
| kStartElement, | |
| kEndElement, | |
| kText, | |
| kComment, | |
| kCdataStart, | |
| kCdataEnd, | |
| }; | |
| /** | |
| * Skips to the next direct descendant node of the given start_depth, | |
| * skipping namespace nodes. | |
| * | |
| * When NextChildNode() returns true, you can expect Comments, Text, and | |
| * StartElement events. | |
| */ | |
| static bool NextChildNode(XmlPullParser* parser, size_t start_depth); | |
| static bool SkipCurrentElement(XmlPullParser* parser); | |
| static bool IsGoodEvent(Event event); | |
| explicit XmlPullParser(io::InputStream* in); | |
| ~XmlPullParser(); | |
| /** | |
| * Returns the current event that is being processed. | |
| */ | |
| Event event() const; | |
| const std::string& error() const; | |
| /** | |
| * Note, unlike XmlPullParser, the first call to next() will return | |
| * StartElement of the first element. | |
| */ | |
| Event Next(); | |
| // | |
| // These are available for all nodes. | |
| // | |
| const std::string& comment() const; | |
| size_t line_number() const; | |
| size_t depth() const; | |
| /** | |
| * Returns the character data for a Text event. | |
| */ | |
| const std::string& text() const; | |
| // | |
| // Namespace prefix and URI are available for StartNamespace and EndNamespace. | |
| // | |
| const std::string& namespace_prefix() const; | |
| const std::string& namespace_uri() const; | |
| // | |
| // These are available for StartElement and EndElement. | |
| // | |
| const std::string& element_namespace() const; | |
| const std::string& element_name() const; | |
| /* | |
| * Uses the current stack of namespaces to resolve the package. Eg: | |
| * xmlns:app = "http://schemas.android.com/apk/res/com.android.app" | |
| * ... | |
| * android:text="@app:string/message" | |
| * | |
| * In this case, 'app' will be converted to 'com.android.app'. | |
| * | |
| * If xmlns:app="http://schemas.android.com/apk/res-auto", then | |
| * 'package' will be set to 'defaultPackage'. | |
| */ | |
| Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override; | |
| struct PackageDecl { | |
| std::string prefix; | |
| ExtractedPackage package; | |
| }; | |
| const std::vector<PackageDecl>& package_decls() const; | |
| // | |
| // Remaining methods are for retrieving information about attributes | |
| // associated with a StartElement. | |
| // | |
| // Attributes must be in sorted order (according to the less than operator | |
| // of struct Attribute). | |
| // | |
| struct Attribute { | |
| std::string namespace_uri; | |
| std::string name; | |
| std::string value; | |
| int compare(const Attribute& rhs) const; | |
| bool operator<(const Attribute& rhs) const; | |
| bool operator==(const Attribute& rhs) const; | |
| bool operator!=(const Attribute& rhs) const; | |
| }; | |
| using const_iterator = std::vector<Attribute>::const_iterator; | |
| const_iterator begin_attributes() const; | |
| const_iterator end_attributes() const; | |
| size_t attribute_count() const; | |
| const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const; | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(XmlPullParser); | |
| static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix, | |
| const char* uri); | |
| static void XMLCALL StartElementHandler(void* user_data, const char* name, | |
| const char** attrs); | |
| static void XMLCALL CharacterDataHandler(void* user_data, const char* s, | |
| int len); | |
| static void XMLCALL EndElementHandler(void* user_data, const char* name); | |
| static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix); | |
| static void XMLCALL CommentDataHandler(void* user_data, const char* comment); | |
| static void XMLCALL StartCdataSectionHandler(void* user_data); | |
| static void XMLCALL EndCdataSectionHandler(void* user_data); | |
| struct EventData { | |
| Event event; | |
| size_t line_number; | |
| size_t depth; | |
| std::string data1; | |
| std::string data2; | |
| std::vector<Attribute> attributes; | |
| }; | |
| io::InputStream* in_; | |
| XML_Parser parser_; | |
| std::queue<EventData> event_queue_; | |
| std::string error_; | |
| const std::string empty_; | |
| size_t depth_; | |
| std::stack<std::string> namespace_uris_; | |
| std::vector<PackageDecl> package_aliases_; | |
| }; | |
| /** | |
| * Finds the attribute in the current element within the global namespace. | |
| */ | |
| Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser, | |
| const android::StringPiece& name); | |
| /** | |
| * Finds the attribute in the current element within the global namespace. The | |
| * attribute's value | |
| * must not be the empty string. | |
| */ | |
| Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser, | |
| const android::StringPiece& name); | |
| // | |
| // Implementation | |
| // | |
| inline ::std::ostream& operator<<(::std::ostream& out, | |
| XmlPullParser::Event event) { | |
| switch (event) { | |
| case XmlPullParser::Event::kBadDocument: | |
| return out << "BadDocument"; | |
| case XmlPullParser::Event::kStartDocument: | |
| return out << "StartDocument"; | |
| case XmlPullParser::Event::kEndDocument: | |
| return out << "EndDocument"; | |
| case XmlPullParser::Event::kStartNamespace: | |
| return out << "StartNamespace"; | |
| case XmlPullParser::Event::kEndNamespace: | |
| return out << "EndNamespace"; | |
| case XmlPullParser::Event::kStartElement: | |
| return out << "StartElement"; | |
| case XmlPullParser::Event::kEndElement: | |
| return out << "EndElement"; | |
| case XmlPullParser::Event::kText: | |
| return out << "Text"; | |
| case XmlPullParser::Event::kComment: | |
| return out << "Comment"; | |
| case XmlPullParser::Event::kCdataStart: | |
| return out << "CdataStart"; | |
| case XmlPullParser::Event::kCdataEnd: | |
| return out << "CdataEnd"; | |
| } | |
| return out; | |
| } | |
| inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) { | |
| Event event; | |
| // First get back to the start depth. | |
| while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) { | |
| } | |
| // Now look for the first good node. | |
| while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) { | |
| switch (event) { | |
| case Event::kText: | |
| case Event::kComment: | |
| case Event::kStartElement: | |
| case Event::kCdataStart: | |
| case Event::kCdataEnd: | |
| return true; | |
| default: | |
| break; | |
| } | |
| event = parser->Next(); | |
| } | |
| return false; | |
| } | |
| inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) { | |
| int depth = 1; | |
| while (depth > 0) { | |
| switch (parser->Next()) { | |
| case Event::kEndDocument: | |
| return true; | |
| case Event::kBadDocument: | |
| return false; | |
| case Event::kStartElement: | |
| depth++; | |
| break; | |
| case Event::kEndElement: | |
| depth--; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) { | |
| return event != Event::kBadDocument && event != Event::kEndDocument; | |
| } | |
| inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const { | |
| int cmp = namespace_uri.compare(rhs.namespace_uri); | |
| if (cmp != 0) return cmp; | |
| return name.compare(rhs.name); | |
| } | |
| inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const { | |
| return compare(rhs) < 0; | |
| } | |
| inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const { | |
| return compare(rhs) == 0; | |
| } | |
| inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { | |
| return compare(rhs) != 0; | |
| } | |
| inline XmlPullParser::const_iterator XmlPullParser::FindAttribute( | |
| android::StringPiece namespace_uri, android::StringPiece name) const { | |
| const auto end_iter = end_attributes(); | |
| const auto iter = std::lower_bound( | |
| begin_attributes(), end_iter, | |
| std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name), | |
| [](const Attribute& attr, | |
| const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool { | |
| int cmp = attr.namespace_uri.compare( | |
| 0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size()); | |
| if (cmp < 0) return true; | |
| if (cmp > 0) return false; | |
| cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), | |
| rhs.second.size()); | |
| if (cmp < 0) return true; | |
| return false; | |
| }); | |
| if (iter != end_iter && namespace_uri == iter->namespace_uri && | |
| name == iter->name) { | |
| return iter; | |
| } | |
| return end_iter; | |
| } | |
| } // namespace xml | |
| } // namespace aapt | |
| #endif // AAPT_XML_PULL_PARSER_H |