diff options
Diffstat (limited to 'tools/aapt2/xml/XmlPullParser.h')
-rw-r--r-- | tools/aapt2/xml/XmlPullParser.h | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h new file mode 100644 index 000000000000..7e7070e5e5ea --- /dev/null +++ b/tools/aapt2/xml/XmlPullParser.h @@ -0,0 +1,298 @@ +/* + * 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 "Resource.h" +#include "process/IResourceTableConsumer.h" +#include "util/Maybe.h" +#include "util/StringPiece.h" +#include "xml/XmlUtil.h" + +#include <algorithm> +#include <expat.h> +#include <istream> +#include <ostream> +#include <queue> +#include <stack> +#include <string> +#include <vector> + +namespace aapt { +namespace xml { + +class XmlPullParser : public IPackageDeclStack { +public: + enum class Event { + kBadDocument, + kStartDocument, + kEndDocument, + + kStartNamespace, + kEndNamespace, + kStartElement, + kEndElement, + kText, + kComment, + }; + + /** + * Skips to the next direct descendant node of the given startDepth, + * skipping namespace nodes. + * + * When nextChildNode returns true, you can expect Comments, Text, and StartElement events. + */ + static bool nextChildNode(XmlPullParser* parser, size_t startDepth); + static bool skipCurrentElement(XmlPullParser* parser); + static bool isGoodEvent(Event event); + + XmlPullParser(std::istream& in); + ~XmlPullParser(); + + /** + * Returns the current event that is being processed. + */ + Event getEvent() const; + + const std::string& getLastError() 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::u16string& getComment() const; + size_t getLineNumber() const; + size_t getDepth() const; + + /** + * Returns the character data for a Text event. + */ + const std::u16string& getText() const; + + // + // Namespace prefix and URI are available for StartNamespace and EndNamespace. + // + + const std::u16string& getNamespacePrefix() const; + const std::u16string& getNamespaceUri() const; + + // + // These are available for StartElement and EndElement. + // + + const std::u16string& getElementNamespace() const; + const std::u16string& getElementName() 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 StringPiece16& alias, const StringPiece16& localPackage) const override; + + // + // 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::u16string namespaceUri; + std::u16string name; + std::u16string 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 beginAttributes() const; + const_iterator endAttributes() const; + size_t getAttributeCount() const; + const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const; + +private: + static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri); + static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs); + static void XMLCALL characterDataHandler(void* userData, const char* s, int len); + static void XMLCALL endElementHandler(void* userData, const char* name); + static void XMLCALL endNamespaceHandler(void* userData, const char* prefix); + static void XMLCALL commentDataHandler(void* userData, const char* comment); + + struct EventData { + Event event; + size_t lineNumber; + size_t depth; + std::u16string data1; + std::u16string data2; + std::vector<Attribute> attributes; + }; + + std::istream& mIn; + XML_Parser mParser; + char mBuffer[16384]; + std::queue<EventData> mEventQueue; + std::string mLastError; + const std::u16string mEmpty; + size_t mDepth; + std::stack<std::u16string> mNamespaceUris; + + struct PackageDecl { + std::u16string prefix; + ExtractedPackage package; + }; + std::vector<PackageDecl> mPackageAliases; +}; + +/** + * Finds the attribute in the current element within the global namespace. + */ +Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name); + +/** + * Finds the attribute in the current element within the global namespace. The attribute's value + * must not be the empty string. + */ +Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& 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"; + } + return out; +} + +inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) { + Event event; + + // First get back to the start depth. + while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {} + + // Now look for the first good node. + while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) { + switch (event) { + case Event::kText: + case Event::kComment: + case Event::kStartElement: + 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 = namespaceUri.compare(rhs.namespaceUri); + 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(StringPiece16 namespaceUri, + StringPiece16 name) const { + const auto endIter = endAttributes(); + const auto iter = std::lower_bound(beginAttributes(), endIter, + std::pair<StringPiece16, StringPiece16>(namespaceUri, name), + [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool { + int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.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 != endIter && namespaceUri == iter->namespaceUri && name == iter->name) { + return iter; + } + return endIter; +} + +} // namespace xml +} // namespace aapt + +#endif // AAPT_XML_PULL_PARSER_H |