/* * 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. */ #include "util/Maybe.h" #include "util/Util.h" #include "xml/XmlPullParser.h" #include "xml/XmlUtil.h" #include <iostream> #include <string> namespace aapt { namespace xml { constexpr char kXmlNamespaceSep = 1; XmlPullParser::XmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) { mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep); XML_SetUserData(mParser, this); XML_SetElementHandler(mParser, startElementHandler, endElementHandler); XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler); XML_SetCharacterDataHandler(mParser, characterDataHandler); XML_SetCommentHandler(mParser, commentDataHandler); mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ }); } XmlPullParser::~XmlPullParser() { XML_ParserFree(mParser); } XmlPullParser::Event XmlPullParser::next() { const Event currentEvent = getEvent(); if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) { return currentEvent; } mEventQueue.pop(); while (mEventQueue.empty()) { mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer)); const bool done = mIn.eof(); if (mIn.bad() && !done) { mLastError = strerror(errno); mEventQueue.push(EventData{ Event::kBadDocument }); continue; } if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) { mLastError = XML_ErrorString(XML_GetErrorCode(mParser)); mEventQueue.push(EventData{ Event::kBadDocument }); continue; } if (done) { mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 }); } } Event event = getEvent(); // Record namespace prefixes and package names so that we can do our own // handling of references that use namespace aliases. if (event == Event::kStartNamespace || event == Event::kEndNamespace) { Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri()); if (event == Event::kStartNamespace) { if (result) { mPackageAliases.emplace_back( PackageDecl{ getNamespacePrefix(), std::move(result.value()) }); } } else { if (result) { mPackageAliases.pop_back(); } } } return event; } XmlPullParser::Event XmlPullParser::getEvent() const { return mEventQueue.front().event; } const std::string& XmlPullParser::getLastError() const { return mLastError; } const std::u16string& XmlPullParser::getComment() const { return mEventQueue.front().data1; } size_t XmlPullParser::getLineNumber() const { return mEventQueue.front().lineNumber; } size_t XmlPullParser::getDepth() const { return mEventQueue.front().depth; } const std::u16string& XmlPullParser::getText() const { if (getEvent() != Event::kText) { return mEmpty; } return mEventQueue.front().data1; } const std::u16string& XmlPullParser::getNamespacePrefix() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) { return mEmpty; } return mEventQueue.front().data1; } const std::u16string& XmlPullParser::getNamespaceUri() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) { return mEmpty; } return mEventQueue.front().data2; } Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias( const StringPiece16& alias, const StringPiece16& localPackage) const { if (alias.empty()) { return ExtractedPackage{ localPackage.toString(), false /* private */ }; } const auto endIter = mPackageAliases.rend(); for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) { if (alias == iter->prefix) { if (iter->package.package.empty()) { return ExtractedPackage{ localPackage.toString(), iter->package.privateNamespace }; } return iter->package; } } return {}; } const std::u16string& XmlPullParser::getElementNamespace() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) { return mEmpty; } return mEventQueue.front().data1; } const std::u16string& XmlPullParser::getElementName() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) { return mEmpty; } return mEventQueue.front().data2; } XmlPullParser::const_iterator XmlPullParser::beginAttributes() const { return mEventQueue.front().attributes.begin(); } XmlPullParser::const_iterator XmlPullParser::endAttributes() const { return mEventQueue.front().attributes.end(); } size_t XmlPullParser::getAttributeCount() const { if (getEvent() != Event::kStartElement) { return 0; } return mEventQueue.front().attributes.size(); } /** * Extracts the namespace and name of an expanded element or attribute name. */ static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) { const char* p = name; while (*p != 0 && *p != kXmlNamespaceSep) { p++; } if (*p == 0) { outNs = std::u16string(); outName = util::utf8ToUtf16(name); } else { outNs = util::utf8ToUtf16(StringPiece(name, (p - name))); outName = util::utf8ToUtf16(p + 1); } } void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix, const char* uri) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string(); parser->mNamespaceUris.push(namespaceUri); parser->mEventQueue.push(EventData{ Event::kStartNamespace, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++, prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(), namespaceUri }); } void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name, const char** attrs) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); EventData data = { Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++ }; splitName(name, data.data1, data.data2); while (*attrs) { Attribute attribute; splitName(*attrs++, attribute.namespaceUri, attribute.name); attribute.value = util::utf8ToUtf16(*attrs++); // Insert in sorted order. auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute); data.attributes.insert(iter, std::move(attribute)); } // Move the structure into the queue (no copy). parser->mEventQueue.push(std::move(data)); } void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, int len) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); parser->mEventQueue.push(EventData{ Event::kText, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth, util::utf8ToUtf16(StringPiece(s, len)) }); } void XMLCALL XmlPullParser::endElementHandler(void* userData, const char* name) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); EventData data = { Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth) }; splitName(name, data.data1, data.data2); // Move the data into the queue (no copy). parser->mEventQueue.push(std::move(data)); } void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* prefix) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); parser->mEventQueue.push(EventData{ Event::kEndNamespace, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth), prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(), parser->mNamespaceUris.top() }); parser->mNamespaceUris.pop(); } void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comment) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); parser->mEventQueue.push(EventData{ Event::kComment, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth, util::utf8ToUtf16(comment) }); } Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) { auto iter = parser->findAttribute(u"", name); if (iter != parser->endAttributes()) { return StringPiece16(util::trimWhitespace(iter->value)); } return {}; } Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) { auto iter = parser->findAttribute(u"", name); if (iter != parser->endAttributes()) { StringPiece16 trimmed = util::trimWhitespace(iter->value); if (!trimmed.empty()) { return trimmed; } } return {}; } } // namespace xml } // namespace aapt