Files
Jeremy Meyer 211bec2871 First pass at flagged resources
This gets the main parts of resource flagging in place and the basic use
case of flagging with an xml attribute working.

Test: Automated
Bug: 329436914
Flag: EXEMPT Aconfig not supported on host tools
Change-Id: Id2b5ba450d05da00a922e98ca204b6e5aa6c6c24
2024-07-12 13:29:55 -07:00

352 lines
12 KiB
C++

/*
* 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 "xml/XmlPullParser.h"
#include <algorithm>
#include <string>
#include <tuple>
#include "util/Util.h"
#include "xml/XmlUtil.h"
using ::android::InputStream;
using ::android::StringPiece;
namespace aapt {
namespace xml {
constexpr char kXmlNamespaceSep = 1;
XmlPullParser::XmlPullParser(InputStream* in) : in_(in), empty_(), depth_(0) {
parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
XML_SetUserData(parser_, this);
XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler);
XML_SetNamespaceDeclHandler(parser_, StartNamespaceHandler,
EndNamespaceHandler);
XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
XML_SetCommentHandler(parser_, CommentDataHandler);
XML_SetCdataSectionHandler(parser_, StartCdataSectionHandler, EndCdataSectionHandler);
event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
}
XmlPullParser::~XmlPullParser() {
XML_ParserFree(parser_);
}
XmlPullParser::Event XmlPullParser::Next() {
const Event currentEvent = event();
if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
return currentEvent;
}
event_queue_.pop();
while (event_queue_.empty()) {
const char* buffer = nullptr;
size_t buffer_size = 0;
bool done = false;
if (!in_->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) {
if (in_->HadError()) {
error_ = in_->GetError();
event_queue_.push(EventData{Event::kBadDocument});
break;
}
done = true;
}
if (XML_Parse(parser_, buffer, buffer_size, done) == XML_STATUS_ERROR) {
error_ = XML_ErrorString(XML_GetErrorCode(parser_));
event_queue_.push(EventData{Event::kBadDocument});
break;
}
if (done) {
event_queue_.push(EventData{Event::kEndDocument, 0, 0});
}
}
Event next_event = event();
// Record namespace prefixes and package names so that we can do our own
// handling of references that use namespace aliases.
if (next_event == Event::kStartNamespace ||
next_event == Event::kEndNamespace) {
std::optional<ExtractedPackage> result = ExtractPackageFromNamespace(namespace_uri());
if (next_event == Event::kStartNamespace) {
if (result) {
package_aliases_.emplace_back(
PackageDecl{namespace_prefix(), std::move(result.value())});
}
} else {
if (result) {
package_aliases_.pop_back();
}
}
}
return next_event;
}
XmlPullParser::Event XmlPullParser::event() const {
return event_queue_.front().event;
}
const std::string& XmlPullParser::error() const { return error_; }
const std::string& XmlPullParser::comment() const {
return event_queue_.front().data1;
}
size_t XmlPullParser::line_number() const {
return event_queue_.front().line_number;
}
size_t XmlPullParser::depth() const { return event_queue_.front().depth; }
const std::string& XmlPullParser::text() const {
if (event() != Event::kText) {
return empty_;
}
return event_queue_.front().data1;
}
const std::string& XmlPullParser::namespace_prefix() const {
const Event current_event = event();
if (current_event != Event::kStartNamespace &&
current_event != Event::kEndNamespace) {
return empty_;
}
return event_queue_.front().data1;
}
const std::string& XmlPullParser::namespace_uri() const {
const Event current_event = event();
if (current_event != Event::kStartNamespace &&
current_event != Event::kEndNamespace) {
return empty_;
}
return event_queue_.front().data2;
}
std::optional<ExtractedPackage> XmlPullParser::TransformPackageAlias(StringPiece alias) const {
if (alias.empty()) {
return ExtractedPackage{{}, false /*private*/};
}
const auto end_iter = package_aliases_.rend();
for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
if (alias == iter->prefix) {
if (iter->package.package.empty()) {
return ExtractedPackage{{}, iter->package.private_namespace};
}
return iter->package;
}
}
return {};
}
const std::string& XmlPullParser::element_namespace() const {
const Event current_event = event();
if (current_event != Event::kStartElement &&
current_event != Event::kEndElement) {
return empty_;
}
return event_queue_.front().data1;
}
const std::string& XmlPullParser::element_name() const {
const Event current_event = event();
if (current_event != Event::kStartElement &&
current_event != Event::kEndElement) {
return empty_;
}
return event_queue_.front().data2;
}
const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
return package_aliases_;
}
XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
return event_queue_.front().attributes.begin();
}
XmlPullParser::const_iterator XmlPullParser::end_attributes() const {
return event_queue_.front().attributes.end();
}
size_t XmlPullParser::attribute_count() const {
if (event() != Event::kStartElement) {
return 0;
}
return event_queue_.front().attributes.size();
}
/**
* Extracts the namespace and name of an expanded element or attribute name.
*/
static void SplitName(const char* name, std::string* out_ns, std::string* out_name) {
const char* p = name;
while (*p != 0 && *p != kXmlNamespaceSep) {
p++;
}
if (*p == 0) {
out_ns->clear();
out_name->assign(name);
} else {
out_ns->assign(name, (p - name));
out_name->assign(p + 1);
}
}
void XMLCALL XmlPullParser::StartNamespaceHandler(void* user_data,
const char* prefix,
const char* uri) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
std::string namespace_uri = uri != nullptr ? uri : std::string();
parser->namespace_uris_.push(namespace_uri);
parser->event_queue_.push(
EventData{Event::kStartNamespace,
XML_GetCurrentLineNumber(parser->parser_), parser->depth_++,
prefix != nullptr ? prefix : std::string(), namespace_uri});
}
void XMLCALL XmlPullParser::StartElementHandler(void* user_data,
const char* name,
const char** attrs) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
EventData data = {Event::kStartElement,
XML_GetCurrentLineNumber(parser->parser_),
parser->depth_++};
SplitName(name, &data.data1, &data.data2);
while (*attrs) {
Attribute attribute;
SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
attribute.value = *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->event_queue_.push(std::move(data));
}
void XMLCALL XmlPullParser::CharacterDataHandler(void* user_data, const char* s,
int len) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
parser->event_queue_.push(EventData{Event::kText, XML_GetCurrentLineNumber(parser->parser_),
parser->depth_, std::string(s, len)});
}
void XMLCALL XmlPullParser::EndElementHandler(void* user_data,
const char* name) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
EventData data = {Event::kEndElement,
XML_GetCurrentLineNumber(parser->parser_),
--(parser->depth_)};
SplitName(name, &data.data1, &data.data2);
// Move the data into the queue (no copy).
parser->event_queue_.push(std::move(data));
}
void XMLCALL XmlPullParser::EndNamespaceHandler(void* user_data,
const char* prefix) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
parser->event_queue_.push(
EventData{Event::kEndNamespace, XML_GetCurrentLineNumber(parser->parser_),
--(parser->depth_), prefix != nullptr ? prefix : std::string(),
parser->namespace_uris_.top()});
parser->namespace_uris_.pop();
}
void XMLCALL XmlPullParser::CommentDataHandler(void* user_data,
const char* comment) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
parser->event_queue_.push(EventData{Event::kComment,
XML_GetCurrentLineNumber(parser->parser_),
parser->depth_, comment});
}
void XMLCALL XmlPullParser::StartCdataSectionHandler(void* user_data) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
parser->event_queue_.push(EventData{Event::kCdataStart,
XML_GetCurrentLineNumber(parser->parser_),
parser->depth_ });
}
void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
parser->event_queue_.push(EventData{Event::kCdataEnd,
XML_GetCurrentLineNumber(parser->parser_),
parser->depth_ });
}
std::optional<StringPiece> FindAttribute(const XmlPullParser* parser, StringPiece name) {
return FindAttribute(parser, "", name);
}
std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser,
android::StringPiece namespace_uri,
android::StringPiece name) {
auto iter = parser->FindAttribute(namespace_uri, name);
if (iter != parser->end_attributes()) {
return StringPiece(util::TrimWhitespace(iter->value));
}
return {};
}
std::optional<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser, StringPiece name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
StringPiece trimmed = util::TrimWhitespace(iter->value);
if (!trimmed.empty()) {
return trimmed;
}
}
return {};
}
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::tuple(namespace_uri, name),
[](const Attribute& attr, const auto& rhs) {
return std::tie(attr.namespace_uri, attr.name) < rhs;
});
if (iter != end_iter && namespace_uri == iter->namespace_uri && name == iter->name) {
return iter;
}
return end_iter;
}
} // namespace xml
} // namespace aapt