Ryan Mitchell 326e35ffaf Add <macro> tag to aapt2
AAPT2 Macros are compile-time resources definitions that are expanded
when referenced during the link phase.

A macro must be defined in the res/values.xml directory. A macro
definition for a macro named "foo" looks like the following:
 <macro name="foo">contents</macro>

When "@macro/foo" is used in the res/values directory or in a compiled
XML file, the contents of the macro replace the macro reference and
then the substituted contents are compiled and linked. If the macro
contents reference xml namespaces from its original definition, the
namespaces of the original macro definition will be used to determine
which package is being referenced.

Macros can be used anywhere resources can be referenced using the
@package:type/entry syntax.

Macros are not included in the final resource table or the R.java since
they are not actual resources.

Bug: 175616308
Test: aapt2_tests
Change-Id: I48b29ab6564357b32b4b4e32bff7ef06036382bc
2021-04-28 14:58:23 -07:00

334 lines
11 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 <iostream>
#include <string>
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
#include "xml/XmlUtil.h"
using ::aapt::io::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) {
Maybe<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;
}
Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const 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_ });
}
Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
return StringPiece(util::TrimWhitespace(iter->value));
}
return {};
}
Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
StringPiece trimmed = util::TrimWhitespace(iter->value);
if (!trimmed.empty()) {
return trimmed;
}
}
return {};
}
} // namespace xml
} // namespace aapt