Merge "AAPT2: Rename to match new style"

This commit is contained in:
TreeHugger Robot
2016-10-27 04:31:02 +00:00
committed by Android (Google) Code Review
181 changed files with 13853 additions and 13329 deletions

View File

@@ -2,6 +2,7 @@
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
#include <utils/ByteOrder.h>
#include <utils/String8.h>
#include <fcntl.h>

View File

@@ -26,11 +26,12 @@
#include <utils/Compat.h>
#include <utils/Errors.h>
#include <utils/FileMap.h>
#include <utils/String8.h>
namespace android {
class FileMap;
/*
* Instances of this class provide read-only operations on a byte stream.
*

View File

@@ -22,7 +22,6 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
#include <utils/ByteOrder.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>

View File

@@ -18,6 +18,7 @@
#define __TYPE_WRAPPERS_H
#include <androidfw/ResourceTypes.h>
#include <utils/ByteOrder.h>
namespace android {

View File

@@ -1,3 +1,2 @@
BasedOnStyle: Google
ColumnLimit: 100

View File

@@ -158,7 +158,7 @@ hostStaticLibs_windows := libz
hostLdLibs_linux := -lz
hostLdLibs_darwin := -lz
cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
cFlags := -Wall -Werror -Wno-unused-parameter
cFlags_darwin := -D_DARWIN_UNLIMITED_STREAMS
cFlags_windows := -Wno-maybe-uninitialized # Incorrectly marking use of Maybe.value() as error.
cppFlags := -Wno-missing-field-initializers -fno-exceptions -fno-rtti

View File

@@ -17,10 +17,10 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
#include "util/Maybe.h"
#include <string>
#include "util/Maybe.h"
namespace aapt {
/**
@@ -36,17 +36,17 @@ struct AppInfo {
/**
* The App's minimum SDK version.
*/
Maybe<std::string> minSdkVersion;
Maybe<std::string> min_sdk_version;
/**
* The Version code of the app.
*/
Maybe<uint32_t> versionCode;
Maybe<uint32_t> version_code;
/**
* The revision code of the app.
*/
Maybe<uint32_t> revisionCode;
Maybe<uint32_t> revision_code;
};
} // namespace aapt

View File

@@ -15,22 +15,24 @@
*/
#include "ConfigDescription.h"
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "Locale.h"
#include "SdkConstants.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <string>
#include <vector>
namespace aapt {
using android::ResTable_config;
static const char* kWildcardName = "any";
const ConfigDescription& ConfigDescription::defaultConfig() {
const ConfigDescription& ConfigDescription::DefaultConfig() {
static ConfigDescription config = {};
return config;
}
@@ -589,169 +591,169 @@ static bool parseVersion(const char* name, ResTable_config* out) {
return true;
}
bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) {
std::vector<std::string> parts = util::splitAndLowercase(str, '-');
bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) {
std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
ConfigDescription config;
ssize_t partsConsumed = 0;
ssize_t parts_consumed = 0;
LocaleValue locale;
const auto partsEnd = parts.end();
auto partIter = parts.begin();
const auto parts_end = parts.end();
auto part_iter = parts.begin();
if (str.size() == 0) {
goto success;
}
if (parseMcc(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseMcc(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseMnc(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseMnc(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
// Locale spans a few '-' separators, so we let it
// control the index.
partsConsumed = locale.initFromParts(partIter, partsEnd);
if (partsConsumed < 0) {
parts_consumed = locale.InitFromParts(part_iter, parts_end);
if (parts_consumed < 0) {
return false;
} else {
locale.writeTo(&config);
partIter += partsConsumed;
if (partIter == partsEnd) {
locale.WriteTo(&config);
part_iter += parts_consumed;
if (part_iter == parts_end) {
goto success;
}
}
if (parseLayoutDirection(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseLayoutDirection(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseSmallestScreenWidthDp(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenWidthDp(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenWidthDp(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenHeightDp(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenHeightDp(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenLayoutSize(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenLayoutSize(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenLayoutLong(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenLayoutLong(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenRound(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenRound(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseOrientation(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseOrientation(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseUiModeType(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseUiModeType(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseUiModeNight(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseUiModeNight(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseDensity(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseDensity(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseTouchscreen(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseTouchscreen(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseKeysHidden(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseKeysHidden(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseKeyboard(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseKeyboard(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseNavHidden(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseNavHidden(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseNavigation(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseNavigation(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseScreenSize(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseScreenSize(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
if (parseVersion(partIter->c_str(), &config)) {
++partIter;
if (partIter == partsEnd) {
if (parseVersion(part_iter->c_str(), &config)) {
++part_iter;
if (part_iter == parts_end) {
goto success;
}
}
@@ -761,57 +763,57 @@ bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) {
success:
if (out != NULL) {
applyVersionForCompatibility(&config);
ApplyVersionForCompatibility(&config);
*out = config;
}
return true;
}
void ConfigDescription::applyVersionForCompatibility(
void ConfigDescription::ApplyVersionForCompatibility(
ConfigDescription* config) {
uint16_t minSdk = 0;
uint16_t min_sdk = 0;
if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
minSdk = SDK_MARSHMALLOW;
min_sdk = SDK_MARSHMALLOW;
} else if (config->density == ResTable_config::DENSITY_ANY) {
minSdk = SDK_LOLLIPOP;
min_sdk = SDK_LOLLIPOP;
} else if (config->smallestScreenWidthDp !=
ResTable_config::SCREENWIDTH_ANY ||
config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY ||
config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
minSdk = SDK_HONEYCOMB_MR2;
min_sdk = SDK_HONEYCOMB_MR2;
} else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) !=
ResTable_config::UI_MODE_TYPE_ANY ||
(config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) !=
ResTable_config::UI_MODE_NIGHT_ANY) {
minSdk = SDK_FROYO;
min_sdk = SDK_FROYO;
} else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) !=
ResTable_config::SCREENSIZE_ANY ||
(config->screenLayout & ResTable_config::MASK_SCREENLONG) !=
ResTable_config::SCREENLONG_ANY ||
config->density != ResTable_config::DENSITY_DEFAULT) {
minSdk = SDK_DONUT;
min_sdk = SDK_DONUT;
}
if (minSdk > config->sdkVersion) {
config->sdkVersion = minSdk;
if (min_sdk > config->sdkVersion) {
config->sdkVersion = min_sdk;
}
}
ConfigDescription ConfigDescription::copyWithoutSdkVersion() const {
ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const {
ConfigDescription copy = *this;
copy.sdkVersion = 0;
return copy;
}
bool ConfigDescription::dominates(const ConfigDescription& o) const {
if (*this == defaultConfig() || *this == o) {
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
if (*this == DefaultConfig() || *this == o) {
return true;
}
return matchWithDensity(o) && !o.matchWithDensity(*this) &&
!isMoreSpecificThan(o) && !o.hasHigherPrecedenceThan(*this);
return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
!isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
}
bool ConfigDescription::hasHigherPrecedenceThan(
bool ConfigDescription::HasHigherPrecedenceThan(
const ConfigDescription& o) const {
// The order of the following tests defines the importance of one
// configuration parameter over another. Those tests first are more
@@ -866,7 +868,7 @@ bool ConfigDescription::hasHigherPrecedenceThan(
return *this != o;
}
bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const {
// This method should be updated as new configuration parameters are
// introduced (e.g. screenConfig2).
auto pred = [](const uint32_t a, const uint32_t b) -> bool {
@@ -892,8 +894,8 @@ bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
!pred(keyboard, o.keyboard) || !pred(navigation, o.navigation);
}
bool ConfigDescription::isCompatibleWith(const ConfigDescription& o) const {
return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
}
} // namespace aapt

View File

@@ -17,11 +17,12 @@
#ifndef AAPT_CONFIG_DESCRIPTION_H
#define AAPT_CONFIG_DESCRIPTION_H
#include "util/StringPiece.h"
#include <androidfw/ResourceTypes.h>
#include <ostream>
#include "androidfw/ResourceTypes.h"
#include "util/StringPiece.h"
namespace aapt {
/*
@@ -32,7 +33,7 @@ struct ConfigDescription : public android::ResTable_config {
/**
* Returns an immutable default config.
*/
static const ConfigDescription& defaultConfig();
static const ConfigDescription& DefaultConfig();
/*
* Parse a string of the form 'fr-sw600dp-land' and fill in the
@@ -41,14 +42,14 @@ struct ConfigDescription : public android::ResTable_config {
* The resulting configuration has the appropriate sdkVersion defined
* for backwards compatibility.
*/
static bool parse(const StringPiece& str, ConfigDescription* out = nullptr);
static bool Parse(const StringPiece& str, ConfigDescription* out = nullptr);
/**
* If the configuration uses an axis that was added after
* the original Android release, make sure the SDK version
* is set accordingly.
*/
static void applyVersionForCompatibility(ConfigDescription* config);
static void ApplyVersionForCompatibility(ConfigDescription* config);
ConfigDescription();
ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
@@ -59,7 +60,7 @@ struct ConfigDescription : public android::ResTable_config {
ConfigDescription& operator=(const ConfigDescription& o);
ConfigDescription& operator=(ConfigDescription&& o);
ConfigDescription copyWithoutSdkVersion() const;
ConfigDescription CopyWithoutSdkVersion() const;
/**
* A configuration X dominates another configuration Y, if X has at least the
@@ -70,14 +71,14 @@ struct ConfigDescription : public android::ResTable_config {
* For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
* does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
*/
bool dominates(const ConfigDescription& o) const;
bool Dominates(const ConfigDescription& o) const;
/**
* Returns true if this configuration defines a more important configuration
* parameter than o. For example, "en" has higher precedence than "v23",
* whereas "en" has the same precedence as "en-v23".
*/
bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
bool HasHigherPrecedenceThan(const ConfigDescription& o) const;
/**
* A configuration conflicts with another configuration if both
@@ -85,7 +86,7 @@ struct ConfigDescription : public android::ResTable_config {
* incompatible configuration parameter is a non-range, non-density parameter
* that is defined in both configurations as a different, non-default value.
*/
bool conflictsWith(const ConfigDescription& o) const;
bool ConflictsWith(const ConfigDescription& o) const;
/**
* A configuration is compatible with another configuration if both
@@ -93,9 +94,9 @@ struct ConfigDescription : public android::ResTable_config {
* unrelated by domination. For example, land-v11 conflicts with port-v21
* but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
*/
bool isCompatibleWith(const ConfigDescription& o) const;
bool IsCompatibleWith(const ConfigDescription& o) const;
bool matchWithDensity(const ConfigDescription& o) const;
bool MatchWithDensity(const ConfigDescription& o) const;
bool operator<(const ConfigDescription& o) const;
bool operator<=(const ConfigDescription& o) const;
@@ -141,7 +142,7 @@ inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
return *this;
}
inline bool ConfigDescription::matchWithDensity(
inline bool ConfigDescription::MatchWithDensity(
const ConfigDescription& o) const {
return match(o) && (density == 0 || density == o.density);
}

View File

@@ -15,17 +15,18 @@
*/
#include "ConfigDescription.h"
#include <string>
#include "SdkConstants.h"
#include "test/Test.h"
#include "util/StringPiece.h"
#include <string>
namespace aapt {
static ::testing::AssertionResult TestParse(
const StringPiece& input, ConfigDescription* config = nullptr) {
if (ConfigDescription::parse(input, config)) {
if (ConfigDescription::Parse(input, config)) {
return ::testing::AssertionSuccess() << input << " was successfully parsed";
}
return ::testing::AssertionFailure() << input << " could not be parsed";

View File

@@ -15,10 +15,6 @@
*/
#include "Debug.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
#include <algorithm>
#include <iostream>
@@ -28,18 +24,25 @@
#include <set>
#include <vector>
#include "android-base/logging.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
namespace aapt {
class PrintVisitor : public ValueVisitor {
public:
using ValueVisitor::visit;
using ValueVisitor::Visit;
void visit(Attribute* attr) override {
void Visit(Attribute* attr) override {
std::cout << "(attr) type=";
attr->printMask(&std::cout);
attr->PrintMask(&std::cout);
static constexpr uint32_t kMask =
android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
if (attr->typeMask & kMask) {
if (attr->type_mask & kMask) {
for (const auto& symbol : attr->symbols) {
std::cout << "\n " << symbol.symbol.name.value().entry;
if (symbol.symbol.id) {
@@ -50,20 +53,20 @@ class PrintVisitor : public ValueVisitor {
}
}
void visit(Style* style) override {
void Visit(Style* style) override {
std::cout << "(style)";
if (style->parent) {
const Reference& parentRef = style->parent.value();
const Reference& parent_ref = style->parent.value();
std::cout << " parent=";
if (parentRef.name) {
if (parentRef.privateReference) {
if (parent_ref.name) {
if (parent_ref.private_reference) {
std::cout << "*";
}
std::cout << parentRef.name.value() << " ";
std::cout << parent_ref.name.value() << " ";
}
if (parentRef.id) {
std::cout << parentRef.id.value();
if (parent_ref.id) {
std::cout << parent_ref.id.value();
}
}
@@ -85,11 +88,11 @@ class PrintVisitor : public ValueVisitor {
}
}
void visit(Array* array) override { array->print(&std::cout); }
void Visit(Array* array) override { array->Print(&std::cout); }
void visit(Plural* plural) override { plural->print(&std::cout); }
void Visit(Plural* plural) override { plural->Print(&std::cout); }
void visit(Styleable* styleable) override {
void Visit(Styleable* styleable) override {
std::cout << "(styleable)";
for (const auto& attr : styleable->entries) {
std::cout << "\n ";
@@ -107,10 +110,10 @@ class PrintVisitor : public ValueVisitor {
}
}
void visitItem(Item* item) override { item->print(&std::cout); }
void VisitItem(Item* item) override { item->Print(&std::cout); }
};
void Debug::printTable(ResourceTable* table,
void Debug::PrintTable(ResourceTable* table,
const DebugPrintTableOptions& options) {
PrintVisitor visitor;
@@ -128,10 +131,10 @@ void Debug::printTable(ResourceTable* table,
}
std::cout << " entryCount=" << type->entries.size() << std::endl;
std::vector<const ResourceEntry*> sortedEntries;
std::vector<const ResourceEntry*> sorted_entries;
for (const auto& entry : type->entries) {
auto iter = std::lower_bound(
sortedEntries.begin(), sortedEntries.end(), entry.get(),
sorted_entries.begin(), sorted_entries.end(), entry.get(),
[](const ResourceEntry* a, const ResourceEntry* b) -> bool {
if (a->id && b->id) {
return a->id.value() < b->id.value();
@@ -141,17 +144,17 @@ void Debug::printTable(ResourceTable* table,
return false;
}
});
sortedEntries.insert(iter, entry.get());
sorted_entries.insert(iter, entry.get());
}
for (const ResourceEntry* entry : sortedEntries) {
for (const ResourceEntry* entry : sorted_entries) {
ResourceId id(package->id ? package->id.value() : uint8_t(0),
type->id ? type->id.value() : uint8_t(0),
entry->id ? entry->id.value() : uint16_t(0));
ResourceName name(package->name, type->type, entry->name);
std::cout << " spec resource " << id << " " << name;
switch (entry->symbolStatus.state) {
switch (entry->symbol_status.state) {
case SymbolState::kPublic:
std::cout << " PUBLIC";
break;
@@ -166,9 +169,9 @@ void Debug::printTable(ResourceTable* table,
for (const auto& value : entry->values) {
std::cout << " (" << value->config << ") ";
value->value->accept(&visitor);
if (options.showSources && !value->value->getSource().path.empty()) {
std::cout << " src=" << value->value->getSource();
value->value->Accept(&visitor);
if (options.show_sources && !value->value->GetSource().path.empty()) {
std::cout << " src=" << value->value->GetSource();
}
std::cout << std::endl;
}
@@ -177,35 +180,36 @@ void Debug::printTable(ResourceTable* table,
}
}
static size_t getNodeIndex(const std::vector<ResourceName>& names,
static size_t GetNodeIndex(const std::vector<ResourceName>& names,
const ResourceName& name) {
auto iter = std::lower_bound(names.begin(), names.end(), name);
assert(iter != names.end() && *iter == name);
CHECK(iter != names.end());
CHECK(*iter == name);
return std::distance(names.begin(), iter);
}
void Debug::printStyleGraph(ResourceTable* table,
const ResourceName& targetStyle) {
void Debug::PrintStyleGraph(ResourceTable* table,
const ResourceName& target_style) {
std::map<ResourceName, std::set<ResourceName>> graph;
std::queue<ResourceName> stylesToVisit;
stylesToVisit.push(targetStyle);
for (; !stylesToVisit.empty(); stylesToVisit.pop()) {
const ResourceName& styleName = stylesToVisit.front();
std::set<ResourceName>& parents = graph[styleName];
std::queue<ResourceName> styles_to_visit;
styles_to_visit.push(target_style);
for (; !styles_to_visit.empty(); styles_to_visit.pop()) {
const ResourceName& style_name = styles_to_visit.front();
std::set<ResourceName>& parents = graph[style_name];
if (!parents.empty()) {
// We've already visited this style.
continue;
}
Maybe<ResourceTable::SearchResult> result = table->findResource(styleName);
Maybe<ResourceTable::SearchResult> result = table->FindResource(style_name);
if (result) {
ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
if (Style* style = valueCast<Style>(value->value.get())) {
if (Style* style = ValueCast<Style>(value->value.get())) {
if (style->parent && style->parent.value().name) {
parents.insert(style->parent.value().name.value());
stylesToVisit.push(style->parent.value().name.value());
styles_to_visit.push(style->parent.value().name.value());
}
}
}
@@ -219,24 +223,24 @@ void Debug::printStyleGraph(ResourceTable* table,
std::cout << "digraph styles {\n";
for (const auto& name : names) {
std::cout << " node_" << getNodeIndex(names, name) << " [label=\"" << name
std::cout << " node_" << GetNodeIndex(names, name) << " [label=\"" << name
<< "\"];\n";
}
for (const auto& entry : graph) {
const ResourceName& styleName = entry.first;
size_t styleNodeIndex = getNodeIndex(names, styleName);
const ResourceName& style_name = entry.first;
size_t style_node_index = GetNodeIndex(names, style_name);
for (const auto& parentName : entry.second) {
std::cout << " node_" << styleNodeIndex << " -> "
<< "node_" << getNodeIndex(names, parentName) << ";\n";
for (const auto& parent_name : entry.second) {
std::cout << " node_" << style_node_index << " -> "
<< "node_" << GetNodeIndex(names, parent_name) << ";\n";
}
}
std::cout << "}" << std::endl;
}
void Debug::dumpHex(const void* data, size_t len) {
void Debug::DumpHex(const void* data, size_t len) {
const uint8_t* d = (const uint8_t*)data;
for (size_t i = 0; i < len; i++) {
std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i]
@@ -255,55 +259,55 @@ namespace {
class XmlPrinter : public xml::Visitor {
public:
using xml::Visitor::visit;
using xml::Visitor::Visit;
void visit(xml::Element* el) override {
std::cerr << mPrefix;
void Visit(xml::Element* el) override {
std::cerr << prefix_;
std::cerr << "E: ";
if (!el->namespaceUri.empty()) {
std::cerr << el->namespaceUri << ":";
if (!el->namespace_uri.empty()) {
std::cerr << el->namespace_uri << ":";
}
std::cerr << el->name << " (line=" << el->lineNumber << ")\n";
std::cerr << el->name << " (line=" << el->line_number << ")\n";
for (const xml::Attribute& attr : el->attributes) {
std::cerr << mPrefix << " A: ";
if (!attr.namespaceUri.empty()) {
std::cerr << attr.namespaceUri << ":";
std::cerr << prefix_ << " A: ";
if (!attr.namespace_uri.empty()) {
std::cerr << attr.namespace_uri << ":";
}
std::cerr << attr.name << "=" << attr.value << "\n";
}
const size_t previousSize = mPrefix.size();
mPrefix += " ";
xml::Visitor::visit(el);
mPrefix.resize(previousSize);
const size_t previous_size = prefix_.size();
prefix_ += " ";
xml::Visitor::Visit(el);
prefix_.resize(previous_size);
}
void visit(xml::Namespace* ns) override {
std::cerr << mPrefix;
std::cerr << "N: " << ns->namespacePrefix << "=" << ns->namespaceUri
<< " (line=" << ns->lineNumber << ")\n";
void Visit(xml::Namespace* ns) override {
std::cerr << prefix_;
std::cerr << "N: " << ns->namespace_prefix << "=" << ns->namespace_uri
<< " (line=" << ns->line_number << ")\n";
const size_t previousSize = mPrefix.size();
mPrefix += " ";
xml::Visitor::visit(ns);
mPrefix.resize(previousSize);
const size_t previous_size = prefix_.size();
prefix_ += " ";
xml::Visitor::Visit(ns);
prefix_.resize(previous_size);
}
void visit(xml::Text* text) override {
std::cerr << mPrefix;
void Visit(xml::Text* text) override {
std::cerr << prefix_;
std::cerr << "T: '" << text->text << "'\n";
}
private:
std::string mPrefix;
std::string prefix_;
};
} // namespace
void Debug::dumpXml(xml::XmlResource* doc) {
void Debug::DumpXml(xml::XmlResource* doc) {
XmlPrinter printer;
doc->root->accept(&printer);
doc->root->Accept(&printer);
}
} // namespace aapt

View File

@@ -17,26 +17,26 @@
#ifndef AAPT_DEBUG_H
#define AAPT_DEBUG_H
// Include for printf-like debugging.
#include <iostream>
#include "Resource.h"
#include "ResourceTable.h"
#include "xml/XmlDom.h"
// Include for printf-like debugging.
#include <iostream>
namespace aapt {
struct DebugPrintTableOptions {
bool showSources = false;
bool show_sources = false;
};
struct Debug {
static void printTable(ResourceTable* table,
static void PrintTable(ResourceTable* table,
const DebugPrintTableOptions& options = {});
static void printStyleGraph(ResourceTable* table,
const ResourceName& targetStyle);
static void dumpHex(const void* data, size_t len);
static void dumpXml(xml::XmlResource* doc);
static void PrintStyleGraph(ResourceTable* table,
const ResourceName& target_style);
static void DumpHex(const void* data, size_t len);
static void DumpXml(xml::XmlResource* doc);
};
} // namespace aapt

View File

@@ -17,15 +17,16 @@
#ifndef AAPT_DIAGNOSTICS_H
#define AAPT_DIAGNOSTICS_H
#include "Source.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <android-base/macros.h>
#include <iostream>
#include <sstream>
#include <string>
#include "android-base/macros.h"
#include "Source.h"
#include "util/StringPiece.h"
#include "util/Util.h"
namespace aapt {
struct DiagMessageActual {
@@ -34,28 +35,28 @@ struct DiagMessageActual {
};
struct DiagMessage {
private:
Source mSource;
std::stringstream mMessage;
public:
DiagMessage() = default;
explicit DiagMessage(const StringPiece& src) : mSource(src) {}
explicit DiagMessage(const StringPiece& src) : source_(src) {}
explicit DiagMessage(const Source& src) : mSource(src) {}
explicit DiagMessage(const Source& src) : source_(src) {}
explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {}
explicit DiagMessage(size_t line) : source_(Source().WithLine(line)) {}
template <typename T>
DiagMessage& operator<<(const T& value) {
mMessage << value;
message_ << value;
return *this;
}
DiagMessageActual build() const {
return DiagMessageActual{mSource, mMessage.str()};
DiagMessageActual Build() const {
return DiagMessageActual{source_, message_.str()};
}
private:
Source source_;
std::stringstream message_;
};
struct IDiagnostics {
@@ -63,21 +64,21 @@ struct IDiagnostics {
enum class Level { Note, Warn, Error };
virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
virtual void Log(Level level, DiagMessageActual& actualMsg) = 0;
virtual void error(const DiagMessage& message) {
DiagMessageActual actual = message.build();
log(Level::Error, actual);
virtual void Error(const DiagMessage& message) {
DiagMessageActual actual = message.Build();
Log(Level::Error, actual);
}
virtual void warn(const DiagMessage& message) {
DiagMessageActual actual = message.build();
log(Level::Warn, actual);
virtual void Warn(const DiagMessage& message) {
DiagMessageActual actual = message.Build();
Log(Level::Warn, actual);
}
virtual void note(const DiagMessage& message) {
DiagMessageActual actual = message.build();
log(Level::Note, actual);
virtual void Note(const DiagMessage& message) {
DiagMessageActual actual = message.Build();
Log(Level::Note, actual);
}
};
@@ -85,13 +86,13 @@ class StdErrDiagnostics : public IDiagnostics {
public:
StdErrDiagnostics() = default;
void log(Level level, DiagMessageActual& actualMsg) override {
void Log(Level level, DiagMessageActual& actual_msg) override {
const char* tag;
switch (level) {
case Level::Error:
mNumErrors++;
if (mNumErrors > 20) {
num_errors_++;
if (num_errors_ > 20) {
return;
}
tag = "error";
@@ -106,14 +107,14 @@ class StdErrDiagnostics : public IDiagnostics {
break;
}
if (!actualMsg.source.path.empty()) {
std::cerr << actualMsg.source << ": ";
if (!actual_msg.source.path.empty()) {
std::cerr << actual_msg.source << ": ";
}
std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
std::cerr << tag << ": " << actual_msg.message << "." << std::endl;
}
private:
size_t mNumErrors = 0;
size_t num_errors_ = 0;
DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
};
@@ -121,16 +122,16 @@ class StdErrDiagnostics : public IDiagnostics {
class SourcePathDiagnostics : public IDiagnostics {
public:
SourcePathDiagnostics(const Source& src, IDiagnostics* diag)
: mSource(src), mDiag(diag) {}
: source_(src), diag_(diag) {}
void log(Level level, DiagMessageActual& actualMsg) override {
actualMsg.source.path = mSource.path;
mDiag->log(level, actualMsg);
void Log(Level level, DiagMessageActual& actual_msg) override {
actual_msg.source.path = source_.path;
diag_->Log(level, actual_msg);
}
private:
Source mSource;
IDiagnostics* mDiag;
Source source_;
IDiagnostics* diag_;
DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};

View File

@@ -15,77 +15,80 @@
*/
#include "DominatorTree.h"
#include "ConfigDescription.h"
#include <algorithm>
#include "android-base/logging.h"
#include "ConfigDescription.h"
namespace aapt {
DominatorTree::DominatorTree(
const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
for (const auto& config : configs) {
mProductRoots[config->product].tryAddChild(
product_roots_[config->product].TryAddChild(
util::make_unique<Node>(config.get(), nullptr));
}
}
void DominatorTree::accept(Visitor* visitor) {
for (auto& entry : mProductRoots) {
visitor->visitTree(entry.first, &entry.second);
void DominatorTree::Accept(Visitor* visitor) {
for (auto& entry : product_roots_) {
visitor->VisitTree(entry.first, &entry.second);
}
}
bool DominatorTree::Node::tryAddChild(std::unique_ptr<Node> newChild) {
assert(newChild->mValue && "cannot add a root or empty node as a child");
if (mValue && !dominates(newChild.get())) {
bool DominatorTree::Node::TryAddChild(std::unique_ptr<Node> new_child) {
CHECK(new_child->value_) << "cannot add a root or empty node as a child";
if (value_ && !Dominates(new_child.get())) {
// This is not the root and the child dominates us.
return false;
}
return addChild(std::move(newChild));
return AddChild(std::move(new_child));
}
bool DominatorTree::Node::addChild(std::unique_ptr<Node> newChild) {
bool hasDominatedChildren = false;
bool DominatorTree::Node::AddChild(std::unique_ptr<Node> new_child) {
bool has_dominated_children = false;
// Demote children dominated by the new config.
for (auto& child : mChildren) {
if (newChild->dominates(child.get())) {
child->mParent = newChild.get();
newChild->mChildren.push_back(std::move(child));
for (auto& child : children_) {
if (new_child->Dominates(child.get())) {
child->parent_ = new_child.get();
new_child->children_.push_back(std::move(child));
child = {};
hasDominatedChildren = true;
has_dominated_children = true;
}
}
// Remove dominated children.
if (hasDominatedChildren) {
mChildren.erase(
std::remove_if(mChildren.begin(), mChildren.end(),
if (has_dominated_children) {
children_.erase(
std::remove_if(children_.begin(), children_.end(),
[](const std::unique_ptr<Node>& child) -> bool {
return child == nullptr;
}),
mChildren.end());
children_.end());
}
// Add the new config to a child if a child dominates the new config.
for (auto& child : mChildren) {
if (child->dominates(newChild.get())) {
child->addChild(std::move(newChild));
for (auto& child : children_) {
if (child->Dominates(new_child.get())) {
child->AddChild(std::move(new_child));
return true;
}
}
// The new config is not dominated by a child, so add it here.
newChild->mParent = this;
mChildren.push_back(std::move(newChild));
new_child->parent_ = this;
children_.push_back(std::move(new_child));
return true;
}
bool DominatorTree::Node::dominates(const Node* other) const {
bool DominatorTree::Node::Dominates(const Node* other) const {
// Check root node dominations.
if (other->isRootNode()) {
return isRootNode();
} else if (isRootNode()) {
if (other->is_root_node()) {
return is_root_node();
} else if (is_root_node()) {
return true;
}
// Neither node is a root node; compare the configurations.
return mValue->config.dominates(other->mValue->config);
return value_->config.Dominates(other->value_->config);
}
} // namespace aapt

View File

@@ -17,13 +17,13 @@
#ifndef AAPT_DOMINATOR_TREE_H
#define AAPT_DOMINATOR_TREE_H
#include "ResourceTable.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "ResourceTable.h"
namespace aapt {
/**
@@ -53,67 +53,67 @@ class DominatorTree {
class Node {
public:
explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr)
: mValue(value), mParent(parent) {}
: value_(value), parent_(parent) {}
inline ResourceConfigValue* value() const { return mValue; }
inline ResourceConfigValue* value() const { return value_; }
inline Node* parent() const { return mParent; }
inline Node* parent() const { return parent_; }
inline bool isRootNode() const { return !mValue; }
inline bool is_root_node() const { return !value_; }
inline const std::vector<std::unique_ptr<Node>>& children() const {
return mChildren;
return children_;
}
bool tryAddChild(std::unique_ptr<Node> newChild);
bool TryAddChild(std::unique_ptr<Node> new_child);
private:
bool addChild(std::unique_ptr<Node> newChild);
bool dominates(const Node* other) const;
bool AddChild(std::unique_ptr<Node> new_child);
bool Dominates(const Node* other) const;
ResourceConfigValue* mValue;
Node* mParent;
std::vector<std::unique_ptr<Node>> mChildren;
ResourceConfigValue* value_;
Node* parent_;
std::vector<std::unique_ptr<Node>> children_;
DISALLOW_COPY_AND_ASSIGN(Node);
};
struct Visitor {
virtual ~Visitor() = default;
virtual void visitTree(const std::string& product, Node* root) = 0;
virtual void VisitTree(const std::string& product, Node* root) = 0;
};
class BottomUpVisitor : public Visitor {
public:
virtual ~BottomUpVisitor() = default;
void visitTree(const std::string& product, Node* root) override {
void VisitTree(const std::string& product, Node* root) override {
for (auto& child : root->children()) {
visitNode(child.get());
VisitNode(child.get());
}
}
virtual void visitConfig(Node* node) = 0;
virtual void VisitConfig(Node* node) = 0;
private:
void visitNode(Node* node) {
void VisitNode(Node* node) {
for (auto& child : node->children()) {
visitNode(child.get());
VisitNode(child.get());
}
visitConfig(node);
VisitConfig(node);
}
};
void accept(Visitor* visitor);
void Accept(Visitor* visitor);
inline const std::map<std::string, Node>& getProductRoots() const {
return mProductRoots;
inline const std::map<std::string, Node>& product_roots() const {
return product_roots_;
}
private:
DISALLOW_COPY_AND_ASSIGN(DominatorTree);
std::map<std::string, Node> mProductRoots;
std::map<std::string, Node> product_roots_;
};
} // namespace aapt

View File

@@ -15,67 +15,68 @@
*/
#include "DominatorTree.h"
#include "test/Test.h"
#include "util/Util.h"
#include <sstream>
#include <string>
#include <vector>
#include "test/Test.h"
#include "util/Util.h"
namespace aapt {
namespace {
class PrettyPrinter : public DominatorTree::Visitor {
public:
explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {}
explicit PrettyPrinter(const int indent = 2) : indent_(indent) {}
void visitTree(const std::string& product,
void VisitTree(const std::string& product,
DominatorTree::Node* root) override {
for (auto& child : root->children()) {
visitNode(child.get(), 0);
VisitNode(child.get(), 0);
}
}
std::string toString(DominatorTree* tree) {
mBuffer.str("");
mBuffer.clear();
tree->accept(this);
return mBuffer.str();
std::string ToString(DominatorTree* tree) {
buffer_.str("");
buffer_.clear();
tree->Accept(this);
return buffer_.str();
}
private:
void visitConfig(const DominatorTree::Node* node, const int indent) {
auto configString = node->value()->config.toString();
mBuffer << std::string(indent, ' ')
<< (configString.isEmpty() ? "<default>" : configString)
void VisitConfig(const DominatorTree::Node* node, const int indent) {
auto config_string = node->value()->config.toString();
buffer_ << std::string(indent, ' ')
<< (config_string.isEmpty() ? "<default>" : config_string)
<< std::endl;
}
void visitNode(const DominatorTree::Node* node, const int indent) {
visitConfig(node, indent);
void VisitNode(const DominatorTree::Node* node, const int indent) {
VisitConfig(node, indent);
for (const auto& child : node->children()) {
visitNode(child.get(), indent + mIndent);
VisitNode(child.get(), indent + indent_);
}
}
std::stringstream mBuffer;
const int mIndent = 2;
std::stringstream buffer_;
const int indent_ = 2;
};
} // namespace
TEST(DominatorTreeTest, DefaultDominatesEverything) {
const ConfigDescription defaultConfig = {};
const ConfigDescription landConfig = test::parseConfigOrDie("land");
const ConfigDescription sw600dpLandConfig =
test::parseConfigOrDie("sw600dp-land-v13");
const ConfigDescription default_config = {};
const ConfigDescription land_config = test::ParseConfigOrDie("land");
const ConfigDescription sw600dp_land_config =
test::ParseConfigOrDie("sw600dp-land-v13");
std::vector<std::unique_ptr<ResourceConfigValue>> configs;
configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(land_config, ""));
configs.push_back(
util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
util::make_unique<ResourceConfigValue>(sw600dp_land_config, ""));
DominatorTree tree(configs);
PrettyPrinter printer;
@@ -84,22 +85,22 @@ TEST(DominatorTreeTest, DefaultDominatesEverything) {
"<default>\n"
" land\n"
" sw600dp-land-v13\n";
EXPECT_EQ(expected, printer.toString(&tree));
EXPECT_EQ(expected, printer.ToString(&tree));
}
TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
const ConfigDescription defaultConfig = {};
const ConfigDescription landConfig = test::parseConfigOrDie("land");
const ConfigDescription sw600dpLandConfig =
test::parseConfigOrDie("sw600dp-land-v13");
const ConfigDescription default_config = {};
const ConfigDescription land_config = test::ParseConfigOrDie("land");
const ConfigDescription sw600dp_land_config =
test::ParseConfigOrDie("sw600dp-land-v13");
std::vector<std::unique_ptr<ResourceConfigValue>> configs;
configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(land_config, ""));
configs.push_back(
util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
util::make_unique<ResourceConfigValue>(default_config, "phablet"));
configs.push_back(
util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
util::make_unique<ResourceConfigValue>(sw600dp_land_config, "phablet"));
DominatorTree tree(configs);
PrettyPrinter printer;
@@ -109,34 +110,38 @@ TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
" land\n"
"<default>\n"
" sw600dp-land-v13\n";
EXPECT_EQ(expected, printer.toString(&tree));
EXPECT_EQ(expected, printer.ToString(&tree));
}
TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
const ConfigDescription defaultConfig = {};
const ConfigDescription enConfig = test::parseConfigOrDie("en");
const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
const ConfigDescription ldrtlXhdpiConfig =
test::parseConfigOrDie("ldrtl-xhdpi-v4");
const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
const ConfigDescription v20Config = test::parseConfigOrDie("v20");
const ConfigDescription default_config = {};
const ConfigDescription en_config = test::ParseConfigOrDie("en");
const ConfigDescription en_v21_config = test::ParseConfigOrDie("en-v21");
const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl-v4");
const ConfigDescription ldrtl_xhdpi_config =
test::ParseConfigOrDie("ldrtl-xhdpi-v4");
const ConfigDescription sw300dp_config =
test::ParseConfigOrDie("sw300dp-v13");
const ConfigDescription sw540dp_config =
test::ParseConfigOrDie("sw540dp-v14");
const ConfigDescription sw600dp_config =
test::ParseConfigOrDie("sw600dp-v14");
const ConfigDescription sw720dp_config =
test::ParseConfigOrDie("sw720dp-v13");
const ConfigDescription v20_config = test::ParseConfigOrDie("v20");
std::vector<std::unique_ptr<ResourceConfigValue>> configs;
configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(en_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(en_v21_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(ldrtl_config, ""));
configs.push_back(
util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
util::make_unique<ResourceConfigValue>(ldrtl_xhdpi_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw300dp_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw540dp_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw600dp_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(sw720dp_config, ""));
configs.push_back(util::make_unique<ResourceConfigValue>(v20_config, ""));
DominatorTree tree(configs);
PrettyPrinter printer;
@@ -152,7 +157,7 @@ TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
" sw600dp-v14\n"
" sw720dp-v13\n"
" v20\n";
EXPECT_EQ(expected, printer.toString(&tree));
EXPECT_EQ(expected, printer.ToString(&tree));
}
} // namespace aapt

View File

@@ -15,97 +15,98 @@
*/
#include "Flags.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "util/StringPiece.h"
#include "util/Util.h"
namespace aapt {
Flags& Flags::requiredFlag(const StringPiece& name,
Flags& Flags::RequiredFlag(const StringPiece& name,
const StringPiece& description, std::string* value) {
auto func = [value](const StringPiece& arg) -> bool {
*value = arg.toString();
*value = arg.ToString();
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, true, 1, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, true, 1, false});
return *this;
}
Flags& Flags::requiredFlagList(const StringPiece& name,
Flags& Flags::RequiredFlagList(const StringPiece& name,
const StringPiece& description,
std::vector<std::string>* value) {
auto func = [value](const StringPiece& arg) -> bool {
value->push_back(arg.toString());
value->push_back(arg.ToString());
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, true, 1, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, true, 1, false});
return *this;
}
Flags& Flags::optionalFlag(const StringPiece& name,
Flags& Flags::OptionalFlag(const StringPiece& name,
const StringPiece& description,
Maybe<std::string>* value) {
auto func = [value](const StringPiece& arg) -> bool {
*value = arg.toString();
*value = arg.ToString();
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, false, 1, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, false, 1, false});
return *this;
}
Flags& Flags::optionalFlagList(const StringPiece& name,
Flags& Flags::OptionalFlagList(const StringPiece& name,
const StringPiece& description,
std::vector<std::string>* value) {
auto func = [value](const StringPiece& arg) -> bool {
value->push_back(arg.toString());
value->push_back(arg.ToString());
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, false, 1, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, false, 1, false});
return *this;
}
Flags& Flags::optionalFlagList(const StringPiece& name,
Flags& Flags::OptionalFlagList(const StringPiece& name,
const StringPiece& description,
std::unordered_set<std::string>* value) {
auto func = [value](const StringPiece& arg) -> bool {
value->insert(arg.toString());
value->insert(arg.ToString());
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, false, 1, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, false, 1, false});
return *this;
}
Flags& Flags::optionalSwitch(const StringPiece& name,
Flags& Flags::OptionalSwitch(const StringPiece& name,
const StringPiece& description, bool* value) {
auto func = [value](const StringPiece& arg) -> bool {
*value = true;
return true;
};
mFlags.push_back(
Flag{name.toString(), description.toString(), func, false, 0, false});
flags_.push_back(
Flag{name.ToString(), description.ToString(), func, false, 0, false});
return *this;
}
void Flags::usage(const StringPiece& command, std::ostream* out) {
void Flags::Usage(const StringPiece& command, std::ostream* out) {
constexpr size_t kWidth = 50;
*out << command << " [options]";
for (const Flag& flag : mFlags) {
for (const Flag& flag : flags_) {
if (flag.required) {
*out << " " << flag.name << " arg";
}
@@ -113,10 +114,10 @@ void Flags::usage(const StringPiece& command, std::ostream* out) {
*out << " files...\n\nOptions:\n";
for (const Flag& flag : mFlags) {
std::string argLine = flag.name;
if (flag.numArgs > 0) {
argLine += " arg";
for (const Flag& flag : flags_) {
std::string argline = flag.name;
if (flag.num_args > 0) {
argline += " arg";
}
// Split the description by newlines and write out the argument (which is
@@ -124,9 +125,9 @@ void Flags::usage(const StringPiece& command, std::ostream* out) {
// the first line) followed by the description line. This will make sure
// that multiline
// descriptions are still right justified and aligned.
for (StringPiece line : util::tokenize(flag.description, '\n')) {
*out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
argLine = " ";
for (StringPiece line : util::Tokenize(flag.description, '\n')) {
*out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
argline = " ";
}
}
*out << " " << std::setw(kWidth) << std::left << "-h"
@@ -134,29 +135,29 @@ void Flags::usage(const StringPiece& command, std::ostream* out) {
out->flush();
}
bool Flags::parse(const StringPiece& command,
bool Flags::Parse(const StringPiece& command,
const std::vector<StringPiece>& args,
std::ostream* outError) {
std::ostream* out_error) {
for (size_t i = 0; i < args.size(); i++) {
StringPiece arg = args[i];
if (*(arg.data()) != '-') {
mArgs.push_back(arg.toString());
args_.push_back(arg.ToString());
continue;
}
if (arg == "-h" || arg == "--help") {
usage(command, outError);
Usage(command, out_error);
return false;
}
bool match = false;
for (Flag& flag : mFlags) {
for (Flag& flag : flags_) {
if (arg == flag.name) {
if (flag.numArgs > 0) {
if (flag.num_args > 0) {
i++;
if (i >= args.size()) {
*outError << flag.name << " missing argument.\n\n";
usage(command, outError);
*out_error << flag.name << " missing argument.\n\n";
Usage(command, out_error);
return false;
}
flag.action(args[i]);
@@ -170,22 +171,22 @@ bool Flags::parse(const StringPiece& command,
}
if (!match) {
*outError << "unknown option '" << arg << "'.\n\n";
usage(command, outError);
*out_error << "unknown option '" << arg << "'.\n\n";
Usage(command, out_error);
return false;
}
}
for (const Flag& flag : mFlags) {
for (const Flag& flag : flags_) {
if (flag.required && !flag.parsed) {
*outError << "missing required flag " << flag.name << "\n\n";
usage(command, outError);
*out_error << "missing required flag " << flag.name << "\n\n";
Usage(command, out_error);
return false;
}
}
return true;
}
const std::vector<std::string>& Flags::getArgs() { return mArgs; }
const std::vector<std::string>& Flags::GetArgs() { return args_; }
} // namespace aapt

View File

@@ -17,41 +17,41 @@
#ifndef AAPT_FLAGS_H
#define AAPT_FLAGS_H
#include "util/Maybe.h"
#include "util/StringPiece.h"
#include <functional>
#include <ostream>
#include <string>
#include <unordered_set>
#include <vector>
#include "util/Maybe.h"
#include "util/StringPiece.h"
namespace aapt {
class Flags {
public:
Flags& requiredFlag(const StringPiece& name, const StringPiece& description,
Flags& RequiredFlag(const StringPiece& name, const StringPiece& description,
std::string* value);
Flags& requiredFlagList(const StringPiece& name,
Flags& RequiredFlagList(const StringPiece& name,
const StringPiece& description,
std::vector<std::string>* value);
Flags& optionalFlag(const StringPiece& name, const StringPiece& description,
Flags& OptionalFlag(const StringPiece& name, const StringPiece& description,
Maybe<std::string>* value);
Flags& optionalFlagList(const StringPiece& name,
Flags& OptionalFlagList(const StringPiece& name,
const StringPiece& description,
std::vector<std::string>* value);
Flags& optionalFlagList(const StringPiece& name,
Flags& OptionalFlagList(const StringPiece& name,
const StringPiece& description,
std::unordered_set<std::string>* value);
Flags& optionalSwitch(const StringPiece& name, const StringPiece& description,
Flags& OptionalSwitch(const StringPiece& name, const StringPiece& description,
bool* value);
void usage(const StringPiece& command, std::ostream* out);
void Usage(const StringPiece& command, std::ostream* out);
bool parse(const StringPiece& command, const std::vector<StringPiece>& args,
bool Parse(const StringPiece& command, const std::vector<StringPiece>& args,
std::ostream* outError);
const std::vector<std::string>& getArgs();
const std::vector<std::string>& GetArgs();
private:
struct Flag {
@@ -59,13 +59,13 @@ class Flags {
std::string description;
std::function<bool(const StringPiece& value)> action;
bool required;
size_t numArgs;
size_t num_args;
bool parsed;
};
std::vector<Flag> mFlags;
std::vector<std::string> mArgs;
std::vector<Flag> flags_;
std::vector<std::string> args_;
};
} // namespace aapt

View File

@@ -15,174 +15,176 @@
*/
#include "Locale.h"
#include "util/Util.h"
#include <ctype.h>
#include <algorithm>
#include <string>
#include <vector>
#include "util/Util.h"
namespace aapt {
using android::ResTable_config;
void LocaleValue::setLanguage(const char* languageChars) {
void LocaleValue::set_language(const char* language_chars) {
size_t i = 0;
while ((*languageChars) != '\0') {
language[i++] = ::tolower(*languageChars);
languageChars++;
while ((*language_chars) != '\0') {
language[i++] = ::tolower(*language_chars);
language_chars++;
}
}
void LocaleValue::setRegion(const char* regionChars) {
void LocaleValue::set_region(const char* region_chars) {
size_t i = 0;
while ((*regionChars) != '\0') {
region[i++] = ::toupper(*regionChars);
regionChars++;
while ((*region_chars) != '\0') {
region[i++] = ::toupper(*region_chars);
region_chars++;
}
}
void LocaleValue::setScript(const char* scriptChars) {
void LocaleValue::set_script(const char* script_chars) {
size_t i = 0;
while ((*scriptChars) != '\0') {
while ((*script_chars) != '\0') {
if (i == 0) {
script[i++] = ::toupper(*scriptChars);
script[i++] = ::toupper(*script_chars);
} else {
script[i++] = ::tolower(*scriptChars);
script[i++] = ::tolower(*script_chars);
}
scriptChars++;
script_chars++;
}
}
void LocaleValue::setVariant(const char* variantChars) {
void LocaleValue::set_variant(const char* variant_chars) {
size_t i = 0;
while ((*variantChars) != '\0') {
variant[i++] = *variantChars;
variantChars++;
while ((*variant_chars) != '\0') {
variant[i++] = *variant_chars;
variant_chars++;
}
}
static inline bool isAlpha(const std::string& str) {
static inline bool is_alpha(const std::string& str) {
return std::all_of(std::begin(str), std::end(str), ::isalpha);
}
static inline bool isNumber(const std::string& str) {
static inline bool is_number(const std::string& str) {
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
bool LocaleValue::initFromFilterString(const StringPiece& str) {
bool LocaleValue::InitFromFilterString(const StringPiece& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
std::vector<std::string> parts = util::splitAndLowercase(str, '_');
std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
const int numTags = parts.size();
const int num_tags = parts.size();
bool valid = false;
if (numTags >= 1) {
if (num_tags >= 1) {
const std::string& lang = parts[0];
if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
setLanguage(lang.c_str());
if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
set_language(lang.c_str());
valid = true;
}
}
if (!valid || numTags == 1) {
if (!valid || num_tags == 1) {
return valid;
}
// At this point, valid == true && numTags > 1.
const std::string& part2 = parts[1];
if ((part2.length() == 2 && isAlpha(part2)) ||
(part2.length() == 3 && isNumber(part2))) {
setRegion(part2.c_str());
} else if (part2.length() == 4 && isAlpha(part2)) {
setScript(part2.c_str());
if ((part2.length() == 2 && is_alpha(part2)) ||
(part2.length() == 3 && is_number(part2))) {
set_region(part2.c_str());
} else if (part2.length() == 4 && is_alpha(part2)) {
set_script(part2.c_str());
} else if (part2.length() >= 4 && part2.length() <= 8) {
setVariant(part2.c_str());
set_variant(part2.c_str());
} else {
valid = false;
}
if (!valid || numTags == 2) {
if (!valid || num_tags == 2) {
return valid;
}
// At this point, valid == true && numTags > 1.
const std::string& part3 = parts[2];
if (((part3.length() == 2 && isAlpha(part3)) ||
(part3.length() == 3 && isNumber(part3))) &&
if (((part3.length() == 2 && is_alpha(part3)) ||
(part3.length() == 3 && is_number(part3))) &&
script[0]) {
setRegion(part3.c_str());
set_region(part3.c_str());
} else if (part3.length() >= 4 && part3.length() <= 8) {
setVariant(part3.c_str());
set_variant(part3.c_str());
} else {
valid = false;
}
if (!valid || numTags == 3) {
if (!valid || num_tags == 3) {
return valid;
}
const std::string& part4 = parts[3];
if (part4.length() >= 4 && part4.length() <= 8) {
setVariant(part4.c_str());
set_variant(part4.c_str());
} else {
valid = false;
}
if (!valid || numTags > 4) {
if (!valid || num_tags > 4) {
return false;
}
return true;
}
ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
std::vector<std::string>::iterator end) {
const std::vector<std::string>::iterator startIter = iter;
const std::vector<std::string>::iterator start_iter = iter;
std::string& part = *iter;
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
// except that the separator is "+" and not "-".
std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
subtags.erase(subtags.begin());
if (subtags.size() == 1) {
setLanguage(subtags[0].c_str());
set_language(subtags[0].c_str());
} else if (subtags.size() == 2) {
setLanguage(subtags[0].c_str());
set_language(subtags[0].c_str());
// The second tag can either be a region, a variant or a script.
switch (subtags[1].size()) {
case 2:
case 3:
setRegion(subtags[1].c_str());
set_region(subtags[1].c_str());
break;
case 4:
if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
// This is a variant: fall through
} else {
setScript(subtags[1].c_str());
set_script(subtags[1].c_str());
break;
}
case 5:
case 6:
case 7:
case 8:
setVariant(subtags[1].c_str());
set_variant(subtags[1].c_str());
break;
default:
return -1;
}
} else if (subtags.size() == 3) {
// The language is always the first subtag.
setLanguage(subtags[0].c_str());
set_language(subtags[0].c_str());
// The second subtag can either be a script or a region code.
// If its size is 4, it's a script code, else it's a region code.
if (subtags[1].size() == 4) {
setScript(subtags[1].c_str());
set_script(subtags[1].c_str());
} else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
setRegion(subtags[1].c_str());
set_region(subtags[1].c_str());
} else {
return -1;
}
@@ -190,15 +192,15 @@ ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
// The third tag can either be a region code (if the second tag was
// a script), else a variant code.
if (subtags[2].size() >= 4) {
setVariant(subtags[2].c_str());
set_variant(subtags[2].c_str());
} else {
setRegion(subtags[2].c_str());
set_region(subtags[2].c_str());
}
} else if (subtags.size() == 4) {
setLanguage(subtags[0].c_str());
setScript(subtags[1].c_str());
setRegion(subtags[2].c_str());
setVariant(subtags[3].c_str());
set_language(subtags[0].c_str());
set_script(subtags[1].c_str());
set_region(subtags[2].c_str());
set_variant(subtags[3].c_str());
} else {
return -1;
}
@@ -206,25 +208,25 @@ ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
++iter;
} else {
if ((part.length() == 2 || part.length() == 3) && isAlpha(part) &&
if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
part != "car") {
setLanguage(part.c_str());
set_language(part.c_str());
++iter;
if (iter != end) {
const std::string& regionPart = *iter;
if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
setRegion(regionPart.c_str() + 1);
const std::string& region_part = *iter;
if (region_part.c_str()[0] == 'r' && region_part.length() == 3) {
set_region(region_part.c_str() + 1);
++iter;
}
}
}
}
return static_cast<ssize_t>(iter - startIter);
return static_cast<ssize_t>(iter - start_iter);
}
void LocaleValue::initFromResTable(const ResTable_config& config) {
void LocaleValue::InitFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
if (config.localeScript[0] && !config.localeScriptWasComputed) {
@@ -236,7 +238,7 @@ void LocaleValue::initFromResTable(const ResTable_config& config) {
}
}
void LocaleValue::writeTo(ResTable_config* out) const {
void LocaleValue::WriteTo(ResTable_config* out) const {
out->packLanguage(language);
out->packRegion(region);

View File

@@ -17,12 +17,13 @@
#ifndef AAPT_LOCALE_VALUE_H
#define AAPT_LOCALE_VALUE_H
#include "util/StringPiece.h"
#include <androidfw/ResourceTypes.h>
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "util/StringPiece.h"
namespace aapt {
/**
@@ -39,23 +40,23 @@ struct LocaleValue {
/**
* Initialize this LocaleValue from a config string.
*/
bool initFromFilterString(const StringPiece& config);
bool InitFromFilterString(const StringPiece& config);
/**
* Initialize this LocaleValue from parts of a vector.
*/
ssize_t initFromParts(std::vector<std::string>::iterator iter,
ssize_t InitFromParts(std::vector<std::string>::iterator iter,
std::vector<std::string>::iterator end);
/**
* Initialize this LocaleValue from a ResTable_config.
*/
void initFromResTable(const android::ResTable_config& config);
void InitFromResTable(const android::ResTable_config& config);
/**
* Set the locale in a ResTable_config from this LocaleValue.
*/
void writeTo(android::ResTable_config* out) const;
void WriteTo(android::ResTable_config* out) const;
inline int compare(const LocaleValue& other) const;
@@ -67,10 +68,10 @@ struct LocaleValue {
inline bool operator>(const LocaleValue& o) const;
private:
void setLanguage(const char* language);
void setRegion(const char* language);
void setScript(const char* script);
void setVariant(const char* variant);
void set_language(const char* language);
void set_region(const char* language);
void set_script(const char* script);
void set_variant(const char* variant);
};
//

View File

@@ -15,18 +15,20 @@
*/
#include "Locale.h"
#include "util/Util.h"
#include <gtest/gtest.h>
#include <string>
#include "gtest/gtest.h"
#include "util/Util.h"
namespace aapt {
static ::testing::AssertionResult TestLanguage(const char* input,
const char* lang) {
std::vector<std::string> parts = util::splitAndLowercase(input, '-');
std::vector<std::string> parts = util::SplitAndLowercase(input, '-');
LocaleValue lv;
ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts));
if (count < 0) {
return ::testing::AssertionFailure() << " failed to parse '" << input
<< "'.";
@@ -51,9 +53,9 @@ static ::testing::AssertionResult TestLanguage(const char* input,
static ::testing::AssertionResult TestLanguageRegion(const char* input,
const char* lang,
const char* region) {
std::vector<std::string> parts = util::splitAndLowercase(input, '-');
std::vector<std::string> parts = util::SplitAndLowercase(input, '-');
LocaleValue lv;
ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts));
if (count < 0) {
return ::testing::AssertionFailure() << " failed to parse '" << input
<< "'.";

View File

@@ -14,11 +14,11 @@
* limitations under the License.
*/
#include "util/StringPiece.h"
#include <iostream>
#include <vector>
#include "util/StringPiece.h"
namespace aapt {
// DO NOT UPDATE, this is more of a marketing version.
@@ -27,16 +27,16 @@ static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
static const char* sMinorVersion = "2";
int printVersion() {
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
<< sMinorVersion << std::endl;
return 0;
}
extern int compile(const std::vector<StringPiece>& args);
extern int link(const std::vector<StringPiece>& args);
extern int dump(const std::vector<StringPiece>& args);
extern int diff(const std::vector<StringPiece>& args);
extern int Compile(const std::vector<StringPiece>& args);
extern int Link(const std::vector<StringPiece>& args);
extern int Dump(const std::vector<StringPiece>& args);
extern int Diff(const std::vector<StringPiece>& args);
} // namespace aapt
@@ -52,15 +52,15 @@ int main(int argc, char** argv) {
aapt::StringPiece command(argv[0]);
if (command == "compile" || command == "c") {
return aapt::compile(args);
return aapt::Compile(args);
} else if (command == "link" || command == "l") {
return aapt::link(args);
return aapt::Link(args);
} else if (command == "dump" || command == "d") {
return aapt::dump(args);
return aapt::Dump(args);
} else if (command == "diff") {
return aapt::diff(args);
return aapt::Diff(args);
} else if (command == "version") {
return aapt::printVersion();
return aapt::PrintVersion();
}
std::cerr << "unknown command '" << command << "'\n";
} else {

View File

@@ -17,12 +17,12 @@
#ifndef AAPT_NAME_MANGLER_H
#define AAPT_NAME_MANGLER_H
#include "Resource.h"
#include "util/Maybe.h"
#include <set>
#include <string>
#include "Resource.h"
#include "util/Maybe.h"
namespace aapt {
struct NameManglerPolicy {
@@ -31,37 +31,35 @@ struct NameManglerPolicy {
* to this package are not mangled, and mangled references inherit this
* package name.
*/
std::string targetPackageName;
std::string target_package_name;
/**
* We must know which references to mangle, and which to keep (android vs.
* com.android.support).
*/
std::set<std::string> packagesToMangle;
std::set<std::string> packages_to_mangle;
};
class NameMangler {
private:
NameManglerPolicy mPolicy;
public:
explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {}
explicit NameMangler(NameManglerPolicy policy) : policy_(policy) {}
Maybe<ResourceName> mangleName(const ResourceName& name) {
if (mPolicy.targetPackageName == name.package ||
mPolicy.packagesToMangle.count(name.package) == 0) {
Maybe<ResourceName> MangleName(const ResourceName& name) {
if (policy_.target_package_name == name.package ||
policy_.packages_to_mangle.count(name.package) == 0) {
return {};
}
std::string mangledEntryName = mangleEntry(name.package, name.entry);
return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName);
std::string mangled_entry_name = MangleEntry(name.package, name.entry);
return ResourceName(policy_.target_package_name, name.type,
mangled_entry_name);
}
bool shouldMangle(const std::string& package) const {
if (package.empty() || mPolicy.targetPackageName == package) {
bool ShouldMangle(const std::string& package) const {
if (package.empty() || policy_.target_package_name == package) {
return false;
}
return mPolicy.packagesToMangle.count(package) != 0;
return policy_.packages_to_mangle.count(package) != 0;
}
/**
@@ -69,7 +67,7 @@ class NameMangler {
* The mangled name should contain symbols that are illegal to define in XML,
* so that there will never be name mangling collisions.
*/
static std::string mangleEntry(const std::string& package,
static std::string MangleEntry(const std::string& package,
const std::string& name) {
return package + "$" + name;
}
@@ -79,16 +77,20 @@ class NameMangler {
* and the package in `outPackage`. Returns true if the name was unmangled or
* false if the name was never mangled to begin with.
*/
static bool unmangle(std::string* outName, std::string* outPackage) {
size_t pivot = outName->find('$');
static bool Unmangle(std::string* out_name, std::string* out_package) {
size_t pivot = out_name->find('$');
if (pivot == std::string::npos) {
return false;
}
outPackage->assign(outName->data(), pivot);
outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
out_package->assign(out_name->data(), pivot);
out_name->assign(out_name->data() + pivot + 1,
out_name->size() - (pivot + 1));
return true;
}
private:
NameManglerPolicy policy_;
};
} // namespace aapt

View File

@@ -15,31 +15,32 @@
*/
#include "NameMangler.h"
#include "test/Test.h"
#include <string>
#include "test/Test.h"
namespace aapt {
TEST(NameManglerTest, MangleName) {
std::string package = "android.appcompat";
std::string name = "Platform.AppCompat";
std::string mangledName = NameMangler::mangleEntry(package, name);
EXPECT_EQ(mangledName, "android.appcompat$Platform.AppCompat");
std::string mangled_name = NameMangler::MangleEntry(package, name);
EXPECT_EQ(mangled_name, "android.appcompat$Platform.AppCompat");
std::string unmangledPackage;
std::string unmangledName = mangledName;
ASSERT_TRUE(NameMangler::unmangle(&unmangledName, &unmangledPackage));
EXPECT_EQ(unmangledName, "Platform.AppCompat");
EXPECT_EQ(unmangledPackage, "android.appcompat");
std::string unmangled_package;
std::string unmangled_name = mangled_name;
ASSERT_TRUE(NameMangler::Unmangle(&unmangled_name, &unmangled_package));
EXPECT_EQ(unmangled_name, "Platform.AppCompat");
EXPECT_EQ(unmangled_package, "android.appcompat");
}
TEST(NameManglerTest, IgnoreUnmangledName) {
std::string package;
std::string name = "foo_bar";
EXPECT_FALSE(NameMangler::unmangle(&name, &package));
EXPECT_FALSE(NameMangler::Unmangle(&name, &package));
EXPECT_EQ(name, "foo_bar");
}

View File

@@ -15,14 +15,13 @@
*/
#include "Resource.h"
#include "util/StringPiece.h"
#include <map>
#include <string>
namespace aapt {
StringPiece toString(ResourceType type) {
StringPiece ToString(ResourceType type) {
switch (type) {
case ResourceType::kAnim:
return "anim";
@@ -100,7 +99,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{
{"xml", ResourceType::kXml},
};
const ResourceType* parseResourceType(const StringPiece& str) {
const ResourceType* ParseResourceType(const StringPiece& str) {
auto iter = sResourceTypeMap.find(str);
if (iter == std::end(sResourceTypeMap)) {
return nullptr;

View File

@@ -17,12 +17,6 @@
#ifndef AAPT_RESOURCE_H
#define AAPT_RESOURCE_H
#include "ConfigDescription.h"
#include "Source.h"
#include "util/StringPiece.h"
#include <utils/JenkinsHash.h>
#include <iomanip>
#include <limits>
#include <sstream>
@@ -30,6 +24,12 @@
#include <tuple>
#include <vector>
#include "utils/JenkinsHash.h"
#include "ConfigDescription.h"
#include "Source.h"
#include "util/StringPiece.h"
namespace aapt {
/**
@@ -62,13 +62,13 @@ enum class ResourceType {
kXml,
};
StringPiece toString(ResourceType type);
StringPiece ToString(ResourceType type);
/**
* Returns a pointer to a valid ResourceType, or nullptr if
* the string was invalid.
*/
const ResourceType* parseResourceType(const StringPiece& str);
const ResourceType* ParseResourceType(const StringPiece& str);
/**
* A resource's name. This can uniquely identify
@@ -76,16 +76,16 @@ const ResourceType* parseResourceType(const StringPiece& str);
*/
struct ResourceName {
std::string package;
ResourceType type;
ResourceType type = ResourceType::kRaw;
std::string entry;
ResourceName() : type(ResourceType::kRaw) {}
ResourceName() = default;
ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
int compare(const ResourceName& other) const;
bool isValid() const;
std::string toString() const;
bool is_valid() const;
std::string ToString() const;
};
/**
@@ -96,7 +96,7 @@ struct ResourceName {
*/
struct ResourceNameRef {
StringPiece package;
ResourceType type;
ResourceType type = ResourceType::kRaw;
StringPiece entry;
ResourceNameRef() = default;
@@ -108,8 +108,8 @@ struct ResourceNameRef {
ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
ResourceNameRef& operator=(const ResourceName& rhs);
ResourceName toResourceName() const;
bool isValid() const;
ResourceName ToResourceName() const;
bool is_valid() const;
};
/**
@@ -128,13 +128,13 @@ struct ResourceId {
ResourceId();
ResourceId(const ResourceId& rhs);
ResourceId(uint32_t resId); // NOLINT(implicit)
ResourceId(uint32_t res_id); // NOLINT(implicit)
ResourceId(uint8_t p, uint8_t t, uint16_t e);
bool isValid() const;
uint8_t packageId() const;
uint8_t typeId() const;
uint16_t entryId() const;
bool is_valid() const;
uint8_t package_id() const;
uint8_t type_id() const;
uint16_t entry_id() const;
};
struct SourcedResourceName {
@@ -153,7 +153,7 @@ struct ResourceFile {
Source source;
// Exported symbols
std::vector<SourcedResourceName> exportedSymbols;
std::vector<SourcedResourceName> exported_symbols;
};
/**
@@ -196,24 +196,24 @@ inline ResourceId::ResourceId() : id(0) {}
inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {}
inline ResourceId::ResourceId(uint32_t resId) : id(resId) {}
inline ResourceId::ResourceId(uint32_t res_id) : id(res_id) {}
inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e)
: id((p << 24) | (t << 16) | e) {}
inline bool ResourceId::isValid() const {
inline bool ResourceId::is_valid() const {
return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
inline uint8_t ResourceId::packageId() const {
inline uint8_t ResourceId::package_id() const {
return static_cast<uint8_t>(id >> 24);
}
inline uint8_t ResourceId::typeId() const {
inline uint8_t ResourceId::type_id() const {
return static_cast<uint8_t>(id >> 16);
}
inline uint16_t ResourceId::entryId() const {
inline uint16_t ResourceId::entry_id() const {
return static_cast<uint16_t>(id);
}
@@ -234,13 +234,13 @@ inline bool operator!=(const ResourceId& lhs, const ResourceId& rhs) {
}
inline ::std::ostream& operator<<(::std::ostream& out,
const ResourceId& resId) {
std::ios_base::fmtflags oldFlags = out.flags();
char oldFill = out.fill();
const ResourceId& res_id) {
std::ios_base::fmtflags old_flags = out.flags();
char old_fill = out.fill();
out << "0x" << std::internal << std::setfill('0') << std::setw(8) << std::hex
<< resId.id;
out.flags(oldFlags);
out.fill(oldFill);
<< res_id.id;
out.flags(old_flags);
out.fill(old_fill);
return out;
}
@@ -250,7 +250,7 @@ inline ::std::ostream& operator<<(::std::ostream& out,
inline ::std::ostream& operator<<(::std::ostream& out,
const ResourceType& val) {
return out << toString(val);
return out << ToString(val);
}
//
@@ -259,7 +259,7 @@ inline ::std::ostream& operator<<(::std::ostream& out,
inline ResourceName::ResourceName(const StringPiece& p, ResourceType t,
const StringPiece& e)
: package(p.toString()), type(t), entry(e.toString()) {}
: package(p.ToString()), type(t), entry(e.ToString()) {}
inline int ResourceName::compare(const ResourceName& other) const {
int cmp = package.compare(other.package);
@@ -270,7 +270,7 @@ inline int ResourceName::compare(const ResourceName& other) const {
return cmp;
}
inline bool ResourceName::isValid() const {
inline bool ResourceName::is_valid() const {
return !package.empty() && !entry.empty();
}
@@ -297,7 +297,7 @@ inline ::std::ostream& operator<<(::std::ostream& out,
return out << name.type << "/" << name.entry;
}
inline std::string ResourceName::toString() const {
inline std::string ResourceName::ToString() const {
std::stringstream stream;
stream << *this;
return stream.str();
@@ -321,11 +321,11 @@ inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
return *this;
}
inline ResourceName ResourceNameRef::toResourceName() const {
inline ResourceName ResourceNameRef::ToResourceName() const {
return ResourceName(package, type, entry);
}
inline bool ResourceNameRef::isValid() const {
inline bool ResourceNameRef::is_valid() const {
return !package.empty() && !entry.empty();
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,10 @@
#ifndef AAPT_RESOURCE_PARSER_H
#define AAPT_RESOURCE_PARSER_H
#include <memory>
#include "android-base/macros.h"
#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "ResourceTable.h"
@@ -26,8 +30,6 @@
#include "util/StringPiece.h"
#include "xml/XmlPullParser.h"
#include <memory>
namespace aapt {
struct ParsedResource;
@@ -42,7 +44,7 @@ struct ResourceParserOptions {
* Whether positional arguments in formatted strings are treated as errors or
* warnings.
*/
bool errorOnPositionalArguments = true;
bool error_on_positional_arguments = true;
};
/*
@@ -53,70 +55,71 @@ class ResourceParser {
ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
const ConfigDescription& config,
const ResourceParserOptions& options = {});
ResourceParser(const ResourceParser&) = delete; // No copy.
bool parse(xml::XmlPullParser* parser);
bool Parse(xml::XmlPullParser* parser);
private:
DISALLOW_COPY_AND_ASSIGN(ResourceParser);
/*
* Parses the XML subtree as a StyleString (flattened XML representation for
* strings
* with formatting). If successful, `outStyleString`
* contains the escaped and whitespace trimmed text, while `outRawString`
* with formatting). If successful, `out_style_string`
* contains the escaped and whitespace trimmed text, while `out_raw_string`
* contains the unescaped text. Returns true on success.
*/
bool flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
StyleString* outStyleString);
bool FlattenXmlSubtree(xml::XmlPullParser* parser,
std::string* out_raw_string,
StyleString* out_style_string);
/*
* Parses the XML subtree and returns an Item.
* The type of Item that can be parsed is denoted by the `typeMask`.
* If `allowRawValue` is true and the subtree can not be parsed as a regular
* The type of Item that can be parsed is denoted by the `type_mask`.
* If `allow_raw_value` is true and the subtree can not be parsed as a regular
* Item, then a
* RawString is returned. Otherwise this returns false;
*/
std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser,
const uint32_t typeMask,
const bool allowRawValue);
std::unique_ptr<Item> ParseXml(xml::XmlPullParser* parser,
const uint32_t type_mask,
const bool allow_raw_value);
bool parseResources(xml::XmlPullParser* parser);
bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
bool ParseResources(xml::XmlPullParser* parser);
bool ParseResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource,
uint32_t format);
bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parsePublicGroup(xml::XmlPullParser* parser,
ParsedResource* outResource);
bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseAddResource(xml::XmlPullParser* parser,
ParsedResource* outResource);
bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAddResource(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
bool weak);
Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
const StringPiece& tag);
bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
bool parseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* outResource);
bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseIntegerArray(xml::XmlPullParser* parser,
ParsedResource* outResource);
bool parseStringArray(xml::XmlPullParser* parser,
ParsedResource* outResource);
bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
bool ParseStyle(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseIntegerArray(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseStringArray(xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseArrayImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
uint32_t typeMask);
bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
bool ParsePlural(xml::XmlPullParser* parser, ParsedResource* out_resource);
IDiagnostics* mDiag;
ResourceTable* mTable;
Source mSource;
ConfigDescription mConfig;
ResourceParserOptions mOptions;
IDiagnostics* diag_;
ResourceTable* table_;
Source source_;
ConfigDescription config_;
ResourceParserOptions options_;
};
} // namespace aapt

View File

@@ -15,79 +15,82 @@
*/
#include "ResourceParser.h"
#include <sstream>
#include <string>
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "test/Test.h"
#include "xml/XmlPullParser.h"
#include <sstream>
#include <string>
namespace aapt {
constexpr const char* kXmlPreamble =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::stringstream input(kXmlPreamble);
input << "<attr name=\"foo\"/>" << std::endl;
ResourceTable table;
ResourceParser parser(context->getDiagnostics(), &table, Source{"test"}, {});
xml::XmlPullParser xmlParser(input);
ASSERT_FALSE(parser.parse(&xmlParser));
ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
xml::XmlPullParser xml_parser(input);
ASSERT_FALSE(parser.Parse(&xml_parser));
}
struct ResourceParserTest : public ::testing::Test {
ResourceTable mTable;
std::unique_ptr<IAaptContext> mContext;
class ResourceParserTest : public ::testing::Test {
public:
void SetUp() override { context_ = test::ContextBuilder().Build(); }
void SetUp() override { mContext = test::ContextBuilder().build(); }
::testing::AssertionResult testParse(const StringPiece& str) {
return testParse(str, ConfigDescription{});
::testing::AssertionResult TestParse(const StringPiece& str) {
return TestParse(str, ConfigDescription{});
}
::testing::AssertionResult testParse(const StringPiece& str,
::testing::AssertionResult TestParse(const StringPiece& str,
const ConfigDescription& config) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{"test"},
ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
config, parserOptions);
xml::XmlPullParser xmlParser(input);
if (parser.parse(&xmlParser)) {
if (parser.Parse(&xmlParser)) {
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure();
}
protected:
ResourceTable table_;
std::unique_ptr<IAaptContext> context_;
};
TEST_F(ResourceParserTest, ParseQuotedString) {
std::string input = "<string name=\"foo\"> \" hey there \" </string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* str = test::getValue<String>(&mTable, "string/foo");
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::string(" hey there "), *str->value);
}
TEST_F(ResourceParserTest, ParseEscapedString) {
std::string input = "<string name=\"foo\">\\?123</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* str = test::getValue<String>(&mTable, "string/foo");
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::string("?123"), *str->value);
}
TEST_F(ResourceParserTest, ParseFormattedString) {
std::string input = "<string name=\"foo\">%d %s</string>";
ASSERT_FALSE(testParse(input));
ASSERT_FALSE(TestParse(input));
input = "<string name=\"foo\">%1$d %2$s</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseStyledString) {
@@ -96,32 +99,32 @@ TEST_F(ResourceParserTest, ParseStyledString) {
// use UTF-16 length and not UTF-18 length.
std::string input =
"<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
StyledString* str = test::getValue<StyledString>(&mTable, "string/foo");
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
const std::string expectedStr = "This is my aunt\u2019s string";
EXPECT_EQ(expectedStr, *str->value->str);
const std::string expected_str = "This is my aunt\u2019s string";
EXPECT_EQ(expected_str, *str->value->str);
EXPECT_EQ(1u, str->value->spans.size());
EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
EXPECT_EQ(17u, str->value->spans[0].firstChar);
EXPECT_EQ(23u, str->value->spans[0].lastChar);
EXPECT_EQ(17u, str->value->spans[0].first_char);
EXPECT_EQ(23u, str->value->spans[0].last_char);
}
TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
std::string input = "<string name=\"foo\"> This is what I think </string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* str = test::getValue<String>(&mTable, "string/foo");
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::string("This is what I think"), *str->value);
input = "<string name=\"foo2\">\" This is what I think \"</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
str = test::getValue<String>(&mTable, "string/foo2");
str = test::GetValue<String>(&table_, "string/foo2");
ASSERT_NE(nullptr, str);
EXPECT_EQ(std::string(" This is what I think "), *str->value);
}
@@ -131,16 +134,16 @@ TEST_F(ResourceParserTest, IgnoreXliffTags) {
"<string name=\"foo\" \n"
" xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
" There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* str = test::getValue<String>(&mTable, "string/foo");
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, str);
EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
}
TEST_F(ResourceParserTest, ParseNull) {
std::string input = "<integer name=\"foo\">@null</integer>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
// The Android runtime treats a value of android::Res_value::TYPE_NULL as
// a non-existing value, and this causes problems in styles when trying to
@@ -149,7 +152,7 @@ TEST_F(ResourceParserTest, ParseNull) {
// android::Res_value::TYPE_REFERENCE
// with a data value of 0.
BinaryPrimitive* integer =
test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_NE(nullptr, integer);
EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE),
integer->value.dataType);
@@ -158,10 +161,10 @@ TEST_F(ResourceParserTest, ParseNull) {
TEST_F(ResourceParserTest, ParseEmpty) {
std::string input = "<integer name=\"foo\">@empty</integer>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
BinaryPrimitive* integer =
test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_NE(nullptr, integer);
EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
@@ -171,15 +174,15 @@ TEST_F(ResourceParserTest, ParseAttr) {
std::string input =
"<attr name=\"foo\" format=\"string\"/>\n"
"<attr name=\"bar\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
attr = test::getValue<Attribute>(&mTable, "attr/bar");
attr = test::GetValue<Attribute>(&table_, "attr/bar");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask);
}
// Old AAPT allowed attributes to be defined under different configurations, but
@@ -188,42 +191,42 @@ TEST_F(ResourceParserTest, ParseAttr) {
// behavior.
TEST_F(ResourceParserTest,
ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
std::string input = R"EOF(
<attr name="foo" />
<declare-styleable name="bar">
<attr name="baz" />
</declare-styleable>)EOF";
ASSERT_TRUE(testParse(input, watchConfig));
ASSERT_TRUE(TestParse(input, watch_config));
EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/foo",
watchConfig));
EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/baz",
watchConfig));
EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(
&mTable, "styleable/bar", watchConfig));
EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo",
watch_config));
EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz",
watch_config));
EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(
&table_, "styleable/bar", watch_config));
EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/foo"));
EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/baz"));
EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "styleable/bar"));
EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar"));
}
TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
std::string input =
"<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask);
EXPECT_EQ(10, attr->minInt);
EXPECT_EQ(23, attr->maxInt);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask);
EXPECT_EQ(10, attr->min_int);
EXPECT_EQ(23, attr->max_int);
}
TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
std::string input =
"<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
ASSERT_FALSE(testParse(input));
ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
@@ -232,11 +235,11 @@ TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
" <attr name=\"foo\" />\n"
"</declare-styleable>\n"
"<attr name=\"foo\" format=\"string\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
}
TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
@@ -247,11 +250,11 @@ TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
"<declare-styleable name=\"Window\">\n"
" <attr name=\"foo\" format=\"boolean\"/>\n"
"</declare-styleable>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask);
}
TEST_F(ResourceParserTest, ParseEnumAttr) {
@@ -261,24 +264,24 @@ TEST_F(ResourceParserTest, ParseEnumAttr) {
" <enum name=\"bat\" value=\"1\"/>\n"
" <enum name=\"baz\" value=\"2\"/>\n"
"</attr>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* enumAttr = test::getValue<Attribute>(&mTable, "attr/foo");
ASSERT_NE(enumAttr, nullptr);
EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
ASSERT_EQ(enumAttr->symbols.size(), 3u);
Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(enum_attr, nullptr);
EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM);
ASSERT_EQ(enum_attr->symbols.size(), 3u);
AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, "bar");
EXPECT_EQ(enumAttr->symbols[0].value, 0u);
AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar");
EXPECT_EQ(enum_attr->symbols[0].value, 0u);
AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, "bat");
EXPECT_EQ(enumAttr->symbols[1].value, 1u);
AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat");
EXPECT_EQ(enum_attr->symbols[1].value, 1u);
AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz");
EXPECT_EQ(enumAttr->symbols[2].value, 2u);
AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz");
EXPECT_EQ(enum_attr->symbols[2].value, 2u);
}
TEST_F(ResourceParserTest, ParseFlagAttr) {
@@ -288,29 +291,29 @@ TEST_F(ResourceParserTest, ParseFlagAttr) {
" <flag name=\"bat\" value=\"1\"/>\n"
" <flag name=\"baz\" value=\"2\"/>\n"
"</attr>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Attribute* flagAttr = test::getValue<Attribute>(&mTable, "attr/foo");
ASSERT_NE(nullptr, flagAttr);
EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
ASSERT_EQ(flagAttr->symbols.size(), 3u);
Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, flag_attr);
EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS);
ASSERT_EQ(flag_attr->symbols.size(), 3u);
AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, "bar");
EXPECT_EQ(flagAttr->symbols[0].value, 0u);
AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar");
EXPECT_EQ(flag_attr->symbols[0].value, 0u);
AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, "bat");
EXPECT_EQ(flagAttr->symbols[1].value, 1u);
AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat");
EXPECT_EQ(flag_attr->symbols[1].value, 1u);
AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, "baz");
EXPECT_EQ(flagAttr->symbols[2].value, 2u);
AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz");
EXPECT_EQ(flag_attr->symbols[2].value, 2u);
std::unique_ptr<BinaryPrimitive> flagValue =
ResourceUtils::tryParseFlagSymbol(flagAttr, "baz|bat");
ASSERT_NE(nullptr, flagValue);
EXPECT_EQ(flagValue->value.data, 1u | 2u);
std::unique_ptr<BinaryPrimitive> flag_value =
ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
ASSERT_NE(nullptr, flag_value);
EXPECT_EQ(flag_value->value.data, 1u | 2u);
}
TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
@@ -320,7 +323,7 @@ TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
" <enum name=\"bat\" value=\"1\"/>\n"
" <enum name=\"bat\" value=\"2\"/>\n"
"</attr>";
ASSERT_FALSE(testParse(input));
ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseStyle) {
@@ -330,38 +333,38 @@ TEST_F(ResourceParserTest, ParseStyle) {
" <item name=\"bat\">@string/hey</item>\n"
" <item name=\"baz\"><b>hey</b></item>\n"
"</style>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo");
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
AAPT_ASSERT_TRUE(style->parent.value().name);
EXPECT_EQ(test::parseNameOrDie("style/fu"),
EXPECT_EQ(test::ParseNameOrDie("style/fu"),
style->parent.value().name.value());
ASSERT_EQ(3u, style->entries.size());
AAPT_ASSERT_TRUE(style->entries[0].key.name);
EXPECT_EQ(test::parseNameOrDie("attr/bar"),
EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
style->entries[0].key.name.value());
AAPT_ASSERT_TRUE(style->entries[1].key.name);
EXPECT_EQ(test::parseNameOrDie("attr/bat"),
EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
style->entries[1].key.name.value());
AAPT_ASSERT_TRUE(style->entries[2].key.name);
EXPECT_EQ(test::parseNameOrDie("attr/baz"),
EXPECT_EQ(test::ParseNameOrDie("attr/baz"),
style->entries[2].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo");
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
AAPT_ASSERT_TRUE(style->parent.value().name);
EXPECT_EQ(test::parseNameOrDie("com.app:style/Theme"),
EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"),
style->parent.value().name.value());
}
@@ -369,13 +372,13 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
std::string input =
"<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
" name=\"foo\" parent=\"app:Theme\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo");
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
AAPT_ASSERT_TRUE(style->parent.value().name);
EXPECT_EQ(test::parseNameOrDie("android:style/Theme"),
EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"),
style->parent.value().name.value());
}
@@ -385,55 +388,55 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
"name=\"foo\">\n"
" <item name=\"app:bar\">0</item>\n"
"</style>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo");
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_NE(nullptr, style);
ASSERT_EQ(1u, style->entries.size());
EXPECT_EQ(test::parseNameOrDie("android:attr/bar"),
EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"),
style->entries[0].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
std::string input = "<style name=\"foo.bar\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
AAPT_ASSERT_TRUE(style->parent.value().name);
EXPECT_EQ(style->parent.value().name.value(),
test::parseNameOrDie("style/foo"));
EXPECT_TRUE(style->parentInferred);
test::ParseNameOrDie("style/foo"));
EXPECT_TRUE(style->parent_inferred);
}
TEST_F(ResourceParserTest,
ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
ASSERT_NE(nullptr, style);
AAPT_EXPECT_FALSE(style->parent);
EXPECT_FALSE(style->parentInferred);
EXPECT_FALSE(style->parent_inferred);
}
TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
std::string input =
R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Style* style = test::getValue<Style>(&mTable, "style/foo");
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
EXPECT_TRUE(style->parent.value().privateReference);
EXPECT_TRUE(style->parent.value().private_reference);
}
TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
std::string input = "<string name=\"foo\">@+id/bar</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Id* id = test::getValue<Id>(&mTable, "id/bar");
Id* id = test::GetValue<Id>(&table_, "id/bar");
ASSERT_NE(id, nullptr);
}
@@ -446,35 +449,35 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
" <enum name=\"foo\" value=\"1\"/>\n"
" </attr>\n"
"</declare-styleable>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Maybe<ResourceTable::SearchResult> result =
mTable.findResource(test::parseNameOrDie("styleable/foo"));
table_.FindResource(test::ParseNameOrDie("styleable/foo"));
AAPT_ASSERT_TRUE(result);
EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/bar");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
ASSERT_NE(attr, nullptr);
EXPECT_TRUE(attr->isWeak());
EXPECT_TRUE(attr->IsWeak());
attr = test::getValue<Attribute>(&mTable, "attr/bat");
attr = test::GetValue<Attribute>(&table_, "attr/bat");
ASSERT_NE(attr, nullptr);
EXPECT_TRUE(attr->isWeak());
EXPECT_TRUE(attr->IsWeak());
attr = test::getValue<Attribute>(&mTable, "attr/baz");
attr = test::GetValue<Attribute>(&table_, "attr/baz");
ASSERT_NE(attr, nullptr);
EXPECT_TRUE(attr->isWeak());
EXPECT_TRUE(attr->IsWeak());
EXPECT_EQ(1u, attr->symbols.size());
EXPECT_NE(nullptr, test::getValue<Id>(&mTable, "id/foo"));
EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo"));
Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
ASSERT_NE(styleable, nullptr);
ASSERT_EQ(3u, styleable->entries.size());
EXPECT_EQ(test::parseNameOrDie("attr/bar"),
EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
styleable->entries[0].name.value());
EXPECT_EQ(test::parseNameOrDie("attr/bat"),
EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
styleable->entries[1].name.value());
}
@@ -485,16 +488,16 @@ TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
" <attr name=\"*android:bar\" />\n"
" <attr name=\"privAndroid:bat\" />\n"
"</declare-styleable>";
ASSERT_TRUE(testParse(input));
Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
ASSERT_TRUE(TestParse(input));
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
ASSERT_NE(nullptr, styleable);
ASSERT_EQ(2u, styleable->entries.size());
EXPECT_TRUE(styleable->entries[0].privateReference);
EXPECT_TRUE(styleable->entries[0].private_reference);
AAPT_ASSERT_TRUE(styleable->entries[0].name);
EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
EXPECT_TRUE(styleable->entries[1].privateReference);
EXPECT_TRUE(styleable->entries[1].private_reference);
AAPT_ASSERT_TRUE(styleable->entries[1].name);
EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
}
@@ -506,15 +509,15 @@ TEST_F(ResourceParserTest, ParseArray) {
" <item>hey</item>\n"
" <item>23</item>\n"
"</array>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Array* array = test::getValue<Array>(&mTable, "array/foo");
Array* array = test::GetValue<Array>(&table_, "array/foo");
ASSERT_NE(array, nullptr);
ASSERT_EQ(3u, array->items.size());
EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get()));
EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get()));
EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get()));
}
TEST_F(ResourceParserTest, ParseStringArray) {
@@ -522,8 +525,8 @@ TEST_F(ResourceParserTest, ParseStringArray) {
"<string-array name=\"foo\">\n"
" <item>\"Werk\"</item>\n"
"</string-array>\n";
ASSERT_TRUE(testParse(input));
EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "array/foo"));
ASSERT_TRUE(TestParse(input));
EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo"));
}
TEST_F(ResourceParserTest, ParsePlural) {
@@ -532,18 +535,18 @@ TEST_F(ResourceParserTest, ParsePlural) {
" <item quantity=\"other\">apples</item>\n"
" <item quantity=\"one\">apple</item>\n"
"</plurals>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
std::string input =
"<!--This is a comment-->\n"
"<string name=\"foo\">Hi</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* value = test::getValue<String>(&mTable, "string/foo");
String* value = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, value);
EXPECT_EQ(value->getComment(), "This is a comment");
EXPECT_EQ(value->GetComment(), "This is a comment");
}
TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
@@ -552,11 +555,11 @@ TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
"<!--Two-->\n"
"<string name=\"foo\">Hi</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* value = test::getValue<String>(&mTable, "string/foo");
String* value = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, value);
EXPECT_EQ(value->getComment(), "Two");
EXPECT_EQ(value->GetComment(), "Two");
}
TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
@@ -567,11 +570,11 @@ TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
"<!--Two-->\n"
"</string>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
String* value = test::getValue<String>(&mTable, "string/foo");
String* value = test::GetValue<String>(&table_, "string/foo");
ASSERT_NE(nullptr, value);
EXPECT_EQ(value->getComment(), "One");
EXPECT_EQ(value->GetComment(), "One");
}
TEST_F(ResourceParserTest, ParseNestedComments) {
@@ -588,21 +591,21 @@ TEST_F(ResourceParserTest, ParseNestedComments) {
<!-- The very first -->
<enum name="one" value="1" />
</attr>)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
ASSERT_NE(nullptr, styleable);
ASSERT_EQ(1u, styleable->entries.size());
EXPECT_EQ(StringPiece("The name of the bar"),
styleable->entries.front().getComment());
styleable->entries.front().GetComment());
Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
ASSERT_NE(nullptr, attr);
ASSERT_EQ(1u, attr->symbols.size());
EXPECT_EQ(StringPiece("The very first"),
attr->symbols.front().symbol.getComment());
attr->symbols.front().symbol.GetComment());
}
/*
@@ -611,9 +614,9 @@ TEST_F(ResourceParserTest, ParseNestedComments) {
*/
TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
std::string input = "<public type=\"id\" name=\"foo\"/>";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Id* id = test::getValue<Id>(&mTable, "id/foo");
Id* id = test::GetValue<Id>(&table_, "id/foo");
ASSERT_NE(nullptr, id);
}
@@ -626,26 +629,26 @@ TEST_F(ResourceParserTest, KeepAllProducts) {
<string name="bit" product="phablet">hoot</string>
<string name="bot" product="default">yes</string>
)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
&mTable, "string/foo",
ConfigDescription::defaultConfig(), "phone"));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
&mTable, "string/foo",
ConfigDescription::defaultConfig(), "no-sdcard"));
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
&table_, "string/foo",
ConfigDescription::DefaultConfig(), "phone"));
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
&table_, "string/foo",
ConfigDescription::DefaultConfig(), "no-sdcard"));
EXPECT_NE(nullptr,
test::getValueForConfigAndProduct<String>(
&mTable, "string/bar", ConfigDescription::defaultConfig(), ""));
test::GetValueForConfigAndProduct<String>(
&table_, "string/bar", ConfigDescription::DefaultConfig(), ""));
EXPECT_NE(nullptr,
test::getValueForConfigAndProduct<String>(
&mTable, "string/baz", ConfigDescription::defaultConfig(), ""));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
&mTable, "string/bit",
ConfigDescription::defaultConfig(), "phablet"));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
&mTable, "string/bot",
ConfigDescription::defaultConfig(), "default"));
test::GetValueForConfigAndProduct<String>(
&table_, "string/baz", ConfigDescription::DefaultConfig(), ""));
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
&table_, "string/bit",
ConfigDescription::DefaultConfig(), "phablet"));
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
&table_, "string/bot",
ConfigDescription::DefaultConfig(), "default"));
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
@@ -654,61 +657,61 @@ TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
<public name="foo" />
<public name="bar" />
</public-group>)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Maybe<ResourceTable::SearchResult> result =
mTable.findResource(test::parseNameOrDie("attr/foo"));
table_.FindResource(test::ParseNameOrDie("attr/foo"));
AAPT_ASSERT_TRUE(result);
AAPT_ASSERT_TRUE(result.value().package->id);
AAPT_ASSERT_TRUE(result.value().type->id);
AAPT_ASSERT_TRUE(result.value().entry->id);
ResourceId actualId(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
EXPECT_EQ(ResourceId(0x01010040), actualId);
ResourceId actual_id(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
EXPECT_EQ(ResourceId(0x01010040), actual_id);
result = mTable.findResource(test::parseNameOrDie("attr/bar"));
result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
AAPT_ASSERT_TRUE(result);
AAPT_ASSERT_TRUE(result.value().package->id);
AAPT_ASSERT_TRUE(result.value().type->id);
AAPT_ASSERT_TRUE(result.value().entry->id);
actualId = ResourceId(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
EXPECT_EQ(ResourceId(0x01010041), actualId);
actual_id = ResourceId(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
EXPECT_EQ(ResourceId(0x01010041), actual_id);
}
TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
std::string input =
R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
ASSERT_FALSE(testParse(input));
ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest,
AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
Maybe<ResourceTable::SearchResult> result =
mTable.findResource(test::parseNameOrDie("string/bar"));
table_.FindResource(test::ParseNameOrDie("string/bar"));
AAPT_ASSERT_TRUE(result);
const ResourceEntry* entry = result.value().entry;
ASSERT_NE(nullptr, entry);
EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
}
TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
std::string input =
R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
ASSERT_TRUE(testParse(input));
ASSERT_TRUE(TestParse(input));
BinaryPrimitive* val =
test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_NE(nullptr, val);
EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);

View File

@@ -21,6 +21,7 @@
#include "ValueVisitor.h"
#include "util/Util.h"
#include <android-base/logging.h>
#include <androidfw/ResourceTypes.h>
#include <algorithm>
#include <memory>
@@ -29,28 +30,29 @@
namespace aapt {
static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs,
ResourceType rhs) {
static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
ResourceType rhs) {
return lhs->type < rhs;
}
template <typename T>
static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
const StringPiece& rhs) {
static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs,
const StringPiece& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) {
ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
const auto last = packages.end();
auto iter = std::lower_bound(packages.begin(), last, name,
lessThanStructWithName<ResourceTablePackage>);
auto iter =
std::lower_bound(packages.begin(), last, name,
less_than_struct_with_name<ResourceTablePackage>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
return nullptr;
}
ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
for (auto& package : packages) {
if (package->id && package->id.value() == id) {
return package.get();
@@ -59,9 +61,9 @@ ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
return nullptr;
}
ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name,
ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name,
Maybe<uint8_t> id) {
ResourceTablePackage* package = findOrCreatePackage(name);
ResourceTablePackage* package = FindOrCreatePackage(name);
if (id && !package->id) {
package->id = id;
return package;
@@ -73,61 +75,62 @@ ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name,
return package;
}
ResourceTablePackage* ResourceTable::findOrCreatePackage(
ResourceTablePackage* ResourceTable::FindOrCreatePackage(
const StringPiece& name) {
const auto last = packages.end();
auto iter = std::lower_bound(packages.begin(), last, name,
lessThanStructWithName<ResourceTablePackage>);
auto iter =
std::lower_bound(packages.begin(), last, name,
less_than_struct_with_name<ResourceTablePackage>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
std::unique_ptr<ResourceTablePackage> newPackage =
std::unique_ptr<ResourceTablePackage> new_package =
util::make_unique<ResourceTablePackage>();
newPackage->name = name.toString();
return packages.emplace(iter, std::move(newPackage))->get();
new_package->name = name.ToString();
return packages.emplace(iter, std::move(new_package))->get();
}
ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
const auto last = types.end();
auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
if (iter != last && (*iter)->type == type) {
return iter->get();
}
return nullptr;
}
ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
const auto last = types.end();
auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
if (iter != last && (*iter)->type == type) {
return iter->get();
}
return types.emplace(iter, new ResourceTableType(type))->get();
}
ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) {
ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
const auto last = entries.end();
auto iter = std::lower_bound(entries.begin(), last, name,
lessThanStructWithName<ResourceEntry>);
less_than_struct_with_name<ResourceEntry>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
return nullptr;
}
ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) {
ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
auto last = entries.end();
auto iter = std::lower_bound(entries.begin(), last, name,
lessThanStructWithName<ResourceEntry>);
less_than_struct_with_name<ResourceEntry>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
return entries.emplace(iter, new ResourceEntry(name))->get();
}
ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
return findValue(config, StringPiece());
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
return FindValue(config, StringPiece());
}
struct ConfigKey {
@@ -144,7 +147,7 @@ bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs,
return cmp < 0;
}
ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
const StringPiece& product) {
auto iter = std::lower_bound(values.begin(), values.end(),
ConfigKey{&config, product}, ltConfigKeyRef);
@@ -157,7 +160,7 @@ ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
return nullptr;
}
ResourceConfigValue* ResourceEntry::findOrCreateValue(
ResourceConfigValue* ResourceEntry::FindOrCreateValue(
const ConfigDescription& config, const StringPiece& product) {
auto iter = std::lower_bound(values.begin(), values.end(),
ConfigKey{&config, product}, ltConfigKeyRef);
@@ -197,7 +200,7 @@ std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(
return results;
}
std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
const std::function<bool(ResourceConfigValue*)>& f) {
std::vector<ResourceConfigValue*> results;
for (auto& configValue : values) {
@@ -232,16 +235,16 @@ std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
* format for there to be
* no error.
*/
ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(
Value* existing, Value* incoming) {
Attribute* existingAttr = valueCast<Attribute>(existing);
Attribute* incomingAttr = valueCast<Attribute>(incoming);
if (!incomingAttr) {
if (incoming->isWeak()) {
Attribute* existing_attr = ValueCast<Attribute>(existing);
Attribute* incoming_attr = ValueCast<Attribute>(incoming);
if (!incoming_attr) {
if (incoming->IsWeak()) {
// We're trying to add a weak resource but a resource
// already exists. Keep the existing.
return CollisionResult::kKeepOriginal;
} else if (existing->isWeak()) {
} else if (existing->IsWeak()) {
// Override the weak resource with the new strong resource.
return CollisionResult::kTakeNew;
}
@@ -250,8 +253,8 @@ ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
return CollisionResult::kConflict;
}
if (!existingAttr) {
if (existing->isWeak()) {
if (!existing_attr) {
if (existing->IsWeak()) {
// The existing value is not an attribute and it is weak,
// so take the incoming attribute value.
return CollisionResult::kTakeNew;
@@ -261,7 +264,7 @@ ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
return CollisionResult::kConflict;
}
assert(incomingAttr && existingAttr);
CHECK(incoming_attr != nullptr && existing_attr != nullptr);
//
// Attribute specific handling. At this point we know both
@@ -269,22 +272,22 @@ ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
// attributes all-over, we do special handling to see
// which definition sticks.
//
if (existingAttr->typeMask == incomingAttr->typeMask) {
if (existing_attr->type_mask == incoming_attr->type_mask) {
// The two attributes are both DECLs, but they are plain attributes
// with the same formats.
// Keep the strongest one.
return existingAttr->isWeak() ? CollisionResult::kTakeNew
: CollisionResult::kKeepOriginal;
return existing_attr->IsWeak() ? CollisionResult::kTakeNew
: CollisionResult::kKeepOriginal;
}
if (existingAttr->isWeak() &&
existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
if (existing_attr->IsWeak() &&
existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
// Any incoming attribute is better than this.
return CollisionResult::kTakeNew;
}
if (incomingAttr->isWeak() &&
incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
if (incoming_attr->IsWeak() &&
incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
// The incoming attribute may be a USE instead of a DECL.
// Keep the existing attribute.
return CollisionResult::kKeepOriginal;
@@ -295,138 +298,139 @@ ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
static constexpr const char* kValidNameChars = "._-";
static constexpr const char* kValidNameMangledChars = "._-$";
bool ResourceTable::addResource(const ResourceNameRef& name,
bool ResourceTable::AddResource(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
return addResourceImpl(name, {}, config, product, std::move(value),
kValidNameChars, resolveValueCollision, diag);
return AddResourceImpl(name, {}, config, product, std::move(value),
kValidNameChars, ResolveValueCollision, diag);
}
bool ResourceTable::addResource(const ResourceNameRef& name,
const ResourceId& resId,
bool ResourceTable::AddResource(const ResourceNameRef& name,
const ResourceId& res_id,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
return addResourceImpl(name, resId, config, product, std::move(value),
kValidNameChars, resolveValueCollision, diag);
return AddResourceImpl(name, res_id, config, product, std::move(value),
kValidNameChars, ResolveValueCollision, diag);
}
bool ResourceTable::addFileReference(const ResourceNameRef& name,
bool ResourceTable::AddFileReference(const ResourceNameRef& name,
const ConfigDescription& config,
const Source& source,
const StringPiece& path,
IDiagnostics* diag) {
return addFileReferenceImpl(name, config, source, path, nullptr,
return AddFileReferenceImpl(name, config, source, path, nullptr,
kValidNameChars, diag);
}
bool ResourceTable::addFileReferenceAllowMangled(
bool ResourceTable::AddFileReferenceAllowMangled(
const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const StringPiece& path, io::IFile* file,
IDiagnostics* diag) {
return addFileReferenceImpl(name, config, source, path, file,
return AddFileReferenceImpl(name, config, source, path, file,
kValidNameMangledChars, diag);
}
bool ResourceTable::addFileReferenceImpl(
bool ResourceTable::AddFileReferenceImpl(
const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const StringPiece& path, io::IFile* file,
const char* validChars, IDiagnostics* diag) {
const char* valid_chars, IDiagnostics* diag) {
std::unique_ptr<FileReference> fileRef =
util::make_unique<FileReference>(stringPool.makeRef(path));
fileRef->setSource(source);
util::make_unique<FileReference>(string_pool.MakeRef(path));
fileRef->SetSource(source);
fileRef->file = file;
return addResourceImpl(name, ResourceId{}, config, StringPiece{},
std::move(fileRef), validChars, resolveValueCollision,
return AddResourceImpl(name, ResourceId{}, config, StringPiece{},
std::move(fileRef), valid_chars, ResolveValueCollision,
diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
kValidNameMangledChars, resolveValueCollision, diag);
return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
kValidNameMangledChars, ResolveValueCollision, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
const ResourceId& id,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
return addResourceImpl(name, id, config, product, std::move(value),
kValidNameMangledChars, resolveValueCollision, diag);
return AddResourceImpl(name, id, config, product, std::move(value),
kValidNameMangledChars, ResolveValueCollision, diag);
}
bool ResourceTable::addResourceImpl(
const ResourceNameRef& name, const ResourceId& resId,
bool ResourceTable::AddResourceImpl(
const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config, const StringPiece& product,
std::unique_ptr<Value> value, const char* validChars,
std::unique_ptr<Value> value, const char* valid_chars,
const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) {
assert(value && "value can't be nullptr");
assert(diag && "diagnostics can't be nullptr");
CHECK(value != nullptr);
CHECK(diag != nullptr);
auto badCharIter =
util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
diag->error(DiagMessage(value->getSource())
auto bad_char_iter =
util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
if (bad_char_iter != name.entry.end()) {
diag->Error(DiagMessage(value->GetSource())
<< "resource '" << name << "' has invalid entry name '"
<< name.entry << "'. Invalid character '"
<< StringPiece(badCharIter, 1) << "'");
<< StringPiece(bad_char_iter, 1) << "'");
return false;
}
ResourceTablePackage* package = findOrCreatePackage(name.package);
if (resId.isValid() && package->id &&
package->id.value() != resId.packageId()) {
diag->error(DiagMessage(value->getSource())
<< "trying to add resource '" << name << "' with ID " << resId
ResourceTablePackage* package = FindOrCreatePackage(name.package);
if (res_id.is_valid() && package->id &&
package->id.value() != res_id.package_id()) {
diag->Error(DiagMessage(value->GetSource())
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but package '" << package->name << "' already has ID "
<< std::hex << (int)package->id.value() << std::dec);
return false;
}
ResourceTableType* type = package->findOrCreateType(name.type);
if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
diag->error(DiagMessage(value->getSource())
<< "trying to add resource '" << name << "' with ID " << resId
ResourceTableType* type = package->FindOrCreateType(name.type);
if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
diag->Error(DiagMessage(value->GetSource())
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but type '" << type->type << "' already has ID "
<< std::hex << (int)type->id.value() << std::dec);
return false;
}
ResourceEntry* entry = type->findOrCreateEntry(name.entry);
if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
diag->error(DiagMessage(value->getSource())
<< "trying to add resource '" << name << "' with ID " << resId
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
if (res_id.is_valid() && entry->id &&
entry->id.value() != res_id.entry_id()) {
diag->Error(DiagMessage(value->GetSource())
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but resource already has ID "
<< ResourceId(package->id.value(), type->id.value(),
entry->id.value()));
return false;
}
ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
if (!configValue->value) {
ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
if (!config_value->value) {
// Resource does not exist, add it now.
configValue->value = std::move(value);
config_value->value = std::move(value);
} else {
switch (conflictResolver(configValue->value.get(), value.get())) {
switch (conflictResolver(config_value->value.get(), value.get())) {
case CollisionResult::kTakeNew:
// Take the incoming value.
configValue->value = std::move(value);
config_value->value = std::move(value);
break;
case CollisionResult::kConflict:
diag->error(DiagMessage(value->getSource())
diag->Error(DiagMessage(value->GetSource())
<< "duplicate value for resource '" << name << "' "
<< "with config '" << config << "'");
diag->error(DiagMessage(configValue->value->getSource())
diag->Error(DiagMessage(config_value->value->GetSource())
<< "resource previously defined here");
return false;
@@ -435,113 +439,114 @@ bool ResourceTable::addResourceImpl(
}
}
if (resId.isValid()) {
package->id = resId.packageId();
type->id = resId.typeId();
entry->id = resId.entryId();
if (res_id.is_valid()) {
package->id = res_id.package_id();
type->id = res_id.type_id();
entry->id = res_id.entry_id();
}
return true;
}
bool ResourceTable::setSymbolState(const ResourceNameRef& name,
const ResourceId& resId,
bool ResourceTable::SetSymbolState(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol, IDiagnostics* diag) {
return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
return SetSymbolStateImpl(name, res_id, symbol, kValidNameChars, diag);
}
bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& resId,
bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol,
IDiagnostics* diag) {
return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
return SetSymbolStateImpl(name, res_id, symbol, kValidNameMangledChars, diag);
}
bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name,
const ResourceId& resId,
bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol,
const char* validChars,
const char* valid_chars,
IDiagnostics* diag) {
assert(diag && "diagnostics can't be nullptr");
CHECK(diag != nullptr);
auto badCharIter =
util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
diag->error(DiagMessage(symbol.source)
auto bad_char_iter =
util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
if (bad_char_iter != name.entry.end()) {
diag->Error(DiagMessage(symbol.source)
<< "resource '" << name << "' has invalid entry name '"
<< name.entry << "'. Invalid character '"
<< StringPiece(badCharIter, 1) << "'");
<< StringPiece(bad_char_iter, 1) << "'");
return false;
}
ResourceTablePackage* package = findOrCreatePackage(name.package);
if (resId.isValid() && package->id &&
package->id.value() != resId.packageId()) {
diag->error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << resId
ResourceTablePackage* package = FindOrCreatePackage(name.package);
if (res_id.is_valid() && package->id &&
package->id.value() != res_id.package_id()) {
diag->Error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but package '" << package->name << "' already has ID "
<< std::hex << (int)package->id.value() << std::dec);
return false;
}
ResourceTableType* type = package->findOrCreateType(name.type);
if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
diag->error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << resId
ResourceTableType* type = package->FindOrCreateType(name.type);
if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
diag->Error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but type '" << type->type << "' already has ID "
<< std::hex << (int)type->id.value() << std::dec);
return false;
}
ResourceEntry* entry = type->findOrCreateEntry(name.entry);
if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
diag->error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << resId
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
if (res_id.is_valid() && entry->id &&
entry->id.value() != res_id.entry_id()) {
diag->Error(DiagMessage(symbol.source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but resource already has ID "
<< ResourceId(package->id.value(), type->id.value(),
entry->id.value()));
return false;
}
if (resId.isValid()) {
package->id = resId.packageId();
type->id = resId.typeId();
entry->id = resId.entryId();
if (res_id.is_valid()) {
package->id = res_id.package_id();
type->id = res_id.type_id();
entry->id = res_id.entry_id();
}
// Only mark the type state as public, it doesn't care about being private.
if (symbol.state == SymbolState::kPublic) {
type->symbolStatus.state = SymbolState::kPublic;
type->symbol_status.state = SymbolState::kPublic;
}
if (symbol.state == SymbolState::kUndefined &&
entry->symbolStatus.state != SymbolState::kUndefined) {
entry->symbol_status.state != SymbolState::kUndefined) {
// We can't undefine a symbol (remove its visibility). Ignore.
return true;
}
if (symbol.state == SymbolState::kPrivate &&
entry->symbolStatus.state == SymbolState::kPublic) {
entry->symbol_status.state == SymbolState::kPublic) {
// We can't downgrade public to private. Ignore.
return true;
}
entry->symbolStatus = std::move(symbol);
entry->symbol_status = std::move(symbol);
return true;
}
Maybe<ResourceTable::SearchResult> ResourceTable::findResource(
Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(
const ResourceNameRef& name) {
ResourceTablePackage* package = findPackage(name.package);
ResourceTablePackage* package = FindPackage(name.package);
if (!package) {
return {};
}
ResourceTableType* type = package->findType(name.type);
ResourceTableType* type = package->FindType(name.type);
if (!type) {
return {};
}
ResourceEntry* entry = type->findEntry(name.entry);
ResourceEntry* entry = type->FindEntry(name.entry);
if (!entry) {
return {};
}

View File

@@ -70,7 +70,7 @@ class ResourceConfigValue {
ResourceConfigValue(const ConfigDescription& config,
const StringPiece& product)
: config(config), product(product.toString()) {}
: config(config), product(product.ToString()) {}
private:
DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
@@ -98,23 +98,23 @@ class ResourceEntry {
* Whether this resource is public (and must maintain the same entry ID across
* builds).
*/
Symbol symbolStatus;
Symbol symbol_status;
/**
* The resource's values for each configuration.
*/
std::vector<std::unique_ptr<ResourceConfigValue>> values;
explicit ResourceEntry(const StringPiece& name) : name(name.toString()) {}
explicit ResourceEntry(const StringPiece& name) : name(name.ToString()) {}
ResourceConfigValue* findValue(const ConfigDescription& config);
ResourceConfigValue* findValue(const ConfigDescription& config,
ResourceConfigValue* FindValue(const ConfigDescription& config);
ResourceConfigValue* FindValue(const ConfigDescription& config,
const StringPiece& product);
ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config,
const StringPiece& product);
std::vector<ResourceConfigValue*> findAllValues(
const ConfigDescription& config);
std::vector<ResourceConfigValue*> findValuesIf(
std::vector<ResourceConfigValue*> FindValuesIf(
const std::function<bool(ResourceConfigValue*)>& f);
private:
@@ -141,7 +141,7 @@ class ResourceTableType {
* Whether this type is public (and must maintain the same
* type ID across builds).
*/
Symbol symbolStatus;
Symbol symbol_status;
/**
* List of resources for this type.
@@ -150,8 +150,8 @@ class ResourceTableType {
explicit ResourceTableType(const ResourceType type) : type(type) {}
ResourceEntry* findEntry(const StringPiece& name);
ResourceEntry* findOrCreateEntry(const StringPiece& name);
ResourceEntry* FindEntry(const StringPiece& name);
ResourceEntry* FindOrCreateEntry(const StringPiece& name);
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
@@ -168,8 +168,8 @@ class ResourceTablePackage {
std::vector<std::unique_ptr<ResourceTableType>> types;
ResourceTablePackage() = default;
ResourceTableType* findType(ResourceType type);
ResourceTableType* findOrCreateType(const ResourceType type);
ResourceTableType* FindType(ResourceType type);
ResourceTableType* FindOrCreateType(const ResourceType type);
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
@@ -191,53 +191,53 @@ class ResourceTable {
* When a collision of resources occurs, this method decides which value to
* keep.
*/
static CollisionResult resolveValueCollision(Value* existing,
static CollisionResult ResolveValueCollision(Value* existing,
Value* incoming);
bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
bool AddResource(const ResourceNameRef& name, const ConfigDescription& config,
const StringPiece& product, std::unique_ptr<Value> value,
IDiagnostics* diag);
bool addResource(const ResourceNameRef& name, const ResourceId& resId,
bool AddResource(const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config, const StringPiece& product,
std::unique_ptr<Value> value, IDiagnostics* diag);
bool addFileReference(const ResourceNameRef& name,
bool AddFileReference(const ResourceNameRef& name,
const ConfigDescription& config, const Source& source,
const StringPiece& path, IDiagnostics* diag);
bool addFileReferenceAllowMangled(const ResourceNameRef& name,
bool AddFileReferenceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
const Source& source,
const StringPiece& path, io::IFile* file,
IDiagnostics* diag);
/**
* Same as addResource, but doesn't verify the validity of the name. This is
* Same as AddResource, but doesn't verify the validity of the name. This is
* used
* when loading resources from an existing binary resource table that may have
* mangled
* names.
*/
bool addResourceAllowMangled(const ResourceNameRef& name,
bool AddResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag);
bool addResourceAllowMangled(const ResourceNameRef& name,
bool AddResourceAllowMangled(const ResourceNameRef& name,
const ResourceId& id,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag);
bool setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
bool SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
const Symbol& symbol, IDiagnostics* diag);
bool setSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& resId, const Symbol& symbol,
IDiagnostics* diag);
bool SetSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol, IDiagnostics* diag);
struct SearchResult {
ResourceTablePackage* package;
@@ -245,21 +245,21 @@ class ResourceTable {
ResourceEntry* entry;
};
Maybe<SearchResult> findResource(const ResourceNameRef& name);
Maybe<SearchResult> FindResource(const ResourceNameRef& name);
/**
* The string pool used by this resource table. Values that reference strings
* must use
* this pool to create their strings.
*
* NOTE: `stringPool` must come before `packages` so that it is destroyed
* NOTE: `string_pool` must come before `packages` so that it is destroyed
* after.
* When `string pool` references are destroyed (as they will be when
* When `string_pool` references are destroyed (as they will be when
* `packages`
* is destroyed), they decrement a refCount, which would cause invalid
* memory access if the pool was already destroyed.
*/
StringPool stringPool;
StringPool string_pool;
/**
* The list of packages in this table, sorted alphabetically by package name.
@@ -273,31 +273,31 @@ class ResourceTable {
* represent the
* 'current' package before it is known to the ResourceTable.
*/
ResourceTablePackage* findPackage(const StringPiece& name);
ResourceTablePackage* FindPackage(const StringPiece& name);
ResourceTablePackage* findPackageById(uint8_t id);
ResourceTablePackage* FindPackageById(uint8_t id);
ResourceTablePackage* createPackage(const StringPiece& name,
ResourceTablePackage* CreatePackage(const StringPiece& name,
Maybe<uint8_t> id = {});
private:
ResourceTablePackage* findOrCreatePackage(const StringPiece& name);
ResourceTablePackage* FindOrCreatePackage(const StringPiece& name);
bool addResourceImpl(const ResourceNameRef& name, const ResourceId& resId,
bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config,
const StringPiece& product, std::unique_ptr<Value> value,
const char* validChars,
const CollisionResolverFunc& conflictResolver,
const char* valid_chars,
const CollisionResolverFunc& conflict_resolver,
IDiagnostics* diag);
bool addFileReferenceImpl(const ResourceNameRef& name,
bool AddFileReferenceImpl(const ResourceNameRef& name,
const ConfigDescription& config,
const Source& source, const StringPiece& path,
io::IFile* file, const char* validChars,
io::IFile* file, const char* valid_chars,
IDiagnostics* diag);
bool setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
const Symbol& symbol, const char* validChars,
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
const Symbol& symbol, const char* valid_chars,
IDiagnostics* diag);
DISALLOW_COPY_AND_ASSIGN(ResourceTable);

View File

@@ -29,108 +29,108 @@ namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
ResourceTable table;
EXPECT_FALSE(table.addResource(
test::parseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
test::getDiagnostics()));
EXPECT_FALSE(table.AddResource(
test::ParseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
test::GetDiagnostics()));
EXPECT_FALSE(table.addResource(
test::parseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
test::getDiagnostics()));
EXPECT_FALSE(table.AddResource(
test::ParseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
test::GetDiagnostics()));
}
TEST(ResourceTableTest, AddOneResource) {
ResourceTable table;
EXPECT_TRUE(table.addResource(
test::parseNameOrDie("android:attr/id"), ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 23u).build(),
test::getDiagnostics()));
EXPECT_TRUE(table.AddResource(
test::ParseNameOrDie("android:attr/id"), ConfigDescription{}, "",
test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(),
test::GetDiagnostics()));
ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/id"));
ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
}
TEST(ResourceTableTest, AddMultipleResources) {
ResourceTable table;
ConfigDescription config;
ConfigDescription languageConfig;
memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
ConfigDescription language_config;
memcpy(language_config.language, "pl", sizeof(language_config.language));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie("android:attr/layout_width"), config, "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
test::getDiagnostics()));
EXPECT_TRUE(table.AddResource(
test::ParseNameOrDie("android:attr/layout_width"), config, "",
test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build(),
test::GetDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie("android:attr/id"), config, "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
test::getDiagnostics()));
EXPECT_TRUE(table.AddResource(
test::ParseNameOrDie("android:attr/id"), config, "",
test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build(),
test::GetDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie("android:string/ok"), config, "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
test::getDiagnostics()));
EXPECT_TRUE(table.AddResource(
test::ParseNameOrDie("android:string/ok"), config, "",
test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build(),
test::GetDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie("android:string/ok"), languageConfig, "",
EXPECT_TRUE(table.AddResource(
test::ParseNameOrDie("android:string/ok"), language_config, "",
test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
.setSource("test/path/file.xml", 20u)
.build(),
test::getDiagnostics()));
.SetSource("test/path/file.xml", 20u)
.Build(),
test::GetDiagnostics()));
ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/layout_width"));
ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/id"));
ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:string/ok"));
ASSERT_NE(nullptr, test::getValueForConfig<BinaryPrimitive>(
&table, "android:string/ok", languageConfig));
ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/layout_width"));
ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:string/ok"));
ASSERT_NE(nullptr, test::GetValueForConfig<BinaryPrimitive>(
&table, "android:string/ok", language_config));
}
TEST(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
ASSERT_TRUE(table.addResource(
test::parseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
util::make_unique<Attribute>(true), test::getDiagnostics()));
ASSERT_TRUE(table.AddResource(
test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
util::make_unique<Attribute>(true), test::GetDiagnostics()));
Attribute* attr = test::getValue<Attribute>(&table, "android:attr/foo");
Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_TRUE(attr->isWeak());
EXPECT_TRUE(attr->IsWeak());
ASSERT_TRUE(table.addResource(
test::parseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
util::make_unique<Attribute>(false), test::getDiagnostics()));
ASSERT_TRUE(table.AddResource(
test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
util::make_unique<Attribute>(false), test::GetDiagnostics()));
attr = test::getValue<Attribute>(&table, "android:attr/foo");
attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_FALSE(attr->isWeak());
EXPECT_FALSE(attr->IsWeak());
}
TEST(ResourceTableTest, ProductVaryingValues) {
ResourceTable table;
EXPECT_TRUE(table.addResource(test::parseNameOrDie("android:string/foo"),
test::parseConfigOrDie("land"), "tablet",
EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
test::ParseConfigOrDie("land"), "tablet",
util::make_unique<Id>(),
test::getDiagnostics()));
EXPECT_TRUE(table.addResource(test::parseNameOrDie("android:string/foo"),
test::parseConfigOrDie("land"), "phone",
test::GetDiagnostics()));
EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
test::ParseConfigOrDie("land"), "phone",
util::make_unique<Id>(),
test::getDiagnostics()));
test::GetDiagnostics()));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
&table, "android:string/foo",
test::parseConfigOrDie("land"), "tablet"));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
test::ParseConfigOrDie("land"), "tablet"));
EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
&table, "android:string/foo",
test::parseConfigOrDie("land"), "phone"));
test::ParseConfigOrDie("land"), "phone"));
Maybe<ResourceTable::SearchResult> sr =
table.findResource(test::parseNameOrDie("android:string/foo"));
table.FindResource(test::ParseNameOrDie("android:string/foo"));
AAPT_ASSERT_TRUE(sr);
std::vector<ResourceConfigValue*> values =
sr.value().entry->findAllValues(test::parseConfigOrDie("land"));
sr.value().entry->findAllValues(test::ParseConfigOrDie("land"));
ASSERT_EQ(2u, values.size());
EXPECT_EQ(std::string("phone"), values[0]->product);
EXPECT_EQ(std::string("tablet"), values[1]->product);

View File

@@ -15,34 +15,36 @@
*/
#include "ResourceUtils.h"
#include <sstream>
#include "androidfw/ResourceTypes.h"
#include "NameMangler.h"
#include "SdkConstants.h"
#include "flatten/ResourceTypeExtensions.h"
#include "util/Files.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <sstream>
namespace aapt {
namespace ResourceUtils {
Maybe<ResourceName> toResourceName(
const android::ResTable::resource_name& nameIn) {
ResourceName nameOut;
if (!nameIn.package) {
Maybe<ResourceName> ToResourceName(
const android::ResTable::resource_name& name_in) {
ResourceName name_out;
if (!name_in.package) {
return {};
}
nameOut.package =
util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
name_out.package =
util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
const ResourceType* type;
if (nameIn.type) {
type = parseResourceType(
util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen)));
} else if (nameIn.type8) {
type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen));
if (name_in.type) {
type = ParseResourceType(
util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
} else if (name_in.type8) {
type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
} else {
return {};
}
@@ -51,46 +53,46 @@ Maybe<ResourceName> toResourceName(
return {};
}
nameOut.type = *type;
name_out.type = *type;
if (nameIn.name) {
nameOut.entry =
util::utf16ToUtf8(StringPiece16(nameIn.name, nameIn.nameLen));
} else if (nameIn.name8) {
nameOut.entry = StringPiece(nameIn.name8, nameIn.nameLen).toString();
if (name_in.name) {
name_out.entry =
util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
} else if (name_in.name8) {
name_out.entry = StringPiece(name_in.name8, name_in.nameLen).ToString();
} else {
return {};
}
return nameOut;
return name_out;
}
bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
StringPiece* outType, StringPiece* outEntry) {
bool hasPackageSeparator = false;
bool hasTypeSeparator = false;
bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
StringPiece* out_type, StringPiece* out_entry) {
bool has_package_separator = false;
bool has_type_separator = false;
const char* start = str.data();
const char* end = start + str.size();
const char* current = start;
while (current != end) {
if (outType->size() == 0 && *current == '/') {
hasTypeSeparator = true;
outType->assign(start, current - start);
if (out_type->size() == 0 && *current == '/') {
has_type_separator = true;
out_type->assign(start, current - start);
start = current + 1;
} else if (outPackage->size() == 0 && *current == ':') {
hasPackageSeparator = true;
outPackage->assign(start, current - start);
} else if (out_package->size() == 0 && *current == ':') {
has_package_separator = true;
out_package->assign(start, current - start);
start = current + 1;
}
current++;
}
outEntry->assign(start, end - start);
out_entry->assign(start, end - start);
return !(hasPackageSeparator && outPackage->empty()) &&
!(hasTypeSeparator && outType->empty());
return !(has_package_separator && out_package->empty()) &&
!(has_type_separator && out_type->empty());
}
bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef,
bool* outPrivate) {
bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
bool* out_private) {
if (str.empty()) {
return false;
}
@@ -105,13 +107,13 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef,
StringPiece package;
StringPiece type;
StringPiece entry;
if (!extractResourceName(str.substr(offset, str.size() - offset), &package,
if (!ExtractResourceName(str.substr(offset, str.size() - offset), &package,
&type, &entry)) {
return false;
}
const ResourceType* parsedType = parseResourceType(type);
if (!parsedType) {
const ResourceType* parsed_type = ParseResourceType(type);
if (!parsed_type) {
return false;
}
@@ -119,37 +121,37 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef,
return false;
}
if (outRef) {
outRef->package = package;
outRef->type = *parsedType;
outRef->entry = entry;
if (out_ref) {
out_ref->package = package;
out_ref->type = *parsed_type;
out_ref->entry = entry;
}
if (outPrivate) {
*outPrivate = priv;
if (out_private) {
*out_private = priv;
}
return true;
}
bool parseReference(const StringPiece& str, ResourceNameRef* outRef,
bool* outCreate, bool* outPrivate) {
StringPiece trimmedStr(util::trimWhitespace(str));
if (trimmedStr.empty()) {
bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
bool* out_create, bool* out_private) {
StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str.empty()) {
return false;
}
bool create = false;
bool priv = false;
if (trimmedStr.data()[0] == '@') {
if (trimmed_str.data()[0] == '@') {
size_t offset = 1;
if (trimmedStr.data()[1] == '+') {
if (trimmed_str.data()[1] == '+') {
create = true;
offset += 1;
}
ResourceNameRef name;
if (!parseResourceName(
trimmedStr.substr(offset, trimmedStr.size() - offset), &name,
if (!ParseResourceName(
trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
&priv)) {
return false;
}
@@ -162,37 +164,37 @@ bool parseReference(const StringPiece& str, ResourceNameRef* outRef,
return false;
}
if (outRef) {
*outRef = name;
if (out_ref) {
*out_ref = name;
}
if (outCreate) {
*outCreate = create;
if (out_create) {
*out_create = create;
}
if (outPrivate) {
*outPrivate = priv;
if (out_private) {
*out_private = priv;
}
return true;
}
return false;
}
bool isReference(const StringPiece& str) {
return parseReference(str, nullptr, nullptr, nullptr);
bool IsReference(const StringPiece& str) {
return ParseReference(str, nullptr, nullptr, nullptr);
}
bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
StringPiece trimmedStr(util::trimWhitespace(str));
if (trimmedStr.empty()) {
bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str.empty()) {
return false;
}
if (*trimmedStr.data() == '?') {
if (*trimmed_str.data() == '?') {
StringPiece package;
StringPiece type;
StringPiece entry;
if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
if (!ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1),
&package, &type, &entry)) {
return false;
}
@@ -205,18 +207,18 @@ bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
return false;
}
if (outRef) {
outRef->package = package;
outRef->type = ResourceType::kAttr;
outRef->entry = entry;
if (out_ref) {
out_ref->package = package;
out_ref->type = ResourceType::kAttr;
out_ref->entry = entry;
}
return true;
}
return false;
}
bool isAttributeReference(const StringPiece& str) {
return parseAttributeReference(str, nullptr);
bool IsAttributeReference(const StringPiece& str) {
return ParseAttributeReference(str, nullptr);
}
/*
@@ -227,65 +229,65 @@ bool isAttributeReference(const StringPiece& str) {
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
Maybe<Reference> parseStyleParentReference(const StringPiece& str,
std::string* outError) {
Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
std::string* out_error) {
if (str.empty()) {
return {};
}
StringPiece name = str;
bool hasLeadingIdentifiers = false;
bool privateRef = false;
bool has_leading_identifiers = false;
bool private_ref = false;
// Skip over these identifiers. A style's parent is a normal reference.
if (name.data()[0] == '@' || name.data()[0] == '?') {
hasLeadingIdentifiers = true;
has_leading_identifiers = true;
name = name.substr(1, name.size() - 1);
}
if (name.data()[0] == '*') {
privateRef = true;
private_ref = true;
name = name.substr(1, name.size() - 1);
}
ResourceNameRef ref;
ref.type = ResourceType::kStyle;
StringPiece typeStr;
extractResourceName(name, &ref.package, &typeStr, &ref.entry);
if (!typeStr.empty()) {
StringPiece type_str;
ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
if (!type_str.empty()) {
// If we have a type, make sure it is a Style.
const ResourceType* parsedType = parseResourceType(typeStr);
if (!parsedType || *parsedType != ResourceType::kStyle) {
const ResourceType* parsed_type = ParseResourceType(type_str);
if (!parsed_type || *parsed_type != ResourceType::kStyle) {
std::stringstream err;
err << "invalid resource type '" << typeStr << "' for parent of style";
*outError = err.str();
err << "invalid resource type '" << type_str << "' for parent of style";
*out_error = err.str();
return {};
}
}
if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
std::stringstream err;
err << "invalid parent reference '" << str << "'";
*outError = err.str();
*out_error = err.str();
return {};
}
Reference result(ref);
result.privateReference = privateRef;
result.private_reference = private_ref;
return result;
}
Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
StringPiece trimmedStr = util::trimWhitespace(str);
const char* start = trimmedStr.data();
const char* const end = start + trimmedStr.size();
Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
StringPiece trimmed_str = util::TrimWhitespace(str);
const char* start = trimmed_str.data();
const char* const end = start + trimmed_str.size();
const char* p = start;
Reference ref;
if (p != end && *p == '*') {
ref.privateReference = true;
ref.private_reference = true;
start++;
p++;
}
@@ -302,38 +304,38 @@ Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
}
ref.name =
ResourceName(package.toString(), ResourceType::kAttr,
name.empty() ? trimmedStr.toString() : name.toString());
ResourceName(package.ToString(), ResourceType::kAttr,
name.empty() ? trimmed_str.ToString() : name.ToString());
return Maybe<Reference>(std::move(ref));
}
std::unique_ptr<Reference> tryParseReference(const StringPiece& str,
bool* outCreate) {
std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
bool* out_create) {
ResourceNameRef ref;
bool privateRef = false;
if (parseReference(str, &ref, outCreate, &privateRef)) {
bool private_ref = false;
if (ParseReference(str, &ref, out_create, &private_ref)) {
std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
value->privateReference = privateRef;
value->private_reference = private_ref;
return value;
}
if (parseAttributeReference(str, &ref)) {
if (outCreate) {
*outCreate = false;
if (ParseAttributeReference(str, &ref)) {
if (out_create) {
*out_create = false;
}
return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
}
return {};
}
std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
StringPiece trimmedStr(util::trimWhitespace(str));
std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
android::Res_value value = {};
if (trimmedStr == "@null") {
if (trimmed_str == "@null") {
// TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
// Instead we set the data type to TYPE_REFERENCE with a value of 0.
value.dataType = android::Res_value::TYPE_REFERENCE;
} else if (trimmedStr == "@empty") {
} else if (trimmed_str == "@empty") {
// TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
value.dataType = android::Res_value::TYPE_NULL;
value.data = android::Res_value::DATA_NULL_EMPTY;
@@ -343,14 +345,14 @@ std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
return util::make_unique<BinaryPrimitive>(value);
}
std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
const StringPiece& str) {
StringPiece trimmedStr(util::trimWhitespace(str));
for (const Attribute::Symbol& symbol : enumAttr->symbols) {
StringPiece trimmed_str(util::TrimWhitespace(str));
for (const Attribute::Symbol& symbol : enum_attr->symbols) {
// Enum symbols are stored as @package:id/symbol resources,
// so we need to match against the 'entry' part of the identifier.
const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
if (trimmedStr == enumSymbolResourceName.entry) {
const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
if (trimmed_str == enum_symbol_resource_name.entry) {
android::Res_value value = {};
value.dataType = android::Res_value::TYPE_INT_DEC;
value.data = symbol.value;
@@ -360,40 +362,41 @@ std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
return {};
}
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
const StringPiece& str) {
android::Res_value flags = {};
flags.dataType = android::Res_value::TYPE_INT_HEX;
flags.data = 0u;
if (util::trimWhitespace(str).empty()) {
if (util::TrimWhitespace(str).empty()) {
// Empty string is a valid flag (0).
return util::make_unique<BinaryPrimitive>(flags);
}
for (StringPiece part : util::tokenize(str, '|')) {
StringPiece trimmedPart = util::trimWhitespace(part);
for (StringPiece part : util::Tokenize(str, '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
bool flagSet = false;
for (const Attribute::Symbol& symbol : flagAttr->symbols) {
bool flag_set = false;
for (const Attribute::Symbol& symbol : flag_attr->symbols) {
// Flag symbols are stored as @package:id/symbol resources,
// so we need to match against the 'entry' part of the identifier.
const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
if (trimmedPart == flagSymbolResourceName.entry) {
const ResourceName& flag_symbol_resource_name =
symbol.symbol.name.value();
if (trimmed_part == flag_symbol_resource_name.entry) {
flags.data |= symbol.value;
flagSet = true;
flag_set = true;
break;
}
}
if (!flagSet) {
if (!flag_set) {
return {};
}
}
return util::make_unique<BinaryPrimitive>(flags);
}
static uint32_t parseHex(char c, bool* outError) {
static uint32_t ParseHex(char c, bool* out_error) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
@@ -401,15 +404,15 @@ static uint32_t parseHex(char c, bool* outError) {
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 0xa;
} else {
*outError = true;
*out_error = true;
return 0xffffffffu;
}
}
std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
StringPiece colorStr(util::trimWhitespace(str));
const char* start = colorStr.data();
const size_t len = colorStr.size();
std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
StringPiece color_str(util::TrimWhitespace(str));
const char* start = color_str.data();
const size_t len = color_str.size();
if (len == 0 || start[0] != '#') {
return {};
}
@@ -419,41 +422,41 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
if (len == 4) {
value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
value.data = 0xff000000u;
value.data |= parseHex(start[1], &error) << 20;
value.data |= parseHex(start[1], &error) << 16;
value.data |= parseHex(start[2], &error) << 12;
value.data |= parseHex(start[2], &error) << 8;
value.data |= parseHex(start[3], &error) << 4;
value.data |= parseHex(start[3], &error);
value.data |= ParseHex(start[1], &error) << 20;
value.data |= ParseHex(start[1], &error) << 16;
value.data |= ParseHex(start[2], &error) << 12;
value.data |= ParseHex(start[2], &error) << 8;
value.data |= ParseHex(start[3], &error) << 4;
value.data |= ParseHex(start[3], &error);
} else if (len == 5) {
value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
value.data |= parseHex(start[1], &error) << 28;
value.data |= parseHex(start[1], &error) << 24;
value.data |= parseHex(start[2], &error) << 20;
value.data |= parseHex(start[2], &error) << 16;
value.data |= parseHex(start[3], &error) << 12;
value.data |= parseHex(start[3], &error) << 8;
value.data |= parseHex(start[4], &error) << 4;
value.data |= parseHex(start[4], &error);
value.data |= ParseHex(start[1], &error) << 28;
value.data |= ParseHex(start[1], &error) << 24;
value.data |= ParseHex(start[2], &error) << 20;
value.data |= ParseHex(start[2], &error) << 16;
value.data |= ParseHex(start[3], &error) << 12;
value.data |= ParseHex(start[3], &error) << 8;
value.data |= ParseHex(start[4], &error) << 4;
value.data |= ParseHex(start[4], &error);
} else if (len == 7) {
value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
value.data = 0xff000000u;
value.data |= parseHex(start[1], &error) << 20;
value.data |= parseHex(start[2], &error) << 16;
value.data |= parseHex(start[3], &error) << 12;
value.data |= parseHex(start[4], &error) << 8;
value.data |= parseHex(start[5], &error) << 4;
value.data |= parseHex(start[6], &error);
value.data |= ParseHex(start[1], &error) << 20;
value.data |= ParseHex(start[2], &error) << 16;
value.data |= ParseHex(start[3], &error) << 12;
value.data |= ParseHex(start[4], &error) << 8;
value.data |= ParseHex(start[5], &error) << 4;
value.data |= ParseHex(start[6], &error);
} else if (len == 9) {
value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
value.data |= parseHex(start[1], &error) << 28;
value.data |= parseHex(start[2], &error) << 24;
value.data |= parseHex(start[3], &error) << 20;
value.data |= parseHex(start[4], &error) << 16;
value.data |= parseHex(start[5], &error) << 12;
value.data |= parseHex(start[6], &error) << 8;
value.data |= parseHex(start[7], &error) << 4;
value.data |= parseHex(start[8], &error);
value.data |= ParseHex(start[1], &error) << 28;
value.data |= ParseHex(start[2], &error) << 24;
value.data |= ParseHex(start[3], &error) << 20;
value.data |= ParseHex(start[4], &error) << 16;
value.data |= ParseHex(start[5], &error) << 12;
value.data |= ParseHex(start[6], &error) << 8;
value.data |= ParseHex(start[7], &error) << 4;
value.data |= ParseHex(start[8], &error);
} else {
return {};
}
@@ -461,19 +464,19 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
: util::make_unique<BinaryPrimitive>(value);
}
Maybe<bool> parseBool(const StringPiece& str) {
StringPiece trimmedStr(util::trimWhitespace(str));
if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
Maybe<bool> ParseBool(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
return Maybe<bool>(true);
} else if (trimmedStr == "false" || trimmedStr == "FALSE" ||
trimmedStr == "False") {
} else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
trimmed_str == "False") {
return Maybe<bool>(false);
}
return {};
}
Maybe<uint32_t> parseInt(const StringPiece& str) {
std::u16string str16 = util::utf8ToUtf16(str);
Maybe<uint32_t> ParseInt(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(str);
android::Res_value value;
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
return value.data;
@@ -481,15 +484,15 @@ Maybe<uint32_t> parseInt(const StringPiece& str) {
return {};
}
Maybe<ResourceId> parseResourceId(const StringPiece& str) {
StringPiece trimmedStr(util::trimWhitespace(str));
Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::utf8ToUtf16(trimmedStr);
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
android::Res_value value;
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
if (value.dataType == android::Res_value::TYPE_INT_HEX) {
ResourceId id(value.data);
if (id.isValid()) {
if (id.is_valid()) {
return id;
}
}
@@ -497,29 +500,29 @@ Maybe<ResourceId> parseResourceId(const StringPiece& str) {
return {};
}
Maybe<int> parseSdkVersion(const StringPiece& str) {
StringPiece trimmedStr(util::trimWhitespace(str));
Maybe<int> ParseSdkVersion(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::utf8ToUtf16(trimmedStr);
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
android::Res_value value;
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
return static_cast<int>(value.data);
}
// Try parsing the code name.
std::pair<StringPiece, int> entry = getDevelopmentSdkCodeNameAndVersion();
if (entry.first == trimmedStr) {
std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
if (entry.first == trimmed_str) {
return entry.second;
}
return {};
}
std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) {
if (Maybe<bool> maybeResult = parseBool(str)) {
std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
if (Maybe<bool> maybe_result = ParseBool(str)) {
android::Res_value value = {};
value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
if (maybeResult.value()) {
if (maybe_result.value()) {
value.data = 0xffffffffu;
} else {
value.data = 0;
@@ -529,8 +532,8 @@ std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) {
return {};
}
std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) {
std::u16string str16 = util::utf8ToUtf16(str);
std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(str);
android::Res_value value;
if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
return {};
@@ -538,8 +541,8 @@ std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) {
return util::make_unique<BinaryPrimitive>(value);
}
std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
std::u16string str16 = util::utf8ToUtf16(str);
std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(str);
android::Res_value value;
if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
return {};
@@ -547,7 +550,7 @@ std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
return util::make_unique<BinaryPrimitive>(value);
}
uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
switch (type) {
case android::Res_value::TYPE_NULL:
case android::Res_value::TYPE_REFERENCE:
@@ -587,57 +590,57 @@ uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
};
}
std::unique_ptr<Item> tryParseItemForAttribute(
const StringPiece& value, uint32_t typeMask,
const std::function<void(const ResourceName&)>& onCreateReference) {
std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
if (nullOrEmpty) {
return std::move(nullOrEmpty);
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, uint32_t type_mask,
const std::function<void(const ResourceName&)>& on_create_reference) {
std::unique_ptr<BinaryPrimitive> null_or_empty = TryParseNullOrEmpty(value);
if (null_or_empty) {
return std::move(null_or_empty);
}
bool create = false;
std::unique_ptr<Reference> reference = tryParseReference(value, &create);
std::unique_ptr<Reference> reference = TryParseReference(value, &create);
if (reference) {
if (create && onCreateReference) {
onCreateReference(reference->name.value());
if (create && on_create_reference) {
on_create_reference(reference->name.value());
}
return std::move(reference);
}
if (typeMask & android::ResTable_map::TYPE_COLOR) {
if (type_mask & android::ResTable_map::TYPE_COLOR) {
// Try parsing this as a color.
std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
std::unique_ptr<BinaryPrimitive> color = TryParseColor(value);
if (color) {
return std::move(color);
}
}
if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
// Try parsing this as a boolean.
std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
std::unique_ptr<BinaryPrimitive> boolean = TryParseBool(value);
if (boolean) {
return std::move(boolean);
}
}
if (typeMask & android::ResTable_map::TYPE_INTEGER) {
if (type_mask & android::ResTable_map::TYPE_INTEGER) {
// Try parsing this as an integer.
std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
std::unique_ptr<BinaryPrimitive> integer = TryParseInt(value);
if (integer) {
return std::move(integer);
}
}
const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
android::ResTable_map::TYPE_DIMENSION |
android::ResTable_map::TYPE_FRACTION;
if (typeMask & floatMask) {
const uint32_t float_mask = android::ResTable_map::TYPE_FLOAT |
android::ResTable_map::TYPE_DIMENSION |
android::ResTable_map::TYPE_FRACTION;
if (type_mask & float_mask) {
// Try parsing this as a float.
std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
if (floatingPoint) {
if (typeMask &
androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
return std::move(floatingPoint);
std::unique_ptr<BinaryPrimitive> floating_point = TryParseFloat(value);
if (floating_point) {
if (type_mask &
AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
return std::move(floating_point);
}
}
}
@@ -648,49 +651,49 @@ std::unique_ptr<Item> tryParseItemForAttribute(
* We successively try to parse the string as a resource type that the Attribute
* allows.
*/
std::unique_ptr<Item> tryParseItemForAttribute(
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& str, const Attribute* attr,
const std::function<void(const ResourceName&)>& onCreateReference) {
const uint32_t typeMask = attr->typeMask;
const std::function<void(const ResourceName&)>& on_create_reference) {
const uint32_t type_mask = attr->type_mask;
std::unique_ptr<Item> value =
tryParseItemForAttribute(str, typeMask, onCreateReference);
TryParseItemForAttribute(str, type_mask, on_create_reference);
if (value) {
return value;
}
if (typeMask & android::ResTable_map::TYPE_ENUM) {
if (type_mask & android::ResTable_map::TYPE_ENUM) {
// Try parsing this as an enum.
std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
if (enumValue) {
return std::move(enumValue);
std::unique_ptr<BinaryPrimitive> enum_value = TryParseEnumSymbol(attr, str);
if (enum_value) {
return std::move(enum_value);
}
}
if (typeMask & android::ResTable_map::TYPE_FLAGS) {
if (type_mask & android::ResTable_map::TYPE_FLAGS) {
// Try parsing this as a flag.
std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
if (flagValue) {
return std::move(flagValue);
std::unique_ptr<BinaryPrimitive> flag_value = TryParseFlagSymbol(attr, str);
if (flag_value) {
return std::move(flag_value);
}
}
return {};
}
std::string buildResourceFileName(const ResourceFile& resFile,
std::string BuildResourceFileName(const ResourceFile& res_file,
const NameMangler* mangler) {
std::stringstream out;
out << "res/" << resFile.name.type;
if (resFile.config != ConfigDescription{}) {
out << "-" << resFile.config;
out << "res/" << res_file.name.type;
if (res_file.config != ConfigDescription{}) {
out << "-" << res_file.config;
}
out << "/";
if (mangler && mangler->shouldMangle(resFile.name.package)) {
out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
if (mangler && mangler->ShouldMangle(res_file.name.package)) {
out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
} else {
out << resFile.name.entry;
out << res_file.name.entry;
}
out << file::getExtension(resFile.source.path);
out << file::GetExtension(res_file.source.path);
return out.str();
}

View File

@@ -17,14 +17,14 @@
#ifndef AAPT_RESOURCEUTILS_H
#define AAPT_RESOURCEUTILS_H
#include <functional>
#include <memory>
#include "NameMangler.h"
#include "Resource.h"
#include "ResourceValues.h"
#include "util/StringPiece.h"
#include <functional>
#include <memory>
namespace aapt {
namespace ResourceUtils {
@@ -37,76 +37,75 @@ namespace ResourceUtils {
* individual extracted piece to verify that the pieces are valid.
* Returns false if there was no package but a ':' was present.
*/
bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
StringPiece* outType, StringPiece* outEntry);
bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
StringPiece* out_type, StringPiece* out_entry);
/**
* Returns true if the string was parsed as a resource name
* ([*][package:]type/name), with
* `outResource` set to the parsed resource name and `outPrivate` set to true if
* a '*' prefix
* was present.
* `out_resource` set to the parsed resource name and `out_private` set to true
* if a '*' prefix was present.
*/
bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource,
bool* outPrivate = nullptr);
bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_resource,
bool* out_private = nullptr);
/*
* Returns true if the string was parsed as a reference
* (@[+][package:]type/name), with
* `outReference` set to the parsed reference.
* `out_reference` set to the parsed reference.
*
* If '+' was present in the reference, `outCreate` is set to true.
* If '*' was present in the reference, `outPrivate` is set to true.
* If '+' was present in the reference, `out_create` is set to true.
* If '*' was present in the reference, `out_private` is set to true.
*/
bool parseReference(const StringPiece& str, ResourceNameRef* outReference,
bool* outCreate = nullptr, bool* outPrivate = nullptr);
bool ParseReference(const StringPiece& str, ResourceNameRef* out_reference,
bool* out_create = nullptr, bool* out_private = nullptr);
/*
* Returns true if the string is in the form of a resource reference
* (@[+][package:]type/name).
*/
bool isReference(const StringPiece& str);
bool IsReference(const StringPiece& str);
/*
* Returns true if the string was parsed as an attribute reference
* (?[package:][type/]name),
* with `outReference` set to the parsed reference.
* with `out_reference` set to the parsed reference.
*/
bool parseAttributeReference(const StringPiece& str,
ResourceNameRef* outReference);
bool ParseAttributeReference(const StringPiece& str,
ResourceNameRef* out_reference);
/**
* Returns true if the string is in the form of an attribute
* reference(?[package:][type/]name).
*/
bool isAttributeReference(const StringPiece& str);
bool IsAttributeReference(const StringPiece& str);
/**
* Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
*/
Maybe<ResourceName> toResourceName(
Maybe<ResourceName> ToResourceName(
const android::ResTable::resource_name& name);
/**
* Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
* false, or False.
*/
Maybe<bool> parseBool(const StringPiece& str);
Maybe<bool> ParseBool(const StringPiece& str);
/**
* Returns a uint32_t if the string is an integer.
*/
Maybe<uint32_t> parseInt(const StringPiece& str);
Maybe<uint32_t> ParseInt(const StringPiece& str);
/**
* Returns an ID if it the string represented a valid ID.
*/
Maybe<ResourceId> parseResourceId(const StringPiece& str);
Maybe<ResourceId> ParseResourceId(const StringPiece& str);
/**
* Parses an SDK version, which can be an integer, or a letter from A-Z.
*/
Maybe<int> parseSdkVersion(const StringPiece& str);
Maybe<int> ParseSdkVersion(const StringPiece& str);
/*
* Returns a Reference, or None Maybe instance if the string `str` was parsed as
@@ -119,8 +118,8 @@ Maybe<int> parseSdkVersion(const StringPiece& str);
* ?[package:]style/<entry> or
* <package>:[style/]<entry>
*/
Maybe<Reference> parseStyleParentReference(const StringPiece& str,
std::string* outError);
Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
std::string* out_error);
/*
* Returns a Reference if the string `str` was parsed as a valid XML attribute
@@ -129,7 +128,7 @@ Maybe<Reference> parseStyleParentReference(const StringPiece& str,
*
* package:entry
*/
Maybe<Reference> parseXmlAttributeName(const StringPiece& str);
Maybe<Reference> ParseXmlAttributeName(const StringPiece& str);
/*
* Returns a Reference object if the string was parsed as a resource or
@@ -138,73 +137,68 @@ Maybe<Reference> parseXmlAttributeName(const StringPiece& str);
* if
* the '+' was present in the string.
*/
std::unique_ptr<Reference> tryParseReference(const StringPiece& str,
bool* outCreate = nullptr);
std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
bool* out_create = nullptr);
/*
* Returns a BinaryPrimitve object representing @null or @empty if the string
* was parsed
* as one.
* was parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str);
std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a color if the string was parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str);
std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a boolean if the string was
* parsed
* as one.
* parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str);
std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing an integer if the string was
* parsed
* as one.
* parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str);
std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a floating point number
* (float, dimension, etc) if the string was parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str);
std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing an enum symbol if the string was
* parsed
* as one.
* parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a flag symbol if the string was
* parsed
* as one.
* parsed as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr,
std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* enum_attr,
const StringPiece& str);
/*
* Try to convert a string to an Item for the given attribute. The attribute
* will
* restrict what values the string can be converted to.
* The callback function onCreateReference is called when the parsed item is a
* The callback function on_create_reference is called when the parsed item is a
* reference to an ID that must be created (@+id/foo).
*/
std::unique_ptr<Item> tryParseItemForAttribute(
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, const Attribute* attr,
const std::function<void(const ResourceName&)>& onCreateReference = {});
const std::function<void(const ResourceName&)>& on_create_reference = {});
std::unique_ptr<Item> tryParseItemForAttribute(
const StringPiece& value, uint32_t typeMask,
const std::function<void(const ResourceName&)>& onCreateReference = {});
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, uint32_t type_mask,
const std::function<void(const ResourceName&)>& on_create_reference = {});
uint32_t androidTypeToAttributeTypeMask(uint16_t type);
uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
/**
* Returns a string path suitable for use within an APK. The path will look
@@ -216,8 +210,8 @@ uint32_t androidTypeToAttributeTypeMask(uint16_t type);
* the package
* requires mangling.
*/
std::string buildResourceFileName(const ResourceFile& resFile,
const NameMangler* mangler);
std::string BuildResourceFileName(const ResourceFile& res_file,
const NameMangler* mangler = nullptr);
} // namespace ResourceUtils
} // namespace aapt

View File

@@ -15,128 +15,129 @@
*/
#include "ResourceUtils.h"
#include "Resource.h"
#include "test/Test.h"
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("true"));
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("TRUE"));
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("True"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("false"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("FALSE"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("False"));
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("true"));
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("TRUE"));
EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("True"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("false"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("FALSE"));
EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("False"));
}
TEST(ResourceUtilsTest, ParseResourceName) {
ResourceNameRef actual;
bool actualPriv = false;
EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual,
&actualPriv));
bool actual_priv = false;
EXPECT_TRUE(ResourceUtils::ParseResourceName("android:color/foo", &actual,
&actual_priv));
EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
EXPECT_FALSE(actualPriv);
EXPECT_FALSE(actual_priv);
EXPECT_TRUE(
ResourceUtils::parseResourceName("color/foo", &actual, &actualPriv));
ResourceUtils::ParseResourceName("color/foo", &actual, &actual_priv));
EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
EXPECT_FALSE(actualPriv);
EXPECT_FALSE(actual_priv);
EXPECT_TRUE(ResourceUtils::parseResourceName("*android:color/foo", &actual,
&actualPriv));
EXPECT_TRUE(ResourceUtils::ParseResourceName("*android:color/foo", &actual,
&actual_priv));
EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
EXPECT_TRUE(actualPriv);
EXPECT_TRUE(actual_priv);
EXPECT_FALSE(
ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
ResourceUtils::ParseResourceName(StringPiece(), &actual, &actual_priv));
}
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
ResourceNameRef expected({}, ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create,
&privateRef));
bool private_ref = false;
EXPECT_TRUE(ResourceUtils::ParseReference("@color/foo", &actual, &create,
&private_ref));
EXPECT_EQ(expected, actual);
EXPECT_FALSE(create);
EXPECT_FALSE(privateRef);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
ResourceNameRef expected("android", ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual,
&create, &privateRef));
bool private_ref = false;
EXPECT_TRUE(ResourceUtils::ParseReference("@android:color/foo", &actual,
&create, &private_ref));
EXPECT_EQ(expected, actual);
EXPECT_FALSE(create);
EXPECT_FALSE(privateRef);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
ResourceNameRef expected("android", ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t",
&actual, &create, &privateRef));
bool private_ref = false;
EXPECT_TRUE(ResourceUtils::ParseReference("\t @android:color/foo\n \n\t",
&actual, &create, &private_ref));
EXPECT_EQ(expected, actual);
EXPECT_FALSE(create);
EXPECT_FALSE(privateRef);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
ResourceNameRef expected("android", ResourceType::kId, "foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual,
&create, &privateRef));
bool private_ref = false;
EXPECT_TRUE(ResourceUtils::ParseReference("@+android:id/foo", &actual,
&create, &private_ref));
EXPECT_EQ(expected, actual);
EXPECT_TRUE(create);
EXPECT_FALSE(privateRef);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParsePrivateReference) {
ResourceNameRef expected("android", ResourceType::kId, "foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual,
&create, &privateRef));
bool private_ref = false;
EXPECT_TRUE(ResourceUtils::ParseReference("@*android:id/foo", &actual,
&create, &private_ref));
EXPECT_EQ(expected, actual);
EXPECT_FALSE(create);
EXPECT_TRUE(privateRef);
EXPECT_TRUE(private_ref);
}
TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
bool create = false;
bool privateRef = false;
bool private_ref = false;
ResourceNameRef actual;
EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual,
&create, &privateRef));
EXPECT_FALSE(ResourceUtils::ParseReference("@+android:color/foo", &actual,
&create, &private_ref));
}
TEST(ResourceUtilsTest, ParseAttributeReferences) {
EXPECT_TRUE(ResourceUtils::isAttributeReference("?android"));
EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:foo"));
EXPECT_TRUE(ResourceUtils::isAttributeReference("?attr/foo"));
EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:attr/foo"));
EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android"));
EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android:foo"));
EXPECT_TRUE(ResourceUtils::IsAttributeReference("?attr/foo"));
EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android:attr/foo"));
}
TEST(ResourceUtilsTest, FailParseIncompleteReference) {
EXPECT_FALSE(ResourceUtils::isAttributeReference("?style/foo"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:style/foo"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:attr/"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/foo"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/foo"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?attr/"));
EXPECT_FALSE(ResourceUtils::isAttributeReference("?/foo"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?style/foo"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:style/foo"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:attr/"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:attr/"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:attr/foo"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:/"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:/foo"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?attr/"));
EXPECT_FALSE(ResourceUtils::IsAttributeReference("?/foo"));
}
TEST(ResourceUtilsTest, ParseStyleParentReference) {
@@ -144,56 +145,58 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) {
"foo");
const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
std::string errStr;
std::string err_str;
Maybe<Reference> ref =
ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr);
ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
ref = ResourceUtils::parseStyleParentReference("@style/foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kStyleFooName);
ref = ResourceUtils::parseStyleParentReference("?android:style/foo", &errStr);
ref =
ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
ref = ResourceUtils::parseStyleParentReference("?style/foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kStyleFooName);
ref = ResourceUtils::parseStyleParentReference("android:style/foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
ref = ResourceUtils::parseStyleParentReference("android:foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
ref = ResourceUtils::parseStyleParentReference("@android:foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
ref = ResourceUtils::parseStyleParentReference("foo", &errStr);
ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kStyleFooName);
ref = ResourceUtils::parseStyleParentReference("*android:style/foo", &errStr);
ref =
ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
EXPECT_TRUE(ref.value().privateReference);
EXPECT_TRUE(ref.value().private_reference);
}
TEST(ResourceUtilsTest, ParseEmptyFlag) {
std::unique_ptr<Attribute> attr =
test::AttributeBuilder(false)
.setTypeMask(android::ResTable_map::TYPE_FLAGS)
.addItem("one", 0x01)
.addItem("two", 0x02)
.build();
.SetTypeMask(android::ResTable_map::TYPE_FLAGS)
.AddItem("one", 0x01)
.AddItem("two", 0x02)
.Build();
std::unique_ptr<BinaryPrimitive> result =
ResourceUtils::tryParseFlagSymbol(attr.get(), "");
ResourceUtils::TryParseFlagSymbol(attr.get(), "");
ASSERT_NE(nullptr, result);
EXPECT_EQ(0u, result->value.data);
}

View File

@@ -15,94 +15,95 @@
*/
#include "ResourceValues.h"
#include <algorithm>
#include <limits>
#include <set>
#include "androidfw/ResourceTypes.h"
#include "Resource.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <algorithm>
#include <limits>
#include <set>
namespace aapt {
template <typename Derived>
void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
visitor->visit(static_cast<Derived*>(this));
void BaseValue<Derived>::Accept(RawValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
template <typename Derived>
void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
visitor->visit(static_cast<Derived*>(this));
void BaseItem<Derived>::Accept(RawValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
RawString::RawString(const StringPool::Ref& ref) : value(ref) {}
bool RawString::equals(const Value* value) const {
const RawString* other = valueCast<RawString>(value);
bool RawString::Equals(const Value* value) const {
const RawString* other = ValueCast<RawString>(value);
if (!other) {
return false;
}
return *this->value == *other->value;
}
RawString* RawString::clone(StringPool* newPool) const {
RawString* rs = new RawString(newPool->makeRef(*value));
rs->mComment = mComment;
rs->mSource = mSource;
RawString* RawString::Clone(StringPool* new_pool) const {
RawString* rs = new RawString(new_pool->MakeRef(*value));
rs->comment_ = comment_;
rs->source_ = source_;
return rs;
}
bool RawString::flatten(android::Res_value* outValue) const {
outValue->dataType = android::Res_value::TYPE_STRING;
outValue->data =
util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
bool RawString::Flatten(android::Res_value* out_value) const {
out_value->dataType = android::Res_value::TYPE_STRING;
out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
return true;
}
void RawString::print(std::ostream* out) const {
void RawString::Print(std::ostream* out) const {
*out << "(raw string) " << *value;
}
Reference::Reference() : referenceType(Type::kResource) {}
Reference::Reference() : reference_type(Type::kResource) {}
Reference::Reference(const ResourceNameRef& n, Type t)
: name(n.toResourceName()), referenceType(t) {}
: name(n.ToResourceName()), reference_type(t) {}
Reference::Reference(const ResourceId& i, Type type)
: id(i), referenceType(type) {}
: id(i), reference_type(type) {}
Reference::Reference(const ResourceNameRef& n, const ResourceId& i)
: name(n.toResourceName()), id(i), referenceType(Type::kResource) {}
: name(n.ToResourceName()), id(i), reference_type(Type::kResource) {}
bool Reference::equals(const Value* value) const {
const Reference* other = valueCast<Reference>(value);
bool Reference::Equals(const Value* value) const {
const Reference* other = ValueCast<Reference>(value);
if (!other) {
return false;
}
return referenceType == other->referenceType &&
privateReference == other->privateReference && id == other->id &&
return reference_type == other->reference_type &&
private_reference == other->private_reference && id == other->id &&
name == other->name;
}
bool Reference::flatten(android::Res_value* outValue) const {
outValue->dataType = (referenceType == Reference::Type::kResource)
? android::Res_value::TYPE_REFERENCE
: android::Res_value::TYPE_ATTRIBUTE;
outValue->data = util::hostToDevice32(id ? id.value().id : 0);
bool Reference::Flatten(android::Res_value* out_value) const {
out_value->dataType = (reference_type == Reference::Type::kResource)
? android::Res_value::TYPE_REFERENCE
: android::Res_value::TYPE_ATTRIBUTE;
out_value->data = util::HostToDevice32(id ? id.value().id : 0);
return true;
}
Reference* Reference::clone(StringPool* /*newPool*/) const {
Reference* Reference::Clone(StringPool* /*new_pool*/) const {
return new Reference(*this);
}
void Reference::print(std::ostream* out) const {
void Reference::Print(std::ostream* out) const {
*out << "(reference) ";
if (referenceType == Reference::Type::kResource) {
if (reference_type == Reference::Type::kResource) {
*out << "@";
if (privateReference) {
if (private_reference) {
*out << "*";
}
} else {
@@ -118,128 +119,127 @@ void Reference::print(std::ostream* out) const {
}
}
bool Id::equals(const Value* value) const {
return valueCast<Id>(value) != nullptr;
bool Id::Equals(const Value* value) const {
return ValueCast<Id>(value) != nullptr;
}
bool Id::flatten(android::Res_value* out) const {
bool Id::Flatten(android::Res_value* out) const {
out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
out->data = util::hostToDevice32(0);
out->data = util::HostToDevice32(0);
return true;
}
Id* Id::clone(StringPool* /*newPool*/) const { return new Id(*this); }
Id* Id::Clone(StringPool* /*new_pool*/) const { return new Id(*this); }
void Id::print(std::ostream* out) const { *out << "(id)"; }
void Id::Print(std::ostream* out) const { *out << "(id)"; }
String::String(const StringPool::Ref& ref) : value(ref) {}
bool String::equals(const Value* value) const {
const String* other = valueCast<String>(value);
bool String::Equals(const Value* value) const {
const String* other = ValueCast<String>(value);
if (!other) {
return false;
}
return *this->value == *other->value;
}
bool String::flatten(android::Res_value* outValue) const {
bool String::Flatten(android::Res_value* out_value) const {
// Verify that our StringPool index is within encode-able limits.
if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
if (value.index() > std::numeric_limits<uint32_t>::max()) {
return false;
}
outValue->dataType = android::Res_value::TYPE_STRING;
outValue->data =
util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
out_value->dataType = android::Res_value::TYPE_STRING;
out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
return true;
}
String* String::clone(StringPool* newPool) const {
String* str = new String(newPool->makeRef(*value));
str->mComment = mComment;
str->mSource = mSource;
String* String::Clone(StringPool* new_pool) const {
String* str = new String(new_pool->MakeRef(*value));
str->comment_ = comment_;
str->source_ = source_;
return str;
}
void String::print(std::ostream* out) const {
void String::Print(std::ostream* out) const {
*out << "(string) \"" << *value << "\"";
}
StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {}
bool StyledString::equals(const Value* value) const {
const StyledString* other = valueCast<StyledString>(value);
bool StyledString::Equals(const Value* value) const {
const StyledString* other = ValueCast<StyledString>(value);
if (!other) {
return false;
}
if (*this->value->str == *other->value->str) {
const std::vector<StringPool::Span>& spansA = this->value->spans;
const std::vector<StringPool::Span>& spansB = other->value->spans;
const std::vector<StringPool::Span>& spans_a = this->value->spans;
const std::vector<StringPool::Span>& spans_b = other->value->spans;
return std::equal(
spansA.begin(), spansA.end(), spansB.begin(),
spans_a.begin(), spans_a.end(), spans_b.begin(),
[](const StringPool::Span& a, const StringPool::Span& b) -> bool {
return *a.name == *b.name && a.firstChar == b.firstChar &&
a.lastChar == b.lastChar;
return *a.name == *b.name && a.first_char == b.first_char &&
a.last_char == b.last_char;
});
}
return false;
}
bool StyledString::flatten(android::Res_value* outValue) const {
if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
bool StyledString::Flatten(android::Res_value* out_value) const {
if (value.index() > std::numeric_limits<uint32_t>::max()) {
return false;
}
outValue->dataType = android::Res_value::TYPE_STRING;
outValue->data =
util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
out_value->dataType = android::Res_value::TYPE_STRING;
out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
return true;
}
StyledString* StyledString::clone(StringPool* newPool) const {
StyledString* str = new StyledString(newPool->makeRef(value));
str->mComment = mComment;
str->mSource = mSource;
StyledString* StyledString::Clone(StringPool* new_pool) const {
StyledString* str = new StyledString(new_pool->MakeRef(value));
str->comment_ = comment_;
str->source_ = source_;
return str;
}
void StyledString::print(std::ostream* out) const {
void StyledString::Print(std::ostream* out) const {
*out << "(styled string) \"" << *value->str << "\"";
for (const StringPool::Span& span : value->spans) {
*out << " " << *span.name << ":" << span.firstChar << "," << span.lastChar;
*out << " " << *span.name << ":" << span.first_char << ","
<< span.last_char;
}
}
FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {}
bool FileReference::equals(const Value* value) const {
const FileReference* other = valueCast<FileReference>(value);
bool FileReference::Equals(const Value* value) const {
const FileReference* other = ValueCast<FileReference>(value);
if (!other) {
return false;
}
return *path == *other->path;
}
bool FileReference::flatten(android::Res_value* outValue) const {
if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
bool FileReference::Flatten(android::Res_value* out_value) const {
if (path.index() > std::numeric_limits<uint32_t>::max()) {
return false;
}
outValue->dataType = android::Res_value::TYPE_STRING;
outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
out_value->dataType = android::Res_value::TYPE_STRING;
out_value->data = util::HostToDevice32(static_cast<uint32_t>(path.index()));
return true;
}
FileReference* FileReference::clone(StringPool* newPool) const {
FileReference* fr = new FileReference(newPool->makeRef(*path));
FileReference* FileReference::Clone(StringPool* new_pool) const {
FileReference* fr = new FileReference(new_pool->MakeRef(*path));
fr->file = file;
fr->mComment = mComment;
fr->mSource = mSource;
fr->comment_ = comment_;
fr->source_ = source_;
return fr;
}
void FileReference::print(std::ostream* out) const {
void FileReference::Print(std::ostream* out) const {
*out << "(file) " << *path;
}
@@ -250,8 +250,8 @@ BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
value.data = data;
}
bool BinaryPrimitive::equals(const Value* value) const {
const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
bool BinaryPrimitive::Equals(const Value* value) const {
const BinaryPrimitive* other = ValueCast<BinaryPrimitive>(value);
if (!other) {
return false;
}
@@ -259,17 +259,17 @@ bool BinaryPrimitive::equals(const Value* value) const {
this->value.data == other->value.data;
}
bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
outValue->dataType = value.dataType;
outValue->data = util::hostToDevice32(value.data);
bool BinaryPrimitive::Flatten(android::Res_value* out_value) const {
out_value->dataType = value.dataType;
out_value->data = util::HostToDevice32(value.data);
return true;
}
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
BinaryPrimitive* BinaryPrimitive::Clone(StringPool* /*new_pool*/) const {
return new BinaryPrimitive(*this);
}
void BinaryPrimitive::print(std::ostream* out) const {
void BinaryPrimitive::Print(std::ostream* out) const {
switch (value.dataType) {
case android::Res_value::TYPE_NULL:
*out << "(null)";
@@ -297,10 +297,10 @@ void BinaryPrimitive::print(std::ostream* out) const {
}
Attribute::Attribute(bool w, uint32_t t)
: typeMask(t),
minInt(std::numeric_limits<int32_t>::min()),
maxInt(std::numeric_limits<int32_t>::max()) {
mWeak = w;
: type_mask(t),
min_int(std::numeric_limits<int32_t>::min()),
max_int(std::numeric_limits<int32_t>::max()) {
weak_ = w;
}
template <typename T>
@@ -308,8 +308,8 @@ T* addPointer(T& val) {
return &val;
}
bool Attribute::equals(const Value* value) const {
const Attribute* other = valueCast<Attribute>(value);
bool Attribute::Equals(const Value* value) const {
const Attribute* other = ValueCast<Attribute>(value);
if (!other) {
return false;
}
@@ -318,46 +318,46 @@ bool Attribute::equals(const Value* value) const {
return false;
}
if (typeMask != other->typeMask || minInt != other->minInt ||
maxInt != other->maxInt) {
if (type_mask != other->type_mask || min_int != other->min_int ||
max_int != other->max_int) {
return false;
}
std::vector<const Symbol*> sortedA;
std::transform(symbols.begin(), symbols.end(), std::back_inserter(sortedA),
std::vector<const Symbol*> sorted_a;
std::transform(symbols.begin(), symbols.end(), std::back_inserter(sorted_a),
addPointer<const Symbol>);
std::sort(sortedA.begin(), sortedA.end(),
std::sort(sorted_a.begin(), sorted_a.end(),
[](const Symbol* a, const Symbol* b) -> bool {
return a->symbol.name < b->symbol.name;
});
std::vector<const Symbol*> sortedB;
std::vector<const Symbol*> sorted_b;
std::transform(other->symbols.begin(), other->symbols.end(),
std::back_inserter(sortedB), addPointer<const Symbol>);
std::sort(sortedB.begin(), sortedB.end(),
std::back_inserter(sorted_b), addPointer<const Symbol>);
std::sort(sorted_b.begin(), sorted_b.end(),
[](const Symbol* a, const Symbol* b) -> bool {
return a->symbol.name < b->symbol.name;
});
return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
return std::equal(sorted_a.begin(), sorted_a.end(), sorted_b.begin(),
[](const Symbol* a, const Symbol* b) -> bool {
return a->symbol.equals(&b->symbol) &&
return a->symbol.Equals(&b->symbol) &&
a->value == b->value;
});
}
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
Attribute* Attribute::Clone(StringPool* /*new_pool*/) const {
return new Attribute(*this);
}
void Attribute::printMask(std::ostream* out) const {
if (typeMask == android::ResTable_map::TYPE_ANY) {
void Attribute::PrintMask(std::ostream* out) const {
if (type_mask == android::ResTable_map::TYPE_ANY) {
*out << "any";
return;
}
bool set = false;
if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
if ((type_mask & android::ResTable_map::TYPE_REFERENCE) != 0) {
if (!set) {
set = true;
} else {
@@ -366,7 +366,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "reference";
}
if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
if ((type_mask & android::ResTable_map::TYPE_STRING) != 0) {
if (!set) {
set = true;
} else {
@@ -375,7 +375,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "string";
}
if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
if ((type_mask & android::ResTable_map::TYPE_INTEGER) != 0) {
if (!set) {
set = true;
} else {
@@ -384,7 +384,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "integer";
}
if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
if ((type_mask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
if (!set) {
set = true;
} else {
@@ -393,7 +393,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "boolean";
}
if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
if ((type_mask & android::ResTable_map::TYPE_COLOR) != 0) {
if (!set) {
set = true;
} else {
@@ -402,7 +402,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "color";
}
if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
if ((type_mask & android::ResTable_map::TYPE_FLOAT) != 0) {
if (!set) {
set = true;
} else {
@@ -411,7 +411,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "float";
}
if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
if ((type_mask & android::ResTable_map::TYPE_DIMENSION) != 0) {
if (!set) {
set = true;
} else {
@@ -420,7 +420,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "dimension";
}
if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
if ((type_mask & android::ResTable_map::TYPE_FRACTION) != 0) {
if (!set) {
set = true;
} else {
@@ -429,7 +429,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "fraction";
}
if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
if ((type_mask & android::ResTable_map::TYPE_ENUM) != 0) {
if (!set) {
set = true;
} else {
@@ -438,7 +438,7 @@ void Attribute::printMask(std::ostream* out) const {
*out << "enum";
}
if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
if ((type_mask & android::ResTable_map::TYPE_FLAGS) != 0) {
if (!set) {
set = true;
} else {
@@ -448,96 +448,96 @@ void Attribute::printMask(std::ostream* out) const {
}
}
void Attribute::print(std::ostream* out) const {
void Attribute::Print(std::ostream* out) const {
*out << "(attr) ";
printMask(out);
PrintMask(out);
if (!symbols.empty()) {
*out << " [" << util::joiner(symbols, ", ") << "]";
*out << " [" << util::Joiner(symbols, ", ") << "]";
}
if (minInt != std::numeric_limits<int32_t>::min()) {
*out << " min=" << minInt;
if (min_int != std::numeric_limits<int32_t>::min()) {
*out << " min=" << min_int;
}
if (maxInt != std::numeric_limits<int32_t>::max()) {
*out << " max=" << maxInt;
if (max_int != std::numeric_limits<int32_t>::max()) {
*out << " max=" << max_int;
}
if (isWeak()) {
if (IsWeak()) {
*out << " [weak]";
}
}
static void buildAttributeMismatchMessage(DiagMessage* msg,
static void BuildAttributeMismatchMessage(DiagMessage* msg,
const Attribute* attr,
const Item* value) {
*msg << "expected";
if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
if (attr->type_mask & android::ResTable_map::TYPE_BOOLEAN) {
*msg << " boolean";
}
if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
if (attr->type_mask & android::ResTable_map::TYPE_COLOR) {
*msg << " color";
}
if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
if (attr->type_mask & android::ResTable_map::TYPE_DIMENSION) {
*msg << " dimension";
}
if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
if (attr->type_mask & android::ResTable_map::TYPE_ENUM) {
*msg << " enum";
}
if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
if (attr->type_mask & android::ResTable_map::TYPE_FLAGS) {
*msg << " flags";
}
if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
if (attr->type_mask & android::ResTable_map::TYPE_FLOAT) {
*msg << " float";
}
if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
if (attr->type_mask & android::ResTable_map::TYPE_FRACTION) {
*msg << " fraction";
}
if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
if (attr->type_mask & android::ResTable_map::TYPE_INTEGER) {
*msg << " integer";
}
if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
if (attr->type_mask & android::ResTable_map::TYPE_REFERENCE) {
*msg << " reference";
}
if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
if (attr->type_mask & android::ResTable_map::TYPE_STRING) {
*msg << " string";
}
*msg << " but got " << *value;
}
bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
bool Attribute::Matches(const Item* item, DiagMessage* out_msg) const {
android::Res_value val = {};
item->flatten(&val);
item->Flatten(&val);
// Always allow references.
const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
if (outMsg) {
buildAttributeMismatchMessage(outMsg, this, item);
const uint32_t mask = type_mask | android::ResTable_map::TYPE_REFERENCE;
if (!(mask & ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType))) {
if (out_msg) {
BuildAttributeMismatchMessage(out_msg, this, item);
}
return false;
} else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
} else if (ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType) &
android::ResTable_map::TYPE_INTEGER) {
if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
if (outMsg) {
*outMsg << *item << " is less than minimum integer " << minInt;
if (static_cast<int32_t>(util::DeviceToHost32(val.data)) < min_int) {
if (out_msg) {
*out_msg << *item << " is less than minimum integer " << min_int;
}
return false;
} else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
if (outMsg) {
*outMsg << *item << " is greater than maximum integer " << maxInt;
} else if (static_cast<int32_t>(util::DeviceToHost32(val.data)) > max_int) {
if (out_msg) {
*out_msg << *item << " is greater than maximum integer " << max_int;
}
return false;
}
@@ -545,14 +545,14 @@ bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
return true;
}
bool Style::equals(const Value* value) const {
const Style* other = valueCast<Style>(value);
bool Style::Equals(const Value* value) const {
const Style* other = ValueCast<Style>(value);
if (!other) {
return false;
}
if (bool(parent) != bool(other->parent) ||
(parent && other->parent &&
!parent.value().equals(&other->parent.value()))) {
!parent.value().Equals(&other->parent.value()))) {
return false;
}
@@ -560,51 +560,51 @@ bool Style::equals(const Value* value) const {
return false;
}
std::vector<const Entry*> sortedA;
std::transform(entries.begin(), entries.end(), std::back_inserter(sortedA),
std::vector<const Entry*> sorted_a;
std::transform(entries.begin(), entries.end(), std::back_inserter(sorted_a),
addPointer<const Entry>);
std::sort(sortedA.begin(), sortedA.end(),
std::sort(sorted_a.begin(), sorted_a.end(),
[](const Entry* a, const Entry* b) -> bool {
return a->key.name < b->key.name;
});
std::vector<const Entry*> sortedB;
std::vector<const Entry*> sorted_b;
std::transform(other->entries.begin(), other->entries.end(),
std::back_inserter(sortedB), addPointer<const Entry>);
std::sort(sortedB.begin(), sortedB.end(),
std::back_inserter(sorted_b), addPointer<const Entry>);
std::sort(sorted_b.begin(), sorted_b.end(),
[](const Entry* a, const Entry* b) -> bool {
return a->key.name < b->key.name;
});
return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
return std::equal(sorted_a.begin(), sorted_a.end(), sorted_b.begin(),
[](const Entry* a, const Entry* b) -> bool {
return a->key.equals(&b->key) &&
a->value->equals(b->value.get());
return a->key.Equals(&b->key) &&
a->value->Equals(b->value.get());
});
}
Style* Style::clone(StringPool* newPool) const {
Style* Style::Clone(StringPool* new_pool) const {
Style* style = new Style();
style->parent = parent;
style->parentInferred = parentInferred;
style->mComment = mComment;
style->mSource = mSource;
style->parent_inferred = parent_inferred;
style->comment_ = comment_;
style->source_ = source_;
for (auto& entry : entries) {
style->entries.push_back(
Entry{entry.key, std::unique_ptr<Item>(entry.value->clone(newPool))});
Entry{entry.key, std::unique_ptr<Item>(entry.value->Clone(new_pool))});
}
return style;
}
void Style::print(std::ostream* out) const {
void Style::Print(std::ostream* out) const {
*out << "(style) ";
if (parent && parent.value().name) {
if (parent.value().privateReference) {
if (parent.value().private_reference) {
*out << "*";
}
*out << parent.value().name.value();
}
*out << " [" << util::joiner(entries, ", ") << "]";
*out << " [" << util::Joiner(entries, ", ") << "]";
}
static ::std::ostream& operator<<(::std::ostream& out,
@@ -617,12 +617,12 @@ static ::std::ostream& operator<<(::std::ostream& out,
out << "???";
}
out << " = ";
value.value->print(&out);
value.value->Print(&out);
return out;
}
bool Array::equals(const Value* value) const {
const Array* other = valueCast<Array>(value);
bool Array::Equals(const Value* value) const {
const Array* other = ValueCast<Array>(value);
if (!other) {
return false;
}
@@ -634,26 +634,26 @@ bool Array::equals(const Value* value) const {
return std::equal(items.begin(), items.end(), other->items.begin(),
[](const std::unique_ptr<Item>& a,
const std::unique_ptr<Item>& b) -> bool {
return a->equals(b.get());
return a->Equals(b.get());
});
}
Array* Array::clone(StringPool* newPool) const {
Array* Array::Clone(StringPool* new_pool) const {
Array* array = new Array();
array->mComment = mComment;
array->mSource = mSource;
array->comment_ = comment_;
array->source_ = source_;
for (auto& item : items) {
array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
array->items.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
}
return array;
}
void Array::print(std::ostream* out) const {
*out << "(array) [" << util::joiner(items, ", ") << "]";
void Array::Print(std::ostream* out) const {
*out << "(array) [" << util::Joiner(items, ", ") << "]";
}
bool Plural::equals(const Value* value) const {
const Plural* other = valueCast<Plural>(value);
bool Plural::Equals(const Value* value) const {
const Plural* other = ValueCast<Plural>(value);
if (!other) {
return false;
}
@@ -668,24 +668,24 @@ bool Plural::equals(const Value* value) const {
if (bool(a) != bool(b)) {
return false;
}
return bool(a) == bool(b) || a->equals(b.get());
return bool(a) == bool(b) || a->Equals(b.get());
});
}
Plural* Plural::clone(StringPool* newPool) const {
Plural* Plural::Clone(StringPool* new_pool) const {
Plural* p = new Plural();
p->mComment = mComment;
p->mSource = mSource;
p->comment_ = comment_;
p->source_ = source_;
const size_t count = values.size();
for (size_t i = 0; i < count; i++) {
if (values[i]) {
p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
p->values[i] = std::unique_ptr<Item>(values[i]->Clone(new_pool));
}
}
return p;
}
void Plural::print(std::ostream* out) const {
void Plural::Print(std::ostream* out) const {
*out << "(plural)";
if (values[Zero]) {
*out << " zero=" << *values[Zero];
@@ -713,8 +713,8 @@ static ::std::ostream& operator<<(::std::ostream& out,
return out << *item;
}
bool Styleable::equals(const Value* value) const {
const Styleable* other = valueCast<Styleable>(value);
bool Styleable::Equals(const Value* value) const {
const Styleable* other = ValueCast<Styleable>(value);
if (!other) {
return false;
}
@@ -725,21 +725,21 @@ bool Styleable::equals(const Value* value) const {
return std::equal(entries.begin(), entries.end(), other->entries.begin(),
[](const Reference& a, const Reference& b) -> bool {
return a.equals(&b);
return a.Equals(&b);
});
}
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
Styleable* Styleable::Clone(StringPool* /*new_pool*/) const {
return new Styleable(*this);
}
void Styleable::print(std::ostream* out) const {
void Styleable::Print(std::ostream* out) const {
*out << "(styleable) "
<< " [" << util::joiner(entries, ", ") << "]";
<< " [" << util::Joiner(entries, ", ") << "]";
}
bool operator<(const Reference& a, const Reference& b) {
int cmp = a.name.valueOrDefault({}).compare(b.name.valueOrDefault({}));
int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
if (cmp != 0) return cmp < 0;
return a.id < b.id;
}
@@ -758,7 +758,7 @@ struct NameOnlyComparator {
}
};
void Styleable::mergeWith(Styleable* other) {
void Styleable::MergeWith(Styleable* other) {
// Compare only names, because some References may already have their IDs
// assigned
// (framework IDs that don't change).

View File

@@ -17,17 +17,18 @@
#ifndef AAPT_RESOURCE_VALUES_H
#define AAPT_RESOURCE_VALUES_H
#include <array>
#include <ostream>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
#include "Resource.h"
#include "StringPool.h"
#include "io/File.h"
#include "util/Maybe.h"
#include <androidfw/ResourceTypes.h>
#include <array>
#include <ostream>
#include <vector>
namespace aapt {
struct RawValueVisitor;
@@ -46,58 +47,59 @@ struct Value {
* Whether this value is weak and can be overridden without
* warning or error. Default is false.
*/
bool isWeak() const { return mWeak; }
bool IsWeak() const { return weak_; }
void setWeak(bool val) { mWeak = val; }
void SetWeak(bool val) { weak_ = val; }
// Whether the value is marked as translateable.
// This does not persist when flattened.
// It is only used during compilation phase.
void setTranslateable(bool val) { mTranslateable = val; }
void SetTranslateable(bool val) { translateable_ = val; }
// Default true.
bool isTranslateable() const { return mTranslateable; }
bool IsTranslateable() const { return translateable_; }
/**
* Returns the source where this value was defined.
*/
const Source& getSource() const { return mSource; }
const Source& GetSource() const { return source_; }
void setSource(const Source& source) { mSource = source; }
void SetSource(const Source& source) { source_ = source; }
void setSource(Source&& source) { mSource = std::move(source); }
void SetSource(Source&& source) { source_ = std::move(source); }
/**
* Returns the comment that was associated with this resource.
*/
const std::string& getComment() const { return mComment; }
const std::string& GetComment() const { return comment_; }
void setComment(const StringPiece& str) { mComment = str.toString(); }
void SetComment(const StringPiece& str) { comment_ = str.ToString(); }
void setComment(std::string&& str) { mComment = std::move(str); }
void SetComment(std::string&& str) { comment_ = std::move(str); }
virtual bool equals(const Value* value) const = 0;
virtual bool Equals(const Value* value) const = 0;
/**
* Calls the appropriate overload of ValueVisitor.
*/
virtual void accept(RawValueVisitor* visitor) = 0;
virtual void Accept(RawValueVisitor* visitor) = 0;
/**
* Clone the value.
* Clone the value. new_pool is the new StringPool that
* any resources with strings should use when copying their string.
*/
virtual Value* clone(StringPool* newPool) const = 0;
virtual Value* Clone(StringPool* new_pool) const = 0;
/**
* Human readable printout of this value.
*/
virtual void print(std::ostream* out) const = 0;
virtual void Print(std::ostream* out) const = 0;
protected:
Source mSource;
std::string mComment;
bool mWeak = false;
bool mTranslateable = true;
Source source_;
std::string comment_;
bool weak_ = false;
bool translateable_ = true;
};
/**
@@ -105,7 +107,7 @@ struct Value {
*/
template <typename Derived>
struct BaseValue : public Value {
void accept(RawValueVisitor* visitor) override;
void Accept(RawValueVisitor* visitor) override;
};
/**
@@ -115,14 +117,14 @@ struct Item : public Value {
/**
* Clone the Item.
*/
virtual Item* clone(StringPool* newPool) const override = 0;
virtual Item* Clone(StringPool* new_pool) const override = 0;
/**
* Fills in an android::Res_value structure with this Item's binary
* representation.
* Returns false if an error occurred.
*/
virtual bool flatten(android::Res_value* outValue) const = 0;
virtual bool Flatten(android::Res_value* out_value) const = 0;
};
/**
@@ -130,7 +132,7 @@ struct Item : public Value {
*/
template <typename Derived>
struct BaseItem : public Item {
void accept(RawValueVisitor* visitor) override;
void Accept(RawValueVisitor* visitor) override;
};
/**
@@ -149,18 +151,18 @@ struct Reference : public BaseItem<Reference> {
Maybe<ResourceName> name;
Maybe<ResourceId> id;
Reference::Type referenceType;
bool privateReference = false;
Reference::Type reference_type;
bool private_reference = false;
Reference();
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
explicit Reference(const ResourceId& i, Type type = Type::kResource);
explicit Reference(const ResourceNameRef& n, const ResourceId& i);
Reference(const ResourceNameRef& n, const ResourceId& i);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
Reference* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
Reference* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
bool operator<(const Reference&, const Reference&);
@@ -170,11 +172,11 @@ bool operator==(const Reference&, const Reference&);
* An ID resource. Has no real value, just a place holder.
*/
struct Id : public BaseItem<Id> {
Id() { mWeak = true; }
bool equals(const Value* value) const override;
bool flatten(android::Res_value* out) const override;
Id* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
Id() { weak_ = true; }
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out) const override;
Id* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
/**
@@ -187,10 +189,10 @@ struct RawString : public BaseItem<RawString> {
explicit RawString(const StringPool::Ref& ref);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
RawString* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
RawString* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct String : public BaseItem<String> {
@@ -198,10 +200,10 @@ struct String : public BaseItem<String> {
explicit String(const StringPool::Ref& ref);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
String* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
String* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct StyledString : public BaseItem<StyledString> {
@@ -209,10 +211,10 @@ struct StyledString : public BaseItem<StyledString> {
explicit StyledString(const StringPool::StyleRef& ref);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
StyledString* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
StyledString* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct FileReference : public BaseItem<FileReference> {
@@ -226,10 +228,10 @@ struct FileReference : public BaseItem<FileReference> {
FileReference() = default;
explicit FileReference(const StringPool::Ref& path);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
FileReference* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
FileReference* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
/**
@@ -242,10 +244,10 @@ struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
explicit BinaryPrimitive(const android::Res_value& val);
BinaryPrimitive(uint8_t dataType, uint32_t data);
bool equals(const Value* value) const override;
bool flatten(android::Res_value* outValue) const override;
BinaryPrimitive* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
BinaryPrimitive* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct Attribute : public BaseValue<Attribute> {
@@ -254,18 +256,18 @@ struct Attribute : public BaseValue<Attribute> {
uint32_t value;
};
uint32_t typeMask;
int32_t minInt;
int32_t maxInt;
uint32_t type_mask;
int32_t min_int;
int32_t max_int;
std::vector<Symbol> symbols;
explicit Attribute(bool w, uint32_t t = 0u);
bool equals(const Value* value) const override;
Attribute* clone(StringPool* newPool) const override;
void printMask(std::ostream* out) const;
void print(std::ostream* out) const override;
bool matches(const Item* item, DiagMessage* outMsg) const;
bool Equals(const Value* value) const override;
Attribute* Clone(StringPool* new_pool) const override;
void PrintMask(std::ostream* out) const;
void Print(std::ostream* out) const override;
bool Matches(const Item* item, DiagMessage* out_msg) const;
};
struct Style : public BaseValue<Style> {
@@ -280,21 +282,21 @@ struct Style : public BaseValue<Style> {
* If set to true, the parent was auto inferred from the
* style's name.
*/
bool parentInferred = false;
bool parent_inferred = false;
std::vector<Entry> entries;
bool equals(const Value* value) const override;
Style* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
Style* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct Array : public BaseValue<Array> {
std::vector<std::unique_ptr<Item>> items;
bool equals(const Value* value) const override;
Array* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
Array* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct Plural : public BaseValue<Plural> {
@@ -302,25 +304,25 @@ struct Plural : public BaseValue<Plural> {
std::array<std::unique_ptr<Item>, Count> values;
bool equals(const Value* value) const override;
Plural* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
bool Equals(const Value* value) const override;
Plural* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
struct Styleable : public BaseValue<Styleable> {
std::vector<Reference> entries;
bool equals(const Value* value) const override;
Styleable* clone(StringPool* newPool) const override;
void print(std::ostream* out) const override;
void mergeWith(Styleable* styleable);
bool Equals(const Value* value) const override;
Styleable* Clone(StringPool* newPool) const override;
void Print(std::ostream* out) const override;
void MergeWith(Styleable* styleable);
};
/**
* Stream operator for printing Value objects.
*/
inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
value.print(&out);
value.Print(&out);
return out;
}

View File

@@ -15,100 +15,101 @@
*/
#include "Resource.h"
#include "test/Test.h"
namespace aapt {
TEST(ResourceTypeTest, ParseResourceTypes) {
const ResourceType* type = parseResourceType("anim");
const ResourceType* type = ParseResourceType("anim");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kAnim);
type = parseResourceType("animator");
type = ParseResourceType("animator");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kAnimator);
type = parseResourceType("array");
type = ParseResourceType("array");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kArray);
type = parseResourceType("attr");
type = ParseResourceType("attr");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kAttr);
type = parseResourceType("^attr-private");
type = ParseResourceType("^attr-private");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kAttrPrivate);
type = parseResourceType("bool");
type = ParseResourceType("bool");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kBool);
type = parseResourceType("color");
type = ParseResourceType("color");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kColor);
type = parseResourceType("dimen");
type = ParseResourceType("dimen");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kDimen);
type = parseResourceType("drawable");
type = ParseResourceType("drawable");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kDrawable);
type = parseResourceType("fraction");
type = ParseResourceType("fraction");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kFraction);
type = parseResourceType("id");
type = ParseResourceType("id");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kId);
type = parseResourceType("integer");
type = ParseResourceType("integer");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInteger);
type = parseResourceType("interpolator");
type = ParseResourceType("interpolator");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kInterpolator);
type = parseResourceType("layout");
type = ParseResourceType("layout");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kLayout);
type = parseResourceType("menu");
type = ParseResourceType("menu");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kMenu);
type = parseResourceType("mipmap");
type = ParseResourceType("mipmap");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kMipmap);
type = parseResourceType("plurals");
type = ParseResourceType("plurals");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kPlurals);
type = parseResourceType("raw");
type = ParseResourceType("raw");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kRaw);
type = parseResourceType("string");
type = ParseResourceType("string");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kString);
type = parseResourceType("style");
type = ParseResourceType("style");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kStyle);
type = parseResourceType("transition");
type = ParseResourceType("transition");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kTransition);
type = parseResourceType("xml");
type = ParseResourceType("xml");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kXml);
type = parseResourceType("blahaha");
type = ParseResourceType("blahaha");
EXPECT_EQ(type, nullptr);
}

View File

@@ -48,17 +48,17 @@ static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
{0x04ce, SDK_LOLLIPOP},
};
static bool lessEntryId(const std::pair<uint16_t, size_t>& p,
static bool less_entry_id(const std::pair<uint16_t, size_t>& p,
uint16_t entryId) {
return p.first < entryId;
}
size_t findAttributeSdkLevel(const ResourceId& id) {
if (id.packageId() != 0x01 && id.typeId() != 0x01) {
size_t FindAttributeSdkLevel(const ResourceId& id) {
if (id.package_id() != 0x01 && id.type_id() != 0x01) {
return 0;
}
auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(),
id.entryId(), lessEntryId);
id.entry_id(), less_entry_id);
if (iter == sAttrIdMap.end()) {
return SDK_LOLLIPOP_MR1;
}
@@ -727,7 +727,7 @@ static const std::unordered_map<std::string, size_t> sAttrMap = {
{"windowActivityTransitions", 21},
{"colorEdgeEffect", 21}};
size_t findAttributeSdkLevel(const ResourceName& name) {
size_t FindAttributeSdkLevel(const ResourceName& name) {
if (name.package != "android" && name.type != ResourceType::kAttr) {
return 0;
}
@@ -739,7 +739,7 @@ size_t findAttributeSdkLevel(const ResourceName& name) {
return SDK_LOLLIPOP_MR1;
}
std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion() {
std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion() {
return std::make_pair(StringPiece(sDevelopmentSdkCodeName),
sDevelopmentSdkLevel);
}

View File

@@ -17,10 +17,10 @@
#ifndef AAPT_SDK_CONSTANTS_H
#define AAPT_SDK_CONSTANTS_H
#include "Resource.h"
#include <utility>
#include "Resource.h"
namespace aapt {
enum {
@@ -47,9 +47,9 @@ enum {
SDK_MARSHMALLOW = 23,
};
size_t findAttributeSdkLevel(const ResourceId& id);
size_t findAttributeSdkLevel(const ResourceName& name);
std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion();
size_t FindAttributeSdkLevel(const ResourceId& id);
size_t FindAttributeSdkLevel(const ResourceName& name);
std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
} // namespace aapt

View File

@@ -16,23 +16,23 @@
#include "SdkConstants.h"
#include <gtest/gtest.h>
#include "gtest/gtest.h"
namespace aapt {
TEST(SdkConstantsTest, FirstAttributeIsSdk1) {
EXPECT_EQ(1u, findAttributeSdkLevel(ResourceId(0x01010000)));
EXPECT_EQ(1u, FindAttributeSdkLevel(ResourceId(0x01010000)));
}
TEST(SdkConstantsTest, AllAttributesAfterLollipopAreLollipopMR1) {
EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010103f7)));
EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010104ce)));
EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010103f7)));
EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010104ce)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104cf)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d8)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104cf)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d8)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d9)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x0101ffff)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d9)));
EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x0101ffff)));
}
} // namespace aapt

View File

@@ -17,12 +17,12 @@
#ifndef AAPT_SOURCE_H
#define AAPT_SOURCE_H
#include "util/Maybe.h"
#include "util/StringPiece.h"
#include <ostream>
#include <string>
#include "util/Maybe.h"
#include "util/StringPiece.h"
namespace aapt {
/**
@@ -36,13 +36,13 @@ struct Source {
Source() = default;
inline Source(const StringPiece& path)
: path(path.toString()) { // NOLINT(implicit)
: path(path.ToString()) { // NOLINT(implicit)
}
inline Source(const StringPiece& path, size_t line)
: path(path.toString()), line(line) {}
: path(path.ToString()), line(line) {}
inline Source withLine(size_t line) const { return Source(path, line); }
inline Source WithLine(size_t line) const { return Source(path, line); }
};
//

View File

@@ -15,263 +15,266 @@
*/
#include "StringPool.h"
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <algorithm>
#include <memory>
#include <string>
#include "android-base/logging.h"
#include "androidfw/ResourceTypes.h"
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include "util/Util.h"
namespace aapt {
StringPool::Ref::Ref() : mEntry(nullptr) {}
StringPool::Ref::Ref() : entry_(nullptr) {}
StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
if (mEntry != nullptr) {
mEntry->ref++;
StringPool::Ref::Ref(const StringPool::Ref& rhs) : entry_(rhs.entry_) {
if (entry_ != nullptr) {
entry_->ref_++;
}
}
StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
if (mEntry != nullptr) {
mEntry->ref++;
StringPool::Ref::Ref(StringPool::Entry* entry) : entry_(entry) {
if (entry_ != nullptr) {
entry_->ref_++;
}
}
StringPool::Ref::~Ref() {
if (mEntry != nullptr) {
mEntry->ref--;
if (entry_ != nullptr) {
entry_->ref_--;
}
}
StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
if (rhs.mEntry != nullptr) {
rhs.mEntry->ref++;
if (rhs.entry_ != nullptr) {
rhs.entry_->ref_++;
}
if (mEntry != nullptr) {
mEntry->ref--;
if (entry_ != nullptr) {
entry_->ref_--;
}
mEntry = rhs.mEntry;
entry_ = rhs.entry_;
return *this;
}
const std::string* StringPool::Ref::operator->() const {
return &mEntry->value;
return &entry_->value;
}
const std::string& StringPool::Ref::operator*() const { return mEntry->value; }
const std::string& StringPool::Ref::operator*() const { return entry_->value; }
size_t StringPool::Ref::getIndex() const { return mEntry->index; }
size_t StringPool::Ref::index() const { return entry_->index; }
const StringPool::Context& StringPool::Ref::getContext() const {
return mEntry->context;
const StringPool::Context& StringPool::Ref::GetContext() const {
return entry_->context;
}
StringPool::StyleRef::StyleRef() : mEntry(nullptr) {}
StringPool::StyleRef::StyleRef() : entry_(nullptr) {}
StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs)
: mEntry(rhs.mEntry) {
if (mEntry != nullptr) {
mEntry->ref++;
: entry_(rhs.entry_) {
if (entry_ != nullptr) {
entry_->ref_++;
}
}
StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
if (mEntry != nullptr) {
mEntry->ref++;
StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : entry_(entry) {
if (entry_ != nullptr) {
entry_->ref_++;
}
}
StringPool::StyleRef::~StyleRef() {
if (mEntry != nullptr) {
mEntry->ref--;
if (entry_ != nullptr) {
entry_->ref_--;
}
}
StringPool::StyleRef& StringPool::StyleRef::operator=(
const StringPool::StyleRef& rhs) {
if (rhs.mEntry != nullptr) {
rhs.mEntry->ref++;
if (rhs.entry_ != nullptr) {
rhs.entry_->ref_++;
}
if (mEntry != nullptr) {
mEntry->ref--;
if (entry_ != nullptr) {
entry_->ref_--;
}
mEntry = rhs.mEntry;
entry_ = rhs.entry_;
return *this;
}
const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
return mEntry;
return entry_;
}
const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
return *mEntry;
return *entry_;
}
size_t StringPool::StyleRef::getIndex() const { return mEntry->str.getIndex(); }
size_t StringPool::StyleRef::index() const { return entry_->str.index(); }
const StringPool::Context& StringPool::StyleRef::getContext() const {
return mEntry->str.getContext();
const StringPool::Context& StringPool::StyleRef::GetContext() const {
return entry_->str.GetContext();
}
StringPool::Ref StringPool::makeRef(const StringPiece& str) {
return makeRefImpl(str, Context{}, true);
StringPool::Ref StringPool::MakeRef(const StringPiece& str) {
return MakeRefImpl(str, Context{}, true);
}
StringPool::Ref StringPool::makeRef(const StringPiece& str,
StringPool::Ref StringPool::MakeRef(const StringPiece& str,
const Context& context) {
return makeRefImpl(str, context, true);
return MakeRefImpl(str, context, true);
}
StringPool::Ref StringPool::makeRefImpl(const StringPiece& str,
StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str,
const Context& context, bool unique) {
if (unique) {
auto iter = mIndexedStrings.find(str);
if (iter != std::end(mIndexedStrings)) {
auto iter = indexed_strings_.find(str);
if (iter != std::end(indexed_strings_)) {
return Ref(iter->second);
}
}
Entry* entry = new Entry();
entry->value = str.toString();
entry->value = str.ToString();
entry->context = context;
entry->index = mStrings.size();
entry->ref = 0;
mStrings.emplace_back(entry);
mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
entry->index = strings_.size();
entry->ref_ = 0;
strings_.emplace_back(entry);
indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
return Ref(entry);
}
StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
return makeRef(str, Context{});
StringPool::StyleRef StringPool::MakeRef(const StyleString& str) {
return MakeRef(str, Context{});
}
StringPool::StyleRef StringPool::makeRef(const StyleString& str,
StringPool::StyleRef StringPool::MakeRef(const StyleString& str,
const Context& context) {
Entry* entry = new Entry();
entry->value = str.str;
entry->context = context;
entry->index = mStrings.size();
entry->ref = 0;
mStrings.emplace_back(entry);
mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
entry->index = strings_.size();
entry->ref_ = 0;
strings_.emplace_back(entry);
indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
StyleEntry* styleEntry = new StyleEntry();
styleEntry->str = Ref(entry);
StyleEntry* style_entry = new StyleEntry();
style_entry->str = Ref(entry);
for (const aapt::Span& span : str.spans) {
styleEntry->spans.emplace_back(
Span{makeRef(span.name), span.firstChar, span.lastChar});
style_entry->spans.emplace_back(
Span{MakeRef(span.name), span.first_char, span.last_char});
}
styleEntry->ref = 0;
mStyles.emplace_back(styleEntry);
return StyleRef(styleEntry);
style_entry->ref_ = 0;
styles_.emplace_back(style_entry);
return StyleRef(style_entry);
}
StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) {
Entry* entry = new Entry();
entry->value = *ref.mEntry->str;
entry->context = ref.mEntry->str.mEntry->context;
entry->index = mStrings.size();
entry->ref = 0;
mStrings.emplace_back(entry);
mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
entry->value = *ref.entry_->str;
entry->context = ref.entry_->str.entry_->context;
entry->index = strings_.size();
entry->ref_ = 0;
strings_.emplace_back(entry);
indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
StyleEntry* styleEntry = new StyleEntry();
styleEntry->str = Ref(entry);
for (const Span& span : ref.mEntry->spans) {
styleEntry->spans.emplace_back(
Span{makeRef(*span.name), span.firstChar, span.lastChar});
StyleEntry* style_entry = new StyleEntry();
style_entry->str = Ref(entry);
for (const Span& span : ref.entry_->spans) {
style_entry->spans.emplace_back(
Span{MakeRef(*span.name), span.first_char, span.last_char});
}
styleEntry->ref = 0;
mStyles.emplace_back(styleEntry);
return StyleRef(styleEntry);
style_entry->ref_ = 0;
styles_.emplace_back(style_entry);
return StyleRef(style_entry);
}
void StringPool::merge(StringPool&& pool) {
mIndexedStrings.insert(pool.mIndexedStrings.begin(),
pool.mIndexedStrings.end());
pool.mIndexedStrings.clear();
std::move(pool.mStrings.begin(), pool.mStrings.end(),
std::back_inserter(mStrings));
pool.mStrings.clear();
std::move(pool.mStyles.begin(), pool.mStyles.end(),
std::back_inserter(mStyles));
pool.mStyles.clear();
void StringPool::Merge(StringPool&& pool) {
indexed_strings_.insert(pool.indexed_strings_.begin(),
pool.indexed_strings_.end());
pool.indexed_strings_.clear();
std::move(pool.strings_.begin(), pool.strings_.end(),
std::back_inserter(strings_));
pool.strings_.clear();
std::move(pool.styles_.begin(), pool.styles_.end(),
std::back_inserter(styles_));
pool.styles_.clear();
// Assign the indices.
const size_t len = mStrings.size();
const size_t len = strings_.size();
for (size_t index = 0; index < len; index++) {
mStrings[index]->index = index;
strings_[index]->index = index;
}
}
void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
mStrings.reserve(mStrings.size() + stringCount);
mStyles.reserve(mStyles.size() + styleCount);
void StringPool::HintWillAdd(size_t stringCount, size_t styleCount) {
strings_.reserve(strings_.size() + stringCount);
styles_.reserve(styles_.size() + styleCount);
}
void StringPool::prune() {
const auto iterEnd = std::end(mIndexedStrings);
auto indexIter = std::begin(mIndexedStrings);
while (indexIter != iterEnd) {
if (indexIter->second->ref <= 0) {
indexIter = mIndexedStrings.erase(indexIter);
void StringPool::Prune() {
const auto iter_end = indexed_strings_.end();
auto index_iter = indexed_strings_.begin();
while (index_iter != iter_end) {
if (index_iter->second->ref_ <= 0) {
index_iter = indexed_strings_.erase(index_iter);
} else {
++indexIter;
++index_iter;
}
}
auto endIter2 =
std::remove_if(std::begin(mStrings), std::end(mStrings),
auto end_iter2 =
std::remove_if(strings_.begin(), strings_.end(),
[](const std::unique_ptr<Entry>& entry) -> bool {
return entry->ref <= 0;
return entry->ref_ <= 0;
});
auto endIter3 =
std::remove_if(std::begin(mStyles), std::end(mStyles),
auto end_iter3 =
std::remove_if(styles_.begin(), styles_.end(),
[](const std::unique_ptr<StyleEntry>& entry) -> bool {
return entry->ref <= 0;
return entry->ref_ <= 0;
});
// Remove the entries at the end or else we'll be accessing
// a deleted string from the StyleEntry.
mStrings.erase(endIter2, std::end(mStrings));
mStyles.erase(endIter3, std::end(mStyles));
strings_.erase(end_iter2, strings_.end());
styles_.erase(end_iter3, styles_.end());
// Reassign the indices.
const size_t len = mStrings.size();
const size_t len = strings_.size();
for (size_t index = 0; index < len; index++) {
mStrings[index]->index = index;
strings_[index]->index = index;
}
}
void StringPool::sort(
void StringPool::Sort(
const std::function<bool(const Entry&, const Entry&)>& cmp) {
std::sort(
std::begin(mStrings), std::end(mStrings),
strings_.begin(), strings_.end(),
[&cmp](const std::unique_ptr<Entry>& a,
const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); });
// Assign the indices.
const size_t len = mStrings.size();
const size_t len = strings_.size();
for (size_t index = 0; index < len; index++) {
mStrings[index]->index = index;
strings_[index]->index = index;
}
// Reorder the styles.
std::sort(std::begin(mStyles), std::end(mStyles),
std::sort(styles_.begin(), styles_.end(),
[](const std::unique_ptr<StyleEntry>& lhs,
const std::unique_ptr<StyleEntry>& rhs) -> bool {
return lhs->str.getIndex() < rhs->str.getIndex();
return lhs->str.index() < rhs->str.index();
});
}
template <typename T>
static T* encodeLength(T* data, size_t length) {
static T* EncodeLength(T* data, size_t length) {
static_assert(std::is_integral<T>::value, "wat.");
constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
@@ -284,7 +287,7 @@ static T* encodeLength(T* data, size_t length) {
}
template <typename T>
static size_t encodedLengthUnits(size_t length) {
static size_t EncodedLengthUnits(size_t length) {
static_assert(std::is_integral<T>::value, "wat.");
constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
@@ -292,10 +295,10 @@ static size_t encodedLengthUnits(size_t length) {
return length > kMaxSize ? 2 : 1;
}
bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
const size_t startIndex = out->size();
bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
const size_t start_index = out->size();
android::ResStringPool_header* header =
out->nextBlock<android::ResStringPool_header>();
out->NextBlock<android::ResStringPool_header>();
header->header.type = android::RES_STRING_POOL_TYPE;
header->header.headerSize = sizeof(*header);
header->stringCount = pool.size();
@@ -304,92 +307,90 @@ bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
}
uint32_t* indices =
pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
uint32_t* styleIndices = nullptr;
if (!pool.mStyles.empty()) {
header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
styleIndices = out->nextBlock<uint32_t>(header->styleCount);
uint32_t* style_indices = nullptr;
if (!pool.styles_.empty()) {
header->styleCount = pool.styles_.back()->str.index() + 1;
style_indices = out->NextBlock<uint32_t>(header->styleCount);
}
const size_t beforeStringsIndex = out->size();
header->stringsStart = beforeStringsIndex - startIndex;
const size_t before_strings_index = out->size();
header->stringsStart = before_strings_index - start_index;
for (const auto& entry : pool) {
*indices = out->size() - beforeStringsIndex;
*indices = out->size() - before_strings_index;
indices++;
if (utf8) {
const std::string& encoded = entry->value;
const ssize_t utf16Length = utf8_to_utf16_length(
const ssize_t utf16_length = utf8_to_utf16_length(
reinterpret_cast<const uint8_t*>(entry->value.data()),
entry->value.size());
assert(utf16Length >= 0);
CHECK(utf16_length >= 0);
const size_t totalSize = encodedLengthUnits<char>(utf16Length) +
encodedLengthUnits<char>(encoded.length()) +
encoded.size() + 1;
const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
EncodedLengthUnits<char>(encoded.length()) +
encoded.size() + 1;
char* data = out->nextBlock<char>(totalSize);
char* data = out->NextBlock<char>(total_size);
// First encode the UTF16 string length.
data = encodeLength(data, utf16Length);
data = EncodeLength(data, utf16_length);
// Now encode the size of the real UTF8 string.
data = encodeLength(data, encoded.length());
data = EncodeLength(data, encoded.length());
strncpy(data, encoded.data(), encoded.size());
} else {
const std::u16string encoded = util::utf8ToUtf16(entry->value);
const ssize_t utf16Length = encoded.size();
const std::u16string encoded = util::Utf8ToUtf16(entry->value);
const ssize_t utf16_length = encoded.size();
// Total number of 16-bit words to write.
const size_t totalSize =
encodedLengthUnits<char16_t>(utf16Length) + encoded.size() + 1;
const size_t total_size =
EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
char16_t* data = out->nextBlock<char16_t>(totalSize);
char16_t* data = out->NextBlock<char16_t>(total_size);
// Encode the actual UTF16 string length.
data = encodeLength(data, utf16Length);
const size_t byteLength = encoded.size() * sizeof(char16_t);
data = EncodeLength(data, utf16_length);
const size_t byte_length = encoded.size() * sizeof(char16_t);
// NOTE: For some reason, strncpy16(data, entry->value.data(),
// entry->value.size())
// truncates the string.
memcpy(data, encoded.data(), byteLength);
// entry->value.size()) truncates the string.
memcpy(data, encoded.data(), byte_length);
// The null-terminating character is already here due to the block of data
// being set
// to 0s on allocation.
// being set to 0s on allocation.
}
}
out->align4();
out->Align4();
if (!pool.mStyles.empty()) {
const size_t beforeStylesIndex = out->size();
header->stylesStart = beforeStylesIndex - startIndex;
if (!pool.styles_.empty()) {
const size_t before_styles_index = out->size();
header->stylesStart = before_styles_index - start_index;
size_t currentIndex = 0;
for (const auto& entry : pool.mStyles) {
while (entry->str.getIndex() > currentIndex) {
styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
size_t current_index = 0;
for (const auto& entry : pool.styles_) {
while (entry->str.index() > current_index) {
style_indices[current_index++] = out->size() - before_styles_index;
uint32_t* spanOffset = out->nextBlock<uint32_t>();
*spanOffset = android::ResStringPool_span::END;
uint32_t* span_offset = out->NextBlock<uint32_t>();
*span_offset = android::ResStringPool_span::END;
}
styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
style_indices[current_index++] = out->size() - before_styles_index;
android::ResStringPool_span* span =
out->nextBlock<android::ResStringPool_span>(entry->spans.size());
out->NextBlock<android::ResStringPool_span>(entry->spans.size());
for (const auto& s : entry->spans) {
span->name.index = s.name.getIndex();
span->firstChar = s.firstChar;
span->lastChar = s.lastChar;
span->name.index = s.name.index();
span->firstChar = s.first_char;
span->lastChar = s.last_char;
span++;
}
uint32_t* spanEnd = out->nextBlock<uint32_t>();
uint32_t* spanEnd = out->NextBlock<uint32_t>();
*spanEnd = android::ResStringPool_span::END;
}
@@ -397,22 +398,22 @@ bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
// ResStringPool_span structure worth of 0xFFFFFFFF at the end
// of the style block, so fill in the remaining 2 32bit words
// with 0xFFFFFFFF.
const size_t paddingLength = sizeof(android::ResStringPool_span) -
sizeof(android::ResStringPool_span::name);
uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
memset(padding, 0xff, paddingLength);
out->align4();
const size_t padding_length = sizeof(android::ResStringPool_span) -
sizeof(android::ResStringPool_span::name);
uint8_t* padding = out->NextBlock<uint8_t>(padding_length);
memset(padding, 0xff, padding_length);
out->Align4();
}
header->header.size = out->size() - startIndex;
header->header.size = out->size() - start_index;
return true;
}
bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
return flatten(out, pool, true);
bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool) {
return Flatten(out, pool, true);
}
bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
return flatten(out, pool, false);
bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool) {
return Flatten(out, pool, false);
}
} // namespace aapt

View File

@@ -17,22 +17,22 @@
#ifndef AAPT_STRING_POOL_H
#define AAPT_STRING_POOL_H
#include "ConfigDescription.h"
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "ConfigDescription.h"
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
namespace aapt {
struct Span {
std::string name;
uint32_t firstChar;
uint32_t lastChar;
uint32_t first_char;
uint32_t last_char;
};
struct StyleString {
@@ -72,15 +72,15 @@ class StringPool {
const std::string* operator->() const;
const std::string& operator*() const;
size_t getIndex() const;
const Context& getContext() const;
size_t index() const;
const Context& GetContext() const;
private:
friend class StringPool;
explicit Ref(Entry* entry);
Entry* mEntry;
Entry* entry_;
};
class StyleEntry;
@@ -95,15 +95,15 @@ class StringPool {
const StyleEntry* operator->() const;
const StyleEntry& operator*() const;
size_t getIndex() const;
const Context& getContext() const;
size_t index() const;
const Context& GetContext() const;
private:
friend class StringPool;
explicit StyleRef(StyleEntry* entry);
StyleEntry* mEntry;
StyleEntry* entry_;
};
class Entry {
@@ -116,13 +116,13 @@ class StringPool {
friend class StringPool;
friend class Ref;
int ref;
int ref_;
};
struct Span {
Ref name;
uint32_t firstChar;
uint32_t lastChar;
uint32_t first_char;
uint32_t last_char;
};
class StyleEntry {
@@ -134,13 +134,13 @@ class StringPool {
friend class StringPool;
friend class StyleRef;
int ref;
int ref_;
};
using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
static bool FlattenUtf8(BigBuffer* out, const StringPool& pool);
static bool FlattenUtf16(BigBuffer* out, const StringPool& pool);
StringPool() = default;
StringPool(const StringPool&) = delete;
@@ -149,84 +149,84 @@ class StringPool {
* Adds a string to the pool, unless it already exists. Returns
* a reference to the string in the pool.
*/
Ref makeRef(const StringPiece& str);
Ref MakeRef(const StringPiece& str);
/**
* Adds a string to the pool, unless it already exists, with a context
* object that can be used when sorting the string pool. Returns
* a reference to the string in the pool.
*/
Ref makeRef(const StringPiece& str, const Context& context);
Ref MakeRef(const StringPiece& str, const Context& context);
/**
* Adds a style to the string pool and returns a reference to it.
*/
StyleRef makeRef(const StyleString& str);
StyleRef MakeRef(const StyleString& str);
/**
* Adds a style to the string pool with a context object that
* can be used when sorting the string pool. Returns a reference
* to the style in the string pool.
*/
StyleRef makeRef(const StyleString& str, const Context& context);
StyleRef MakeRef(const StyleString& str, const Context& context);
/**
* Adds a style from another string pool. Returns a reference to the
* style in the string pool.
*/
StyleRef makeRef(const StyleRef& ref);
StyleRef MakeRef(const StyleRef& ref);
/**
* Moves pool into this one without coalescing strings. When this
* function returns, pool will be empty.
*/
void merge(StringPool&& pool);
void Merge(StringPool&& pool);
/**
* Retuns the number of strings in the table.
* Returns the number of strings in the table.
*/
inline size_t size() const;
/**
* Reserves space for strings and styles as an optimization.
*/
void hintWillAdd(size_t stringCount, size_t styleCount);
void HintWillAdd(size_t string_count, size_t style_count);
/**
* Sorts the strings according to some comparison function.
*/
void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
void Sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
/**
* Removes any strings that have no references.
*/
void prune();
void Prune();
private:
friend const_iterator begin(const StringPool& pool);
friend const_iterator end(const StringPool& pool);
static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
Ref makeRefImpl(const StringPiece& str, const Context& context, bool unique);
Ref MakeRefImpl(const StringPiece& str, const Context& context, bool unique);
std::vector<std::unique_ptr<Entry>> mStrings;
std::vector<std::unique_ptr<StyleEntry>> mStyles;
std::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
std::vector<std::unique_ptr<Entry>> strings_;
std::vector<std::unique_ptr<StyleEntry>> styles_;
std::unordered_multimap<StringPiece, Entry*> indexed_strings_;
};
//
// Inline implementation
//
inline size_t StringPool::size() const { return mStrings.size(); }
inline size_t StringPool::size() const { return strings_.size(); }
inline StringPool::const_iterator begin(const StringPool& pool) {
return pool.mStrings.begin();
return pool.strings_.begin();
}
inline StringPool::const_iterator end(const StringPool& pool) {
return pool.mStrings.end();
return pool.strings_.end();
}
} // namespace aapt

View File

@@ -15,25 +15,26 @@
*/
#include "StringPool.h"
#include "test/Test.h"
#include "util/Util.h"
#include <string>
#include "test/Test.h"
#include "util/Util.h"
namespace aapt {
TEST(StringPoolTest, InsertOneString) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("wut");
StringPool::Ref ref = pool.MakeRef("wut");
EXPECT_EQ(*ref, "wut");
}
TEST(StringPoolTest, InsertTwoUniqueStrings) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("wut");
StringPool::Ref ref2 = pool.makeRef("hey");
StringPool::Ref ref = pool.MakeRef("wut");
StringPool::Ref ref2 = pool.MakeRef("hey");
EXPECT_EQ(*ref, "wut");
EXPECT_EQ(*ref2, "hey");
@@ -42,8 +43,8 @@ TEST(StringPoolTest, InsertTwoUniqueStrings) {
TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("wut");
StringPool::Ref ref2 = pool.makeRef("wut");
StringPool::Ref ref = pool.MakeRef("wut");
StringPool::Ref ref2 = pool.MakeRef("wut");
EXPECT_EQ(*ref, "wut");
EXPECT_EQ(*ref2, "wut");
@@ -53,28 +54,28 @@ TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
TEST(StringPoolTest, MaintainInsertionOrderIndex) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("z");
StringPool::Ref ref2 = pool.makeRef("a");
StringPool::Ref ref3 = pool.makeRef("m");
StringPool::Ref ref = pool.MakeRef("z");
StringPool::Ref ref2 = pool.MakeRef("a");
StringPool::Ref ref3 = pool.MakeRef("m");
EXPECT_EQ(0u, ref.getIndex());
EXPECT_EQ(1u, ref2.getIndex());
EXPECT_EQ(2u, ref3.getIndex());
EXPECT_EQ(0u, ref.index());
EXPECT_EQ(1u, ref2.index());
EXPECT_EQ(2u, ref3.index());
}
TEST(StringPoolTest, PruneStringsWithNoReferences) {
StringPool pool;
StringPool::Ref refA = pool.makeRef("foo");
StringPool::Ref refA = pool.MakeRef("foo");
{
StringPool::Ref ref = pool.makeRef("wut");
StringPool::Ref ref = pool.MakeRef("wut");
EXPECT_EQ(*ref, "wut");
EXPECT_EQ(2u, pool.size());
}
StringPool::Ref refB = pool.makeRef("bar");
StringPool::Ref refB = pool.MakeRef("bar");
EXPECT_EQ(3u, pool.size());
pool.prune();
pool.Prune();
EXPECT_EQ(2u, pool.size());
StringPool::const_iterator iter = begin(pool);
EXPECT_EQ((*iter)->value, "foo");
@@ -87,51 +88,51 @@ TEST(StringPoolTest, PruneStringsWithNoReferences) {
TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("z");
StringPool::StyleRef ref2 = pool.makeRef(StyleString{{"a"}});
StringPool::Ref ref3 = pool.makeRef("m");
StringPool::Ref ref = pool.MakeRef("z");
StringPool::StyleRef ref2 = pool.MakeRef(StyleString{{"a"}});
StringPool::Ref ref3 = pool.MakeRef("m");
EXPECT_EQ(*ref, "z");
EXPECT_EQ(0u, ref.getIndex());
EXPECT_EQ(0u, ref.index());
EXPECT_EQ(*(ref2->str), "a");
EXPECT_EQ(1u, ref2.getIndex());
EXPECT_EQ(1u, ref2.index());
EXPECT_EQ(*ref3, "m");
EXPECT_EQ(2u, ref3.getIndex());
EXPECT_EQ(2u, ref3.index());
pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
return a.value < b.value;
});
EXPECT_EQ(*ref, "z");
EXPECT_EQ(2u, ref.getIndex());
EXPECT_EQ(2u, ref.index());
EXPECT_EQ(*(ref2->str), "a");
EXPECT_EQ(0u, ref2.getIndex());
EXPECT_EQ(0u, ref2.index());
EXPECT_EQ(*ref3, "m");
EXPECT_EQ(1u, ref3.getIndex());
EXPECT_EQ(1u, ref3.index());
}
TEST(StringPoolTest, SortAndStillDedupe) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("z");
StringPool::Ref ref2 = pool.makeRef("a");
StringPool::Ref ref3 = pool.makeRef("m");
StringPool::Ref ref = pool.MakeRef("z");
StringPool::Ref ref2 = pool.MakeRef("a");
StringPool::Ref ref3 = pool.MakeRef("m");
pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
return a.value < b.value;
});
StringPool::Ref ref4 = pool.makeRef("z");
StringPool::Ref ref5 = pool.makeRef("a");
StringPool::Ref ref6 = pool.makeRef("m");
StringPool::Ref ref4 = pool.MakeRef("z");
StringPool::Ref ref5 = pool.MakeRef("a");
StringPool::Ref ref6 = pool.MakeRef("m");
EXPECT_EQ(ref4.getIndex(), ref.getIndex());
EXPECT_EQ(ref5.getIndex(), ref2.getIndex());
EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
EXPECT_EQ(ref4.index(), ref.index());
EXPECT_EQ(ref5.index(), ref2.index());
EXPECT_EQ(ref6.index(), ref3.index());
}
TEST(StringPoolTest, AddStyles) {
@@ -139,27 +140,27 @@ TEST(StringPoolTest, AddStyles) {
StyleString str{{"android"}, {Span{{"b"}, 2, 6}}};
StringPool::StyleRef ref = pool.makeRef(str);
StringPool::StyleRef ref = pool.MakeRef(str);
EXPECT_EQ(0u, ref.getIndex());
EXPECT_EQ(0u, ref.index());
EXPECT_EQ(std::string("android"), *(ref->str));
ASSERT_EQ(1u, ref->spans.size());
const StringPool::Span& span = ref->spans.front();
EXPECT_EQ(*(span.name), "b");
EXPECT_EQ(2u, span.firstChar);
EXPECT_EQ(6u, span.lastChar);
EXPECT_EQ(2u, span.first_char);
EXPECT_EQ(6u, span.last_char);
}
TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
StringPool pool;
StringPool::Ref ref = pool.makeRef("android");
StringPool::Ref ref = pool.MakeRef("android");
StyleString str{{"android"}};
StringPool::StyleRef styleRef = pool.makeRef(str);
StringPool::StyleRef styleRef = pool.MakeRef(str);
EXPECT_NE(ref.getIndex(), styleRef.getIndex());
EXPECT_NE(ref.index(), styleRef.index());
}
TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
@@ -167,9 +168,9 @@ TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
StringPool pool;
BigBuffer buffer(1024);
StringPool::flattenUtf8(&buffer, pool);
StringPool::FlattenUtf8(&buffer, pool);
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
}
@@ -178,11 +179,11 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) {
using namespace android; // For NO_ERROR on Windows.
StringPool pool;
pool.makeRef("\u093f");
pool.MakeRef("\u093f");
BigBuffer buffer(1024);
StringPool::flattenUtf16(&buffer, pool);
StringPool::FlattenUtf16(&buffer, pool);
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
size_t len = 0;
@@ -204,58 +205,58 @@ TEST(StringPoolTest, Flatten) {
StringPool pool;
StringPool::Ref ref1 = pool.makeRef("hello");
StringPool::Ref ref2 = pool.makeRef("goodbye");
StringPool::Ref ref3 = pool.makeRef(sLongString);
StringPool::Ref ref4 = pool.makeRef("");
StringPool::StyleRef ref5 = pool.makeRef(
StringPool::Ref ref1 = pool.MakeRef("hello");
StringPool::Ref ref2 = pool.MakeRef("goodbye");
StringPool::Ref ref3 = pool.MakeRef(sLongString);
StringPool::Ref ref4 = pool.MakeRef("");
StringPool::StyleRef ref5 = pool.MakeRef(
StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
EXPECT_EQ(0u, ref1.getIndex());
EXPECT_EQ(1u, ref2.getIndex());
EXPECT_EQ(2u, ref3.getIndex());
EXPECT_EQ(3u, ref4.getIndex());
EXPECT_EQ(4u, ref5.getIndex());
EXPECT_EQ(0u, ref1.index());
EXPECT_EQ(1u, ref2.index());
EXPECT_EQ(2u, ref3.index());
EXPECT_EQ(3u, ref4.index());
EXPECT_EQ(4u, ref5.index());
BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
StringPool::flattenUtf8(&buffers[0], pool);
StringPool::flattenUtf16(&buffers[1], pool);
StringPool::FlattenUtf8(&buffers[0], pool);
StringPool::FlattenUtf16(&buffers[1], pool);
// Test both UTF-8 and UTF-16 buffers.
for (const BigBuffer& buffer : buffers) {
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
EXPECT_EQ(std::string("hello"), util::getString(test, 0));
EXPECT_EQ(StringPiece16(u"hello"), util::getString16(test, 0));
EXPECT_EQ(std::string("hello"), util::GetString(test, 0));
EXPECT_EQ(StringPiece16(u"hello"), util::GetString16(test, 0));
EXPECT_EQ(std::string("goodbye"), util::getString(test, 1));
EXPECT_EQ(StringPiece16(u"goodbye"), util::getString16(test, 1));
EXPECT_EQ(std::string("goodbye"), util::GetString(test, 1));
EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1));
EXPECT_EQ(StringPiece(sLongString), util::getString(test, 2));
EXPECT_EQ(util::utf8ToUtf16(sLongString),
util::getString16(test, 2).toString());
EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2));
EXPECT_EQ(util::Utf8ToUtf16(sLongString),
util::GetString16(test, 2).ToString());
size_t len;
EXPECT_TRUE(test.stringAt(3, &len) != nullptr ||
test.string8At(3, &len) != nullptr);
EXPECT_EQ(std::string("style"), util::getString(test, 4));
EXPECT_EQ(StringPiece16(u"style"), util::getString16(test, 4));
EXPECT_EQ(std::string("style"), util::GetString(test, 4));
EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4));
const ResStringPool_span* span = test.styleAt(4);
ASSERT_NE(nullptr, span);
EXPECT_EQ(std::string("b"), util::getString(test, span->name.index));
EXPECT_EQ(StringPiece16(u"b"), util::getString16(test, span->name.index));
EXPECT_EQ(std::string("b"), util::GetString(test, span->name.index));
EXPECT_EQ(StringPiece16(u"b"), util::GetString16(test, span->name.index));
EXPECT_EQ(0u, span->firstChar);
EXPECT_EQ(1u, span->lastChar);
span++;
ASSERT_NE(ResStringPool_span::END, span->name.index);
EXPECT_EQ(std::string("i"), util::getString(test, span->name.index));
EXPECT_EQ(StringPiece16(u"i"), util::getString16(test, span->name.index));
EXPECT_EQ(std::string("i"), util::GetString(test, span->name.index));
EXPECT_EQ(StringPiece16(u"i"), util::GetString16(test, span->name.index));
EXPECT_EQ(2u, span->firstChar);
EXPECT_EQ(3u, span->lastChar);
span++;

View File

@@ -24,32 +24,31 @@ namespace aapt {
/**
* Visits a value and invokes the appropriate method based on its type. Does not
* traverse
* into compound types. Use ValueVisitor for that.
* traverse into compound types. Use ValueVisitor for that.
*/
struct RawValueVisitor {
virtual ~RawValueVisitor() = default;
virtual void visitItem(Item* value) {}
virtual void visit(Reference* value) { visitItem(value); }
virtual void visit(RawString* value) { visitItem(value); }
virtual void visit(String* value) { visitItem(value); }
virtual void visit(StyledString* value) { visitItem(value); }
virtual void visit(FileReference* value) { visitItem(value); }
virtual void visit(Id* value) { visitItem(value); }
virtual void visit(BinaryPrimitive* value) { visitItem(value); }
virtual void VisitItem(Item* value) {}
virtual void Visit(Reference* value) { VisitItem(value); }
virtual void Visit(RawString* value) { VisitItem(value); }
virtual void Visit(String* value) { VisitItem(value); }
virtual void Visit(StyledString* value) { VisitItem(value); }
virtual void Visit(FileReference* value) { VisitItem(value); }
virtual void Visit(Id* value) { VisitItem(value); }
virtual void Visit(BinaryPrimitive* value) { VisitItem(value); }
virtual void visit(Attribute* value) {}
virtual void visit(Style* value) {}
virtual void visit(Array* value) {}
virtual void visit(Plural* value) {}
virtual void visit(Styleable* value) {}
virtual void Visit(Attribute* value) {}
virtual void Visit(Style* value) {}
virtual void Visit(Array* value) {}
virtual void Visit(Plural* value) {}
virtual void Visit(Styleable* value) {}
};
// NOLINT, do not add parentheses around T.
#define DECL_VISIT_COMPOUND_VALUE(T) \
virtual void visit(T* value) { /* NOLINT */ \
visitSubValues(value); \
#define DECL_VISIT_COMPOUND_VALUE(T) \
virtual void Visit(T* value) override { /* NOLINT */ \
VisitSubValues(value); \
}
/**
@@ -59,44 +58,43 @@ struct RawValueVisitor {
struct ValueVisitor : public RawValueVisitor {
// The compiler will think we're hiding an overload, when we actually intend
// to call into RawValueVisitor. This will expose the visit methods in the
// super
// class so the compiler knows we are trying to call them.
using RawValueVisitor::visit;
// super class so the compiler knows we are trying to call them.
using RawValueVisitor::Visit;
void visitSubValues(Attribute* attribute) {
void VisitSubValues(Attribute* attribute) {
for (Attribute::Symbol& symbol : attribute->symbols) {
visit(&symbol.symbol);
Visit(&symbol.symbol);
}
}
void visitSubValues(Style* style) {
void VisitSubValues(Style* style) {
if (style->parent) {
visit(&style->parent.value());
Visit(&style->parent.value());
}
for (Style::Entry& entry : style->entries) {
visit(&entry.key);
entry.value->accept(this);
Visit(&entry.key);
entry.value->Accept(this);
}
}
void visitSubValues(Array* array) {
void VisitSubValues(Array* array) {
for (std::unique_ptr<Item>& item : array->items) {
item->accept(this);
item->Accept(this);
}
}
void visitSubValues(Plural* plural) {
void VisitSubValues(Plural* plural) {
for (std::unique_ptr<Item>& item : plural->values) {
if (item) {
item->accept(this);
item->Accept(this);
}
}
}
void visitSubValues(Styleable* styleable) {
void VisitSubValues(Styleable* styleable) {
for (Reference& reference : styleable->entries) {
visit(&reference);
Visit(&reference);
}
}
@@ -114,7 +112,7 @@ template <typename T>
struct DynCastVisitor : public RawValueVisitor {
T* value = nullptr;
void visit(T* v) override { value = v; }
void Visit(T* v) override { value = v; }
};
/**
@@ -124,12 +122,12 @@ template <>
struct DynCastVisitor<Item> : public RawValueVisitor {
Item* value = nullptr;
void visitItem(Item* item) override { value = item; }
void VisitItem(Item* item) override { value = item; }
};
template <typename T>
const T* valueCast(const Value* value) {
return valueCast<T>(const_cast<Value*>(value));
const T* ValueCast(const Value* value) {
return ValueCast<T>(const_cast<Value*>(value));
}
/**
@@ -137,30 +135,30 @@ const T* valueCast(const Value* value) {
* Otherwise, returns nullptr.
*/
template <typename T>
T* valueCast(Value* value) {
T* ValueCast(Value* value) {
if (!value) {
return nullptr;
}
DynCastVisitor<T> visitor;
value->accept(&visitor);
value->Accept(&visitor);
return visitor.value;
}
inline void visitAllValuesInPackage(ResourceTablePackage* pkg,
inline void VisitAllValuesInPackage(ResourceTablePackage* pkg,
RawValueVisitor* visitor) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& configValue : entry->values) {
configValue->value->accept(visitor);
for (auto& config_value : entry->values) {
config_value->value->Accept(visitor);
}
}
}
}
inline void visitAllValuesInTable(ResourceTable* table,
inline void VisitAllValuesInTable(ResourceTable* table,
RawValueVisitor* visitor) {
for (auto& pkg : table->packages) {
visitAllValuesInPackage(pkg.get(), visitor);
VisitAllValuesInPackage(pkg.get(), visitor);
}
}

View File

@@ -15,40 +15,41 @@
*/
#include "ValueVisitor.h"
#include <string>
#include "ResourceValues.h"
#include "test/Test.h"
#include "util/Util.h"
#include <string>
namespace aapt {
struct SingleReferenceVisitor : public ValueVisitor {
using ValueVisitor::visit;
using ValueVisitor::Visit;
Reference* visited = nullptr;
void visit(Reference* ref) override { visited = ref; }
void Visit(Reference* ref) override { visited = ref; }
};
struct StyleVisitor : public ValueVisitor {
using ValueVisitor::visit;
using ValueVisitor::Visit;
std::list<Reference*> visitedRefs;
Style* visitedStyle = nullptr;
std::list<Reference*> visited_refs;
Style* visited_style = nullptr;
void visit(Reference* ref) override { visitedRefs.push_back(ref); }
void Visit(Reference* ref) override { visited_refs.push_back(ref); }
void visit(Style* style) override {
visitedStyle = style;
ValueVisitor::visit(style);
void Visit(Style* style) override {
visited_style = style;
ValueVisitor::Visit(style);
}
};
TEST(ValueVisitorTest, VisitsReference) {
Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"});
SingleReferenceVisitor visitor;
ref.accept(&visitor);
ref.Accept(&visitor);
EXPECT_EQ(visitor.visited, &ref);
}
@@ -56,31 +57,31 @@ TEST(ValueVisitorTest, VisitsReference) {
TEST(ValueVisitorTest, VisitsReferencesInStyle) {
std::unique_ptr<Style> style =
test::StyleBuilder()
.setParent("android:style/foo")
.addItem("android:attr/one", test::buildReference("android:id/foo"))
.build();
.SetParent("android:style/foo")
.AddItem("android:attr/one", test::BuildReference("android:id/foo"))
.Build();
StyleVisitor visitor;
style->accept(&visitor);
style->Accept(&visitor);
ASSERT_EQ(style.get(), visitor.visitedStyle);
ASSERT_EQ(style.get(), visitor.visited_style);
// Entry attribute references, plus the parent reference, plus one value
// reference.
ASSERT_EQ(style->entries.size() + 2, visitor.visitedRefs.size());
ASSERT_EQ(style->entries.size() + 2, visitor.visited_refs.size());
}
TEST(ValueVisitorTest, ValueCast) {
std::unique_ptr<Reference> ref = test::buildReference("android:color/white");
EXPECT_NE(valueCast<Reference>(ref.get()), nullptr);
std::unique_ptr<Reference> ref = test::BuildReference("android:color/white");
EXPECT_NE(ValueCast<Reference>(ref.get()), nullptr);
std::unique_ptr<Style> style =
test::StyleBuilder()
.addItem("android:attr/foo",
test::buildReference("android:color/black"))
.build();
EXPECT_NE(valueCast<Style>(style.get()), nullptr);
EXPECT_EQ(valueCast<Reference>(style.get()), nullptr);
.AddItem("android:attr/foo",
test::BuildReference("android:color/black"))
.Build();
EXPECT_NE(ValueCast<Style>(style.get()), nullptr);
EXPECT_EQ(ValueCast<Reference>(style.get()), nullptr);
}
} // namespace aapt

File diff suppressed because it is too large Load Diff

View File

@@ -15,13 +15,15 @@
*/
#include "compile/IdAssigner.h"
#include <map>
#include "android-base/logging.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
#include <cassert>
#include <map>
namespace aapt {
/**
@@ -29,44 +31,44 @@ namespace aapt {
* ResourceEntry,
* as long as there is no existing ID or the ID is the same.
*/
static bool assignId(IDiagnostics* diag, const ResourceId& id,
static bool AssignId(IDiagnostics* diag, const ResourceId& id,
const ResourceName& name, ResourceTablePackage* pkg,
ResourceTableType* type, ResourceEntry* entry) {
if (pkg->id.value() == id.packageId()) {
if (!type->id || type->id.value() == id.typeId()) {
type->id = id.typeId();
if (pkg->id.value() == id.package_id()) {
if (!type->id || type->id.value() == id.type_id()) {
type->id = id.type_id();
if (!entry->id || entry->id.value() == id.entryId()) {
entry->id = id.entryId();
if (!entry->id || entry->id.value() == id.entry_id()) {
entry->id = id.entry_id();
return true;
}
}
}
const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0,
entry->id ? entry->id.value() : 0);
diag->error(DiagMessage() << "can't assign ID " << id << " to resource "
<< name << " with conflicting ID " << existingId);
const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
entry->id ? entry->id.value() : 0);
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
<< name << " with conflicting ID " << existing_id);
return false;
}
bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
std::map<ResourceId, ResourceName> assignedIds;
bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
std::map<ResourceId, ResourceName> assigned_ids;
for (auto& package : table->packages) {
assert(package->id && "packages must have manually assigned IDs");
CHECK(bool(package->id)) << "packages must have manually assigned IDs";
for (auto& type : package->types) {
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
if (mAssignedIdMap) {
if (assigned_id_map_) {
// Assign the pre-assigned stable ID meant for this resource.
const auto iter = mAssignedIdMap->find(name);
if (iter != mAssignedIdMap->end()) {
const ResourceId assignedId = iter->second;
const auto iter = assigned_id_map_->find(name);
if (iter != assigned_id_map_->end()) {
const ResourceId assigned_id = iter->second;
const bool result =
assignId(context->getDiagnostics(), assignedId, name,
AssignId(context->GetDiagnostics(), assigned_id, name,
package.get(), type.get(), entry.get());
if (!result) {
return false;
@@ -76,14 +78,14 @@ bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
if (package->id && type->id && entry->id) {
// If the ID is set for this resource, then reserve it.
ResourceId resourceId(package->id.value(), type->id.value(),
entry->id.value());
auto result = assignedIds.insert({resourceId, name});
const ResourceName& existingName = result.first->second;
ResourceId resource_id(package->id.value(), type->id.value(),
entry->id.value());
auto result = assigned_ids.insert({resource_id, name});
const ResourceName& existing_name = result.first->second;
if (!result.second) {
context->getDiagnostics()->error(
context->GetDiagnostics()->Error(
DiagMessage() << "resource " << name << " has same ID "
<< resourceId << " as " << existingName);
<< resource_id << " as " << existing_name);
return false;
}
}
@@ -91,20 +93,20 @@ bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
}
}
if (mAssignedIdMap) {
if (assigned_id_map_) {
// Reserve all the IDs mentioned in the stable ID map. That way we won't
// assign
// IDs that were listed in the map if they don't exist in the table.
for (const auto& stableIdEntry : *mAssignedIdMap) {
const ResourceName& preAssignedName = stableIdEntry.first;
const ResourceId& preAssignedId = stableIdEntry.second;
auto result = assignedIds.insert({preAssignedId, preAssignedName});
const ResourceName& existingName = result.first->second;
if (!result.second && existingName != preAssignedName) {
context->getDiagnostics()->error(
DiagMessage() << "stable ID " << preAssignedId << " for resource "
<< preAssignedName << " is already taken by resource "
<< existingName);
for (const auto& stable_id_entry : *assigned_id_map_) {
const ResourceName& pre_assigned_name = stable_id_entry.first;
const ResourceId& pre_assigned_id = stable_id_entry.second;
auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
const ResourceName& existing_name = result.first->second;
if (!result.second && existing_name != pre_assigned_name) {
context->GetDiagnostics()->Error(
DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
<< pre_assigned_name
<< " is already taken by resource " << existing_name);
return false;
}
}
@@ -114,21 +116,21 @@ bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
// if possible,
// unless those IDs have been reserved.
const auto assignedIdsIterEnd = assignedIds.end();
const auto assigned_ids_iter_end = assigned_ids.end();
for (auto& package : table->packages) {
assert(package->id && "packages must have manually assigned IDs");
CHECK(bool(package->id)) << "packages must have manually assigned IDs";
// Build a half filled ResourceId object, which will be used to find the
// closest matching
// reserved ID in the assignedId map. From that point the next available
// type ID can be
// found.
ResourceId resourceId(package->id.value(), 0, 0);
uint8_t nextExpectedTypeId = 1;
ResourceId resource_id(package->id.value(), 0, 0);
uint8_t next_expected_type_id = 1;
// Find the closest matching ResourceId that is <= the one with only the
// package set.
auto nextTypeIter = assignedIds.lower_bound(resourceId);
auto next_type_iter = assigned_ids.lower_bound(resource_id);
for (auto& type : package->types) {
if (!type->id) {
// We need to assign a type ID. Iterate over the reserved IDs until we
@@ -136,41 +138,41 @@ bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
// some type ID that is a distance of 2 greater than the last one we've
// seen.
// That means there is an available type ID between these reserved IDs.
while (nextTypeIter != assignedIdsIterEnd) {
if (nextTypeIter->first.packageId() != package->id.value()) {
while (next_type_iter != assigned_ids_iter_end) {
if (next_type_iter->first.package_id() != package->id.value()) {
break;
}
const uint8_t typeId = nextTypeIter->first.typeId();
if (typeId > nextExpectedTypeId) {
const uint8_t type_id = next_type_iter->first.type_id();
if (type_id > next_expected_type_id) {
// There is a gap in the type IDs, so use the missing one.
type->id = nextExpectedTypeId++;
type->id = next_expected_type_id++;
break;
}
// Set our expectation to be the next type ID after the reserved one
// we
// just saw.
nextExpectedTypeId = typeId + 1;
next_expected_type_id = type_id + 1;
// Move to the next reserved ID.
++nextTypeIter;
++next_type_iter;
}
if (!type->id) {
// We must have hit the end of the reserved IDs and not found a gap.
// That means the next ID is available.
type->id = nextExpectedTypeId++;
type->id = next_expected_type_id++;
}
}
resourceId = ResourceId(package->id.value(), type->id.value(), 0);
uint16_t nextExpectedEntryId = 0;
resource_id = ResourceId(package->id.value(), type->id.value(), 0);
uint16_t next_expected_entry_id = 0;
// Find the closest matching ResourceId that is <= the one with only the
// package
// and type set.
auto nextEntryIter = assignedIds.lower_bound(resourceId);
auto next_entry_iter = assigned_ids.lower_bound(resource_id);
for (auto& entry : type->entries) {
if (!entry->id) {
// We need to assign an entry ID. Iterate over the reserved IDs until
@@ -179,32 +181,32 @@ bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
// we've seen.
// That means there is an available entry ID between these reserved
// IDs.
while (nextEntryIter != assignedIdsIterEnd) {
if (nextEntryIter->first.packageId() != package->id.value() ||
nextEntryIter->first.typeId() != type->id.value()) {
while (next_entry_iter != assigned_ids_iter_end) {
if (next_entry_iter->first.package_id() != package->id.value() ||
next_entry_iter->first.type_id() != type->id.value()) {
break;
}
const uint16_t entryId = nextEntryIter->first.entryId();
if (entryId > nextExpectedEntryId) {
const uint16_t entry_id = next_entry_iter->first.entry_id();
if (entry_id > next_expected_entry_id) {
// There is a gap in the entry IDs, so use the missing one.
entry->id = nextExpectedEntryId++;
entry->id = next_expected_entry_id++;
break;
}
// Set our expectation to be the next type ID after the reserved one
// we
// just saw.
nextExpectedEntryId = entryId + 1;
next_expected_entry_id = entry_id + 1;
// Move to the next reserved entry ID.
++nextEntryIter;
++next_entry_iter;
}
if (!entry->id) {
// We must have hit the end of the reserved IDs and not found a gap.
// That means the next ID is available.
entry->id = nextExpectedEntryId++;
entry->id = next_expected_entry_id++;
}
}
}

View File

@@ -17,11 +17,12 @@
#ifndef AAPT_COMPILE_IDASSIGNER_H
#define AAPT_COMPILE_IDASSIGNER_H
#include <unordered_map>
#include "Resource.h"
#include "process/IResourceTableConsumer.h"
#include <android-base/macros.h>
#include <unordered_map>
#include "android-base/macros.h"
namespace aapt {
@@ -34,12 +35,13 @@ class IdAssigner : public IResourceTableConsumer {
public:
IdAssigner() = default;
explicit IdAssigner(const std::unordered_map<ResourceName, ResourceId>* map)
: mAssignedIdMap(map) {}
: assigned_id_map_(map) {}
bool consume(IAaptContext* context, ResourceTable* table) override;
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
const std::unordered_map<ResourceName, ResourceId>* mAssignedIdMap = nullptr;
const std::unordered_map<ResourceName, ResourceId>* assigned_id_map_ =
nullptr;
};
} // namespace aapt

View File

@@ -15,128 +15,129 @@
*/
#include "compile/IdAssigner.h"
#include "test/Test.h"
namespace aapt {
::testing::AssertionResult verifyIds(ResourceTable* table);
::testing::AssertionResult VerifyIds(ResourceTable* table);
TEST(IdAssignerTest, AssignIds) {
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
.addSimple("android:attr/foo")
.addSimple("android:attr/bar")
.addSimple("android:id/foo")
.setPackageId("android", 0x01)
.build();
.AddSimple("android:attr/foo")
.AddSimple("android:attr/bar")
.AddSimple("android:id/foo")
.SetPackageId("android", 0x01)
.Build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
IdAssigner assigner;
ASSERT_TRUE(assigner.consume(context.get(), table.get()));
ASSERT_TRUE(verifyIds(table.get()));
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
}
TEST(IdAssignerTest, AssignIdsWithReservedIds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.addSimple("android:id/foo", ResourceId(0x01010000))
.addSimple("android:dimen/two")
.addSimple("android:integer/three")
.addSimple("android:string/five")
.addSimple("android:attr/fun", ResourceId(0x01040000))
.addSimple("android:attr/foo", ResourceId(0x01040006))
.addSimple("android:attr/bar")
.addSimple("android:attr/baz")
.addSimple("app:id/biz")
.setPackageId("android", 0x01)
.setPackageId("app", 0x7f)
.build();
.AddSimple("android:id/foo", ResourceId(0x01010000))
.AddSimple("android:dimen/two")
.AddSimple("android:integer/three")
.AddSimple("android:string/five")
.AddSimple("android:attr/fun", ResourceId(0x01040000))
.AddSimple("android:attr/foo", ResourceId(0x01040006))
.AddSimple("android:attr/bar")
.AddSimple("android:attr/baz")
.AddSimple("app:id/biz")
.SetPackageId("android", 0x01)
.SetPackageId("app", 0x7f)
.Build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
IdAssigner assigner;
ASSERT_TRUE(assigner.consume(context.get(), table.get()));
ASSERT_TRUE(verifyIds(table.get()));
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
Maybe<ResourceTable::SearchResult> maybeResult;
Maybe<ResourceTable::SearchResult> maybe_result;
// Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
maybeResult = table->findResource(test::parseNameOrDie("android:dimen/two"));
AAPT_ASSERT_TRUE(maybeResult);
EXPECT_EQ(make_value<uint8_t>(2), maybeResult.value().type->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
AAPT_ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(2), maybe_result.value().type->id);
maybeResult =
table->findResource(test::parseNameOrDie("android:integer/three"));
AAPT_ASSERT_TRUE(maybeResult);
EXPECT_EQ(make_value<uint8_t>(3), maybeResult.value().type->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:integer/three"));
AAPT_ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(3), maybe_result.value().type->id);
// Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
// IDs.
maybeResult =
table->findResource(test::parseNameOrDie("android:string/five"));
AAPT_ASSERT_TRUE(maybeResult);
EXPECT_EQ(make_value<uint8_t>(5), maybeResult.value().type->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:string/five"));
AAPT_ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(5), maybe_result.value().type->id);
// Expect to fill in the gaps between 0x01040000 and 0x01040006.
maybeResult = table->findResource(test::parseNameOrDie("android:attr/bar"));
AAPT_ASSERT_TRUE(maybeResult);
EXPECT_EQ(make_value<uint16_t>(1), maybeResult.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
AAPT_ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint16_t>(1), maybe_result.value().entry->id);
maybeResult = table->findResource(test::parseNameOrDie("android:attr/baz"));
AAPT_ASSERT_TRUE(maybeResult);
EXPECT_EQ(make_value<uint16_t>(2), maybeResult.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
AAPT_ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint16_t>(2), maybe_result.value().entry->id);
}
TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.addSimple("android:attr/foo", ResourceId(0x01040006))
.addSimple("android:attr/bar", ResourceId(0x01040006))
.setPackageId("android", 0x01)
.setPackageId("app", 0x7f)
.build();
.AddSimple("android:attr/foo", ResourceId(0x01040006))
.AddSimple("android:attr/bar", ResourceId(0x01040006))
.SetPackageId("android", 0x01)
.SetPackageId("app", 0x7f)
.Build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
IdAssigner assigner;
ASSERT_FALSE(assigner.consume(context.get(), table.get()));
ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
TEST(IdAssignerTest, AssignIdsWithIdMap) {
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
.addSimple("android:attr/foo")
.addSimple("android:attr/bar")
.setPackageId("android", 0x01)
.build();
.AddSimple("android:attr/foo")
.AddSimple("android:attr/bar")
.SetPackageId("android", 0x01)
.Build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unordered_map<ResourceName, ResourceId> idMap = {
{test::parseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
IdAssigner assigner(&idMap);
ASSERT_TRUE(assigner.consume(context.get(), table.get()));
ASSERT_TRUE(verifyIds(table.get()));
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unordered_map<ResourceName, ResourceId> id_map = {
{test::ParseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
IdAssigner assigner(&id_map);
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
Maybe<ResourceTable::SearchResult> result =
table->findResource(test::parseNameOrDie("android:attr/foo"));
table->FindResource(test::ParseNameOrDie("android:attr/foo"));
AAPT_ASSERT_TRUE(result);
const ResourceTable::SearchResult& searchResult = result.value();
EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.package->id);
EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.type->id);
EXPECT_EQ(make_value<uint16_t>(0x0002), searchResult.entry->id);
const ResourceTable::SearchResult& search_result = result.value();
EXPECT_EQ(make_value<uint8_t>(0x01), search_result.package->id);
EXPECT_EQ(make_value<uint8_t>(0x01), search_result.type->id);
EXPECT_EQ(make_value<uint16_t>(0x0002), search_result.entry->id);
}
::testing::AssertionResult verifyIds(ResourceTable* table) {
std::set<uint8_t> packageIds;
::testing::AssertionResult VerifyIds(ResourceTable* table) {
std::set<uint8_t> package_ids;
for (auto& package : table->packages) {
if (!package->id) {
return ::testing::AssertionFailure() << "package " << package->name
<< " has no ID";
}
if (!packageIds.insert(package->id.value()).second) {
if (!package_ids.insert(package->id.value()).second) {
return ::testing::AssertionFailure()
<< "package " << package->name << " has non-unique ID " << std::hex
<< (int)package->id.value() << std::dec;
@@ -144,7 +145,7 @@ TEST(IdAssignerTest, AssignIdsWithIdMap) {
}
for (auto& package : table->packages) {
std::set<uint8_t> typeIds;
std::set<uint8_t> type_ids;
for (auto& type : package->types) {
if (!type->id) {
return ::testing::AssertionFailure() << "type " << type->type
@@ -152,7 +153,7 @@ TEST(IdAssignerTest, AssignIdsWithIdMap) {
<< " has no ID";
}
if (!typeIds.insert(type->id.value()).second) {
if (!type_ids.insert(type->id.value()).second) {
return ::testing::AssertionFailure()
<< "type " << type->type << " of package " << package->name
<< " has non-unique ID " << std::hex << (int)type->id.value()
@@ -161,7 +162,7 @@ TEST(IdAssignerTest, AssignIdsWithIdMap) {
}
for (auto& type : package->types) {
std::set<uint16_t> entryIds;
std::set<uint16_t> entry_ids;
for (auto& entry : type->entries) {
if (!entry->id) {
return ::testing::AssertionFailure()
@@ -169,7 +170,7 @@ TEST(IdAssignerTest, AssignIdsWithIdMap) {
<< " of package " << package->name << " has no ID";
}
if (!entryIds.insert(entry->id.value()).second) {
if (!entry_ids.insert(entry->id.value()).second) {
return ::testing::AssertionFailure()
<< "entry " << entry->name << " of type " << type->type
<< " of package " << package->name << " has non-unique ID "

View File

@@ -17,12 +17,13 @@
#ifndef AAPT_COMPILE_IMAGE_H
#define AAPT_COMPILE_IMAGE_H
#include <android-base/macros.h>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "android-base/macros.h"
namespace aapt {
/**
@@ -113,15 +114,15 @@ inline bool operator==(const Bounds& left, const Bounds& right) {
*/
class NinePatch {
public:
static std::unique_ptr<NinePatch> create(uint8_t** rows, const int32_t width,
static std::unique_ptr<NinePatch> Create(uint8_t** rows, const int32_t width,
const int32_t height,
std::string* errOut);
std::string* err_out);
/**
* Packs the RGBA_8888 data pointed to by pixel into a uint32_t
* with format 0xAARRGGBB (the way 9-patch expects it).
*/
static uint32_t packRGBA(const uint8_t* pixel);
static uint32_t PackRGBA(const uint8_t* pixel);
/**
* 9-patch content padding/insets. All positions are relative to the 9-patch
@@ -136,7 +137,7 @@ class NinePatch {
* See
* https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
*/
Bounds layoutBounds;
Bounds layout_bounds;
/**
* Outline of the image, calculated based on opacity.
@@ -147,51 +148,51 @@ class NinePatch {
* The computed radius of the outline. If non-zero, the outline is a
* rounded-rect.
*/
float outlineRadius = 0.0f;
float outline_radius = 0.0f;
/**
* The largest alpha value within the outline.
*/
uint32_t outlineAlpha = 0x000000ffu;
uint32_t outline_alpha = 0x000000ffu;
/**
* Horizontal regions of the image that are stretchable.
* All positions are relative to the 9-patch
* NOT including the 1px thick source border.
*/
std::vector<Range> horizontalStretchRegions;
std::vector<Range> horizontal_stretch_regions;
/**
* Vertical regions of the image that are stretchable.
* All positions are relative to the 9-patch
* NOT including the 1px thick source border.
*/
std::vector<Range> verticalStretchRegions;
std::vector<Range> vertical_stretch_regions;
/**
* The colors within each region, fixed or stretchable.
* For w*h regions, the color of region (x,y) is addressable
* via index y*w + x.
*/
std::vector<uint32_t> regionColors;
std::vector<uint32_t> region_colors;
/**
* Returns serialized data containing the original basic 9-patch meta data.
* Optical layout bounds and round rect outline data must be serialized
* separately using serializeOpticalLayoutBounds() and
* serializeRoundedRectOutline().
* separately using SerializeOpticalLayoutBounds() and
* SerializeRoundedRectOutline().
*/
std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
std::unique_ptr<uint8_t[]> SerializeBase(size_t* out_len) const;
/**
* Serializes the layout bounds.
*/
std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
std::unique_ptr<uint8_t[]> SerializeLayoutBounds(size_t* out_len) const;
/**
* Serializes the rounded-rect outline.
*/
std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
std::unique_ptr<uint8_t[]> SerializeRoundedRectOutline(size_t* out_len) const;
private:
explicit NinePatch() = default;
@@ -201,7 +202,7 @@ class NinePatch {
::std::ostream& operator<<(::std::ostream& out, const Range& range);
::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch);
} // namespace aapt

View File

@@ -15,16 +15,18 @@
*/
#include "compile/InlineXmlFormatParser.h"
#include <sstream>
#include <string>
#include "android-base/macros.h"
#include "Debug.h"
#include "ResourceUtils.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlUtil.h"
#include <android-base/macros.h>
#include <sstream>
#include <string>
namespace aapt {
namespace {
@@ -34,38 +36,38 @@ namespace {
*/
class Visitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::visit;
using xml::PackageAwareVisitor::Visit;
struct InlineDeclaration {
xml::Element* el;
std::string attrNamespaceUri;
std::string attrName;
std::string attr_namespace_uri;
std::string attr_name;
};
explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource)
: mContext(context), mXmlResource(xmlResource) {}
explicit Visitor(IAaptContext* context, xml::XmlResource* xml_resource)
: context_(context), xml_resource_(xml_resource) {}
void visit(xml::Element* el) override {
if (el->namespaceUri != xml::kSchemaAapt || el->name != "attr") {
xml::PackageAwareVisitor::visit(el);
void Visit(xml::Element* el) override {
if (el->namespace_uri != xml::kSchemaAapt || el->name != "attr") {
xml::PackageAwareVisitor::Visit(el);
return;
}
const Source& src = mXmlResource->file.source.withLine(el->lineNumber);
const Source& src = xml_resource_->file.source.WithLine(el->line_number);
xml::Attribute* attr = el->findAttribute({}, "name");
xml::Attribute* attr = el->FindAttribute({}, "name");
if (!attr) {
mContext->getDiagnostics()->error(DiagMessage(src)
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "missing 'name' attribute");
mError = true;
error_ = true;
return;
}
Maybe<Reference> ref = ResourceUtils::parseXmlAttributeName(attr->value);
Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
if (!ref) {
mContext->getDiagnostics()->error(
context_->GetDiagnostics()->Error(
DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
mError = true;
error_ = true;
return;
}
@@ -76,63 +78,63 @@ class Visitor : public xml::PackageAwareVisitor {
// the local package if the user specified name="style" or something. This
// should just
// be the default namespace.
Maybe<xml::ExtractedPackage> maybePkg =
transformPackageAlias(name.package, {});
if (!maybePkg) {
mContext->getDiagnostics()->error(DiagMessage(src)
Maybe<xml::ExtractedPackage> maybe_pkg =
TransformPackageAlias(name.package, {});
if (!maybe_pkg) {
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "invalid namespace prefix '"
<< name.package << "'");
mError = true;
error_ = true;
return;
}
const xml::ExtractedPackage& pkg = maybePkg.value();
const bool privateNamespace =
pkg.privateNamespace || ref.value().privateReference;
const xml::ExtractedPackage& pkg = maybe_pkg.value();
const bool private_namespace =
pkg.private_namespace || ref.value().private_reference;
InlineDeclaration decl;
decl.el = el;
decl.attrName = name.entry;
decl.attr_name = name.entry;
if (!pkg.package.empty()) {
decl.attrNamespaceUri =
xml::buildPackageNamespace(pkg.package, privateNamespace);
decl.attr_namespace_uri =
xml::BuildPackageNamespace(pkg.package, private_namespace);
}
mInlineDeclarations.push_back(std::move(decl));
inline_declarations_.push_back(std::move(decl));
}
const std::vector<InlineDeclaration>& getInlineDeclarations() const {
return mInlineDeclarations;
const std::vector<InlineDeclaration>& GetInlineDeclarations() const {
return inline_declarations_;
}
bool hasError() const { return mError; }
bool HasError() const { return error_; }
private:
DISALLOW_COPY_AND_ASSIGN(Visitor);
IAaptContext* mContext;
xml::XmlResource* mXmlResource;
std::vector<InlineDeclaration> mInlineDeclarations;
bool mError = false;
IAaptContext* context_;
xml::XmlResource* xml_resource_;
std::vector<InlineDeclaration> inline_declarations_;
bool error_ = false;
};
} // namespace
bool InlineXmlFormatParser::consume(IAaptContext* context,
bool InlineXmlFormatParser::Consume(IAaptContext* context,
xml::XmlResource* doc) {
Visitor visitor(context, doc);
doc->root->accept(&visitor);
if (visitor.hasError()) {
doc->root->Accept(&visitor);
if (visitor.HasError()) {
return false;
}
size_t nameSuffixCounter = 0;
size_t name_suffix_counter = 0;
for (const Visitor::InlineDeclaration& decl :
visitor.getInlineDeclarations()) {
auto newDoc = util::make_unique<xml::XmlResource>();
newDoc->file.config = doc->file.config;
newDoc->file.source = doc->file.source.withLine(decl.el->lineNumber);
newDoc->file.name = doc->file.name;
visitor.GetInlineDeclarations()) {
auto new_doc = util::make_unique<xml::XmlResource>();
new_doc->file.config = doc->file.config;
new_doc->file.source = doc->file.source.WithLine(decl.el->line_number);
new_doc->file.name = doc->file.name;
// Modify the new entry name. We need to suffix the entry with a number to
// avoid
@@ -140,63 +142,64 @@ bool InlineXmlFormatParser::consume(IAaptContext* context,
// won't show up
// in R.java.
newDoc->file.name.entry = NameMangler::mangleEntry(
{}, newDoc->file.name.entry + "__" + std::to_string(nameSuffixCounter));
new_doc->file.name.entry =
NameMangler::MangleEntry({}, new_doc->file.name.entry + "__" +
std::to_string(name_suffix_counter));
// Extracted elements must be the only child of <aapt:attr>.
// Make sure there is one root node in the children (ignore empty text).
for (auto& child : decl.el->children) {
const Source childSource = doc->file.source.withLine(child->lineNumber);
if (xml::Text* t = xml::nodeCast<xml::Text>(child.get())) {
if (!util::trimWhitespace(t->text).empty()) {
context->getDiagnostics()->error(
DiagMessage(childSource)
const Source child_source = doc->file.source.WithLine(child->line_number);
if (xml::Text* t = xml::NodeCast<xml::Text>(child.get())) {
if (!util::TrimWhitespace(t->text).empty()) {
context->GetDiagnostics()->Error(
DiagMessage(child_source)
<< "can't extract text into its own resource");
return false;
}
} else if (newDoc->root) {
context->getDiagnostics()->error(
DiagMessage(childSource)
} else if (new_doc->root) {
context->GetDiagnostics()->Error(
DiagMessage(child_source)
<< "inline XML resources must have a single root");
return false;
} else {
newDoc->root = std::move(child);
newDoc->root->parent = nullptr;
new_doc->root = std::move(child);
new_doc->root->parent = nullptr;
}
}
// Walk up and find the parent element.
xml::Node* node = decl.el;
xml::Element* parentEl = nullptr;
xml::Element* parent_el = nullptr;
while (node->parent &&
(parentEl = xml::nodeCast<xml::Element>(node->parent)) == nullptr) {
(parent_el = xml::NodeCast<xml::Element>(node->parent)) == nullptr) {
node = node->parent;
}
if (!parentEl) {
context->getDiagnostics()->error(
DiagMessage(newDoc->file.source)
if (!parent_el) {
context->GetDiagnostics()->Error(
DiagMessage(new_doc->file.source)
<< "no suitable parent for inheriting attribute");
return false;
}
// Add the inline attribute to the parent.
parentEl->attributes.push_back(
xml::Attribute{decl.attrNamespaceUri, decl.attrName,
"@" + newDoc->file.name.toString()});
parent_el->attributes.push_back(
xml::Attribute{decl.attr_namespace_uri, decl.attr_name,
"@" + new_doc->file.name.ToString()});
// Delete the subtree.
for (auto iter = parentEl->children.begin();
iter != parentEl->children.end(); ++iter) {
for (auto iter = parent_el->children.begin();
iter != parent_el->children.end(); ++iter) {
if (iter->get() == node) {
parentEl->children.erase(iter);
parent_el->children.erase(iter);
break;
}
}
mQueue.push_back(std::move(newDoc));
queue_.push_back(std::move(new_doc));
nameSuffixCounter++;
name_suffix_counter++;
}
return true;
}

View File

@@ -17,12 +17,13 @@
#ifndef AAPT_COMPILE_INLINEXMLFORMATPARSER_H
#define AAPT_COMPILE_INLINEXMLFORMATPARSER_H
#include "process/IResourceTableConsumer.h"
#include <android-base/macros.h>
#include <memory>
#include <vector>
#include "android-base/macros.h"
#include "process/IResourceTableConsumer.h"
namespace aapt {
/**
@@ -50,17 +51,17 @@ class InlineXmlFormatParser : public IXmlResourceConsumer {
public:
explicit InlineXmlFormatParser() = default;
bool consume(IAaptContext* context, xml::XmlResource* doc) override;
bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
std::vector<std::unique_ptr<xml::XmlResource>>&
getExtractedInlineXmlDocuments() {
return mQueue;
GetExtractedInlineXmlDocuments() {
return queue_;
}
private:
DISALLOW_COPY_AND_ASSIGN(InlineXmlFormatParser);
std::vector<std::unique_ptr<xml::XmlResource>> mQueue;
std::vector<std::unique_ptr<xml::XmlResource>> queue_;
};
} // namespace aapt

View File

@@ -15,13 +15,14 @@
*/
#include "compile/InlineXmlFormatParser.h"
#include "test/Test.h"
namespace aapt {
TEST(InlineXmlFormatParserTest, PassThrough) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<View android:text="hey">
<View android:id="hi" />
@@ -29,13 +30,13 @@ TEST(InlineXmlFormatParserTest, PassThrough) {
</View>)EOF");
InlineXmlFormatParser parser;
ASSERT_TRUE(parser.consume(context.get(), doc.get()));
EXPECT_EQ(0u, parser.getExtractedInlineXmlDocuments().size());
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
EXPECT_EQ(0u, parser.GetExtractedInlineXmlDocuments().size());
}
TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -45,48 +46,48 @@ TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
</aapt:attr>
</View1>)EOF");
doc->file.name = test::parseNameOrDie("layout/main");
doc->file.name = test::ParseNameOrDie("layout/main");
InlineXmlFormatParser parser;
ASSERT_TRUE(parser.consume(context.get(), doc.get()));
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
// One XML resource should have been extracted.
EXPECT_EQ(1u, parser.getExtractedInlineXmlDocuments().size());
EXPECT_EQ(1u, parser.GetExtractedInlineXmlDocuments().size());
xml::Element* el = xml::findRootElement(doc.get());
xml::Element* el = xml::FindRootElement(doc.get());
ASSERT_NE(nullptr, el);
EXPECT_EQ("View1", el->name);
// The <aapt:attr> tag should be extracted.
EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
// The 'android:text' attribute should be set with a reference.
xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "text");
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "text");
ASSERT_NE(nullptr, attr);
ResourceNameRef nameRef;
ASSERT_TRUE(ResourceUtils::parseReference(attr->value, &nameRef));
ResourceNameRef name_ref;
ASSERT_TRUE(ResourceUtils::ParseReference(attr->value, &name_ref));
xml::XmlResource* extractedDoc =
parser.getExtractedInlineXmlDocuments()[0].get();
ASSERT_NE(nullptr, extractedDoc);
xml::XmlResource* extracted_doc =
parser.GetExtractedInlineXmlDocuments()[0].get();
ASSERT_NE(nullptr, extracted_doc);
// Make sure the generated reference is correct.
EXPECT_EQ(nameRef.package, extractedDoc->file.name.package);
EXPECT_EQ(nameRef.type, extractedDoc->file.name.type);
EXPECT_EQ(nameRef.entry, extractedDoc->file.name.entry);
EXPECT_EQ(name_ref.package, extracted_doc->file.name.package);
EXPECT_EQ(name_ref.type, extracted_doc->file.name.type);
EXPECT_EQ(name_ref.entry, extracted_doc->file.name.entry);
// Verify the structure of the extracted XML.
el = xml::findRootElement(extractedDoc);
el = xml::FindRootElement(extracted_doc);
ASSERT_NE(nullptr, el);
EXPECT_EQ("View2", el->name);
EXPECT_NE(nullptr, el->findChild({}, "View3"));
EXPECT_NE(nullptr, el->FindChild({}, "View3"));
}
TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -100,41 +101,41 @@ TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
</aapt:attr>
</View1>)EOF");
doc->file.name = test::parseNameOrDie("layout/main");
doc->file.name = test::ParseNameOrDie("layout/main");
InlineXmlFormatParser parser;
ASSERT_TRUE(parser.consume(context.get(), doc.get()));
ASSERT_EQ(2u, parser.getExtractedInlineXmlDocuments().size());
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
ASSERT_EQ(2u, parser.GetExtractedInlineXmlDocuments().size());
xml::Element* el = xml::findRootElement(doc.get());
xml::Element* el = xml::FindRootElement(doc.get());
ASSERT_NE(nullptr, el);
EXPECT_EQ("View1", el->name);
xml::Attribute* attrText = el->findAttribute(xml::kSchemaAndroid, "text");
ASSERT_NE(nullptr, attrText);
xml::Attribute* attr_text = el->FindAttribute(xml::kSchemaAndroid, "text");
ASSERT_NE(nullptr, attr_text);
xml::Attribute* attrDrawable =
el->findAttribute(xml::kSchemaAndroid, "drawable");
ASSERT_NE(nullptr, attrDrawable);
xml::Attribute* attr_drawable =
el->FindAttribute(xml::kSchemaAndroid, "drawable");
ASSERT_NE(nullptr, attr_drawable);
// The two extracted resources should have different names.
EXPECT_NE(attrText->value, attrDrawable->value);
EXPECT_NE(attr_text->value, attr_drawable->value);
// The child <aapt:attr> elements should be gone.
EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
xml::XmlResource* extractedDocText =
parser.getExtractedInlineXmlDocuments()[0].get();
ASSERT_NE(nullptr, extractedDocText);
el = xml::findRootElement(extractedDocText);
xml::XmlResource* extracted_doc_text =
parser.GetExtractedInlineXmlDocuments()[0].get();
ASSERT_NE(nullptr, extracted_doc_text);
el = xml::FindRootElement(extracted_doc_text);
ASSERT_NE(nullptr, el);
EXPECT_EQ("View2", el->name);
xml::XmlResource* extractedDocDrawable =
parser.getExtractedInlineXmlDocuments()[1].get();
ASSERT_NE(nullptr, extractedDocDrawable);
el = xml::findRootElement(extractedDocDrawable);
xml::XmlResource* extracted_doc_drawable =
parser.GetExtractedInlineXmlDocuments()[1].get();
ASSERT_NE(nullptr, extracted_doc_drawable);
el = xml::FindRootElement(extracted_doc_drawable);
ASSERT_NE(nullptr, el);
EXPECT_EQ("vector", el->name);
}

View File

@@ -15,14 +15,16 @@
*/
#include "compile/Image.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <sstream>
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "util/StringPiece.h"
#include "util/Util.h"
namespace aapt {
// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
@@ -36,7 +38,7 @@ constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
/**
* Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
*/
static uint32_t getAlpha(uint32_t color);
static uint32_t get_alpha(uint32_t color);
/**
* Determines whether a color on an ImageLine is valid.
@@ -53,19 +55,19 @@ class ColorValidator {
* Returns true if the color specified is a neutral color
* (no padding, stretching, or optical bounds).
*/
virtual bool isNeutralColor(uint32_t color) const = 0;
virtual bool IsNeutralColor(uint32_t color) const = 0;
/**
* Returns true if the color is either a neutral color
* or one denoting padding, stretching, or optical bounds.
*/
bool isValidColor(uint32_t color) const {
bool IsValidColor(uint32_t color) const {
switch (color) {
case kPrimaryColor:
case kSecondaryColor:
return true;
}
return isNeutralColor(color);
return IsNeutralColor(color);
}
};
@@ -81,42 +83,43 @@ class ColorValidator {
//
// class ImageLine {
// public:
// virtual int32_t getLength() const = 0;
// virtual uint32_t getColor(int32_t idx) const = 0;
// virtual int32_t GetLength() const = 0;
// virtual uint32_t GetColor(int32_t idx) const = 0;
// };
//
template <typename ImageLine>
static bool fillRanges(const ImageLine* imageLine,
const ColorValidator* colorValidator,
std::vector<Range>* primaryRanges,
std::vector<Range>* secondaryRanges, std::string* err) {
const int32_t length = imageLine->getLength();
static bool FillRanges(const ImageLine* image_line,
const ColorValidator* color_validator,
std::vector<Range>* primary_ranges,
std::vector<Range>* secondary_ranges,
std::string* out_err) {
const int32_t length = image_line->GetLength();
uint32_t lastColor = 0xffffffffu;
uint32_t last_color = 0xffffffffu;
for (int32_t idx = 1; idx < length - 1; idx++) {
const uint32_t color = imageLine->getColor(idx);
if (!colorValidator->isValidColor(color)) {
*err = "found an invalid color";
const uint32_t color = image_line->GetColor(idx);
if (!color_validator->IsValidColor(color)) {
*out_err = "found an invalid color";
return false;
}
if (color != lastColor) {
if (color != last_color) {
// We are ending a range. Which range?
// note: encode the x offset without the final 1 pixel border.
if (lastColor == kPrimaryColor) {
primaryRanges->back().end = idx - 1;
} else if (lastColor == kSecondaryColor) {
secondaryRanges->back().end = idx - 1;
if (last_color == kPrimaryColor) {
primary_ranges->back().end = idx - 1;
} else if (last_color == kSecondaryColor) {
secondary_ranges->back().end = idx - 1;
}
// We are starting a range. Which range?
// note: encode the x offset without the final 1 pixel border.
if (color == kPrimaryColor) {
primaryRanges->push_back(Range(idx - 1, length - 2));
primary_ranges->push_back(Range(idx - 1, length - 2));
} else if (color == kSecondaryColor) {
secondaryRanges->push_back(Range(idx - 1, length - 2));
secondary_ranges->push_back(Range(idx - 1, length - 2));
}
lastColor = color;
last_color = color;
}
}
return true;
@@ -128,19 +131,19 @@ static bool fillRanges(const ImageLine* imageLine,
*/
class HorizontalImageLine {
public:
explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
explicit HorizontalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
int32_t length)
: mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
: rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
inline int32_t getLength() const { return mLength; }
inline int32_t GetLength() const { return length_; }
inline uint32_t getColor(int32_t idx) const {
return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
inline uint32_t GetColor(int32_t idx) const {
return NinePatch::PackRGBA(rows_[yoffset_] + (idx + xoffset_) * 4);
}
private:
uint8_t** mRows;
int32_t mXOffset, mYOffset, mLength;
uint8_t** rows_;
int32_t xoffset_, yoffset_, length_;
DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
};
@@ -151,179 +154,180 @@ class HorizontalImageLine {
*/
class VerticalImageLine {
public:
explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
explicit VerticalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
int32_t length)
: mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
: rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
inline int32_t getLength() const { return mLength; }
inline int32_t GetLength() const { return length_; }
inline uint32_t getColor(int32_t idx) const {
return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
inline uint32_t GetColor(int32_t idx) const {
return NinePatch::PackRGBA(rows_[yoffset_ + idx] + (xoffset_ * 4));
}
private:
uint8_t** mRows;
int32_t mXOffset, mYOffset, mLength;
uint8_t** rows_;
int32_t xoffset_, yoffset_, length_;
DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
};
class DiagonalImageLine {
public:
explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
int32_t xStep, int32_t yStep, int32_t length)
: mRows(rows),
mXOffset(xOffset),
mYOffset(yOffset),
mXStep(xStep),
mYStep(yStep),
mLength(length) {}
explicit DiagonalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
int32_t xstep, int32_t ystep, int32_t length)
: rows_(rows),
xoffset_(xoffset),
yoffset_(yoffset),
xstep_(xstep),
ystep_(ystep),
length_(length) {}
inline int32_t getLength() const { return mLength; }
inline int32_t GetLength() const { return length_; }
inline uint32_t getColor(int32_t idx) const {
return NinePatch::packRGBA(mRows[mYOffset + (idx * mYStep)] +
((idx + mXOffset) * mXStep) * 4);
inline uint32_t GetColor(int32_t idx) const {
return NinePatch::PackRGBA(rows_[yoffset_ + (idx * ystep_)] +
((idx + xoffset_) * xstep_) * 4);
}
private:
uint8_t** mRows;
int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
uint8_t** rows_;
int32_t xoffset_, yoffset_, xstep_, ystep_, length_;
DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
};
class TransparentNeutralColorValidator : public ColorValidator {
public:
bool isNeutralColor(uint32_t color) const override {
return getAlpha(color) == 0;
bool IsNeutralColor(uint32_t color) const override {
return get_alpha(color) == 0;
}
};
class WhiteNeutralColorValidator : public ColorValidator {
public:
bool isNeutralColor(uint32_t color) const override {
bool IsNeutralColor(uint32_t color) const override {
return color == kColorOpaqueWhite;
}
};
inline static uint32_t getAlpha(uint32_t color) {
inline static uint32_t get_alpha(uint32_t color) {
return (color & 0xff000000u) >> 24;
}
static bool populateBounds(const std::vector<Range>& padding,
const std::vector<Range>& layoutBounds,
const std::vector<Range>& stretchRegions,
const int32_t length, int32_t* paddingStart,
int32_t* paddingEnd, int32_t* layoutStart,
int32_t* layoutEnd, const StringPiece& edgeName,
std::string* err) {
static bool PopulateBounds(const std::vector<Range>& padding,
const std::vector<Range>& layout_bounds,
const std::vector<Range>& stretch_regions,
const int32_t length, int32_t* padding_start,
int32_t* padding_end, int32_t* layout_start,
int32_t* layout_end, const StringPiece& edge_name,
std::string* out_err) {
if (padding.size() > 1) {
std::stringstream errStream;
errStream << "too many padding sections on " << edgeName << " border";
*err = errStream.str();
std::stringstream err_stream;
err_stream << "too many padding sections on " << edge_name << " border";
*out_err = err_stream.str();
return false;
}
*paddingStart = 0;
*paddingEnd = 0;
*padding_start = 0;
*padding_end = 0;
if (!padding.empty()) {
const Range& range = padding.front();
*paddingStart = range.start;
*paddingEnd = length - range.end;
} else if (!stretchRegions.empty()) {
*padding_start = range.start;
*padding_end = length - range.end;
} else if (!stretch_regions.empty()) {
// No padding was defined. Compute the padding from the first and last
// stretch regions.
*paddingStart = stretchRegions.front().start;
*paddingEnd = length - stretchRegions.back().end;
*padding_start = stretch_regions.front().start;
*padding_end = length - stretch_regions.back().end;
}
if (layoutBounds.size() > 2) {
std::stringstream errStream;
errStream << "too many layout bounds sections on " << edgeName << " border";
*err = errStream.str();
if (layout_bounds.size() > 2) {
std::stringstream err_stream;
err_stream << "too many layout bounds sections on " << edge_name
<< " border";
*out_err = err_stream.str();
return false;
}
*layoutStart = 0;
*layoutEnd = 0;
if (layoutBounds.size() >= 1) {
const Range& range = layoutBounds.front();
*layout_start = 0;
*layout_end = 0;
if (layout_bounds.size() >= 1) {
const Range& range = layout_bounds.front();
// If there is only one layout bound segment, it might not start at 0, but
// then it should
// end at length.
if (range.start != 0 && range.end != length) {
std::stringstream errStream;
errStream << "layout bounds on " << edgeName
<< " border must start at edge";
*err = errStream.str();
std::stringstream err_stream;
err_stream << "layout bounds on " << edge_name
<< " border must start at edge";
*out_err = err_stream.str();
return false;
}
*layoutStart = range.end;
*layout_start = range.end;
if (layoutBounds.size() >= 2) {
const Range& range = layoutBounds.back();
if (layout_bounds.size() >= 2) {
const Range& range = layout_bounds.back();
if (range.end != length) {
std::stringstream errStream;
errStream << "layout bounds on " << edgeName
<< " border must start at edge";
*err = errStream.str();
std::stringstream err_stream;
err_stream << "layout bounds on " << edge_name
<< " border must start at edge";
*out_err = err_stream.str();
return false;
}
*layoutEnd = length - range.start;
*layout_end = length - range.start;
}
}
return true;
}
static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions,
static int32_t CalculateSegmentCount(const std::vector<Range>& stretch_regions,
int32_t length) {
if (stretchRegions.size() == 0) {
if (stretch_regions.size() == 0) {
return 0;
}
const bool startIsFixed = stretchRegions.front().start != 0;
const bool endIsFixed = stretchRegions.back().end != length;
const bool start_is_fixed = stretch_regions.front().start != 0;
const bool end_is_fixed = stretch_regions.back().end != length;
int32_t modifier = 0;
if (startIsFixed && endIsFixed) {
if (start_is_fixed && end_is_fixed) {
modifier = 1;
} else if (!startIsFixed && !endIsFixed) {
} else if (!start_is_fixed && !end_is_fixed) {
modifier = -1;
}
return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
return static_cast<int32_t>(stretch_regions.size()) * 2 + modifier;
}
static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
static uint32_t GetRegionColor(uint8_t** rows, const Bounds& region) {
// Sample the first pixel to compare against.
const uint32_t expectedColor =
NinePatch::packRGBA(rows[region.top] + region.left * 4);
const uint32_t expected_color =
NinePatch::PackRGBA(rows[region.top] + region.left * 4);
for (int32_t y = region.top; y < region.bottom; y++) {
const uint8_t* row = rows[y];
for (int32_t x = region.left; x < region.right; x++) {
const uint32_t color = NinePatch::packRGBA(row + x * 4);
if (getAlpha(color) == 0) {
const uint32_t color = NinePatch::PackRGBA(row + x * 4);
if (get_alpha(color) == 0) {
// The color is transparent.
// If the expectedColor is not transparent, NO_COLOR.
if (getAlpha(expectedColor) != 0) {
if (get_alpha(expected_color) != 0) {
return android::Res_png_9patch::NO_COLOR;
}
} else if (color != expectedColor) {
} else if (color != expected_color) {
return android::Res_png_9patch::NO_COLOR;
}
}
}
if (getAlpha(expectedColor) == 0) {
if (get_alpha(expected_color) == 0) {
return android::Res_png_9patch::TRANSPARENT_COLOR;
}
return expectedColor;
return expected_color;
}
// Fills outColors with each 9-patch section's colour. If the whole section is
// Fills out_colors with each 9-patch section's color. If the whole section is
// transparent,
// it gets the special TRANSPARENT colour. If the whole section is the same
// colour, it is assigned
// that colour. Otherwise it gets the special NO_COLOR colour.
// it gets the special TRANSPARENT color. If the whole section is the same
// color, it is assigned
// that color. Otherwise it gets the special NO_COLOR color.
//
// Note that the rows contain the 9-patch 1px border, and the indices in the
// stretch regions are
@@ -332,63 +336,63 @@ static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
// the indices must be offset by 1.
//
// width and height also include the 9-patch 1px border.
static void calculateRegionColors(
uint8_t** rows, const std::vector<Range>& horizontalStretchRegions,
const std::vector<Range>& verticalStretchRegions, const int32_t width,
const int32_t height, std::vector<uint32_t>* outColors) {
int32_t nextTop = 0;
static void CalculateRegionColors(
uint8_t** rows, const std::vector<Range>& horizontal_stretch_regions,
const std::vector<Range>& vertical_stretch_regions, const int32_t width,
const int32_t height, std::vector<uint32_t>* out_colors) {
int32_t next_top = 0;
Bounds bounds;
auto rowIter = verticalStretchRegions.begin();
while (nextTop != height) {
if (rowIter != verticalStretchRegions.end()) {
if (nextTop != rowIter->start) {
auto row_iter = vertical_stretch_regions.begin();
while (next_top != height) {
if (row_iter != vertical_stretch_regions.end()) {
if (next_top != row_iter->start) {
// This is a fixed segment.
// Offset the bounds by 1 to accommodate the border.
bounds.top = nextTop + 1;
bounds.bottom = rowIter->start + 1;
nextTop = rowIter->start;
bounds.top = next_top + 1;
bounds.bottom = row_iter->start + 1;
next_top = row_iter->start;
} else {
// This is a stretchy segment.
// Offset the bounds by 1 to accommodate the border.
bounds.top = rowIter->start + 1;
bounds.bottom = rowIter->end + 1;
nextTop = rowIter->end;
++rowIter;
bounds.top = row_iter->start + 1;
bounds.bottom = row_iter->end + 1;
next_top = row_iter->end;
++row_iter;
}
} else {
// This is the end, fixed section.
// Offset the bounds by 1 to accommodate the border.
bounds.top = nextTop + 1;
bounds.top = next_top + 1;
bounds.bottom = height + 1;
nextTop = height;
next_top = height;
}
int32_t nextLeft = 0;
auto colIter = horizontalStretchRegions.begin();
while (nextLeft != width) {
if (colIter != horizontalStretchRegions.end()) {
if (nextLeft != colIter->start) {
int32_t next_left = 0;
auto col_iter = horizontal_stretch_regions.begin();
while (next_left != width) {
if (col_iter != horizontal_stretch_regions.end()) {
if (next_left != col_iter->start) {
// This is a fixed segment.
// Offset the bounds by 1 to accommodate the border.
bounds.left = nextLeft + 1;
bounds.right = colIter->start + 1;
nextLeft = colIter->start;
bounds.left = next_left + 1;
bounds.right = col_iter->start + 1;
next_left = col_iter->start;
} else {
// This is a stretchy segment.
// Offset the bounds by 1 to accommodate the border.
bounds.left = colIter->start + 1;
bounds.right = colIter->end + 1;
nextLeft = colIter->end;
++colIter;
bounds.left = col_iter->start + 1;
bounds.right = col_iter->end + 1;
next_left = col_iter->end;
++col_iter;
}
} else {
// This is the end, fixed section.
// Offset the bounds by 1 to accommodate the border.
bounds.left = nextLeft + 1;
bounds.left = next_left + 1;
bounds.right = width + 1;
nextLeft = width;
next_left = width;
}
outColors->push_back(getRegionColor(rows, bounds));
out_colors->push_back(GetRegionColor(rows, bounds));
}
}
}
@@ -397,12 +401,12 @@ static void calculateRegionColors(
// alpha value begins
// (on both sides).
template <typename ImageLine>
static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart,
int32_t* outEnd) {
*outStart = 0;
*outEnd = 0;
static void FindOutlineInsets(const ImageLine* image_line, int32_t* out_start,
int32_t* out_end) {
*out_start = 0;
*out_end = 0;
const int32_t length = imageLine->getLength();
const int32_t length = image_line->GetLength();
if (length < 3) {
return;
}
@@ -413,179 +417,181 @@ static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart,
const int32_t mid2 = length / 2;
const int32_t mid1 = mid2 + (length % 2);
uint32_t maxAlpha = 0;
for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
uint32_t alpha = getAlpha(imageLine->getColor(i));
if (alpha > maxAlpha) {
maxAlpha = alpha;
*outStart = i;
uint32_t max_alpha = 0;
for (int32_t i = 0; i < mid1 && max_alpha != 0xff; i++) {
uint32_t alpha = get_alpha(image_line->GetColor(i));
if (alpha > max_alpha) {
max_alpha = alpha;
*out_start = i;
}
}
maxAlpha = 0;
for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
uint32_t alpha = getAlpha(imageLine->getColor(i));
if (alpha > maxAlpha) {
maxAlpha = alpha;
*outEnd = length - (i + 1);
max_alpha = 0;
for (int32_t i = length - 1; i >= mid2 && max_alpha != 0xff; i--) {
uint32_t alpha = get_alpha(image_line->GetColor(i));
if (alpha > max_alpha) {
max_alpha = alpha;
*out_end = length - (i + 1);
}
}
return;
}
template <typename ImageLine>
static uint32_t findMaxAlpha(const ImageLine* imageLine) {
const int32_t length = imageLine->getLength();
uint32_t maxAlpha = 0;
for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
uint32_t alpha = getAlpha(imageLine->getColor(idx));
if (alpha > maxAlpha) {
maxAlpha = alpha;
static uint32_t FindMaxAlpha(const ImageLine* image_line) {
const int32_t length = image_line->GetLength();
uint32_t max_alpha = 0;
for (int32_t idx = 0; idx < length && max_alpha != 0xff; idx++) {
uint32_t alpha = get_alpha(image_line->GetColor(idx));
if (alpha > max_alpha) {
max_alpha = alpha;
}
}
return maxAlpha;
return max_alpha;
}
// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
uint32_t NinePatch::PackRGBA(const uint8_t* pixel) {
return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
}
std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
std::unique_ptr<NinePatch> NinePatch::Create(uint8_t** rows,
const int32_t width,
const int32_t height,
std::string* err) {
std::string* out_err) {
if (width < 3 || height < 3) {
*err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
*out_err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
return {};
}
std::vector<Range> horizontalPadding;
std::vector<Range> horizontalOpticalBounds;
std::vector<Range> verticalPadding;
std::vector<Range> verticalOpticalBounds;
std::vector<Range> unexpectedRanges;
std::unique_ptr<ColorValidator> colorValidator;
std::vector<Range> horizontal_padding;
std::vector<Range> horizontal_layout_bounds;
std::vector<Range> vertical_padding;
std::vector<Range> vertical_layout_bounds;
std::vector<Range> unexpected_ranges;
std::unique_ptr<ColorValidator> color_validator;
if (rows[0][3] == 0) {
colorValidator = util::make_unique<TransparentNeutralColorValidator>();
} else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
colorValidator = util::make_unique<WhiteNeutralColorValidator>();
color_validator = util::make_unique<TransparentNeutralColorValidator>();
} else if (PackRGBA(rows[0]) == kColorOpaqueWhite) {
color_validator = util::make_unique<WhiteNeutralColorValidator>();
} else {
*err = "top-left corner pixel must be either opaque white or transparent";
*out_err =
"top-left corner pixel must be either opaque white or transparent";
return {};
}
// Private constructor, can't use make_unique.
auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
auto nine_patch = std::unique_ptr<NinePatch>(new NinePatch());
HorizontalImageLine topRow(rows, 0, 0, width);
if (!fillRanges(&topRow, colorValidator.get(),
&ninePatch->horizontalStretchRegions, &unexpectedRanges,
err)) {
HorizontalImageLine top_row(rows, 0, 0, width);
if (!FillRanges(&top_row, color_validator.get(),
&nine_patch->horizontal_stretch_regions, &unexpected_ranges,
out_err)) {
return {};
}
if (!unexpectedRanges.empty()) {
const Range& range = unexpectedRanges[0];
std::stringstream errStream;
errStream << "found unexpected optical bounds (red pixel) on top border "
<< "at x=" << range.start + 1;
*err = errStream.str();
if (!unexpected_ranges.empty()) {
const Range& range = unexpected_ranges[0];
std::stringstream err_stream;
err_stream << "found unexpected optical bounds (red pixel) on top border "
<< "at x=" << range.start + 1;
*out_err = err_stream.str();
return {};
}
VerticalImageLine leftCol(rows, 0, 0, height);
if (!fillRanges(&leftCol, colorValidator.get(),
&ninePatch->verticalStretchRegions, &unexpectedRanges, err)) {
VerticalImageLine left_col(rows, 0, 0, height);
if (!FillRanges(&left_col, color_validator.get(),
&nine_patch->vertical_stretch_regions, &unexpected_ranges,
out_err)) {
return {};
}
if (!unexpectedRanges.empty()) {
const Range& range = unexpectedRanges[0];
std::stringstream errStream;
errStream << "found unexpected optical bounds (red pixel) on left border "
<< "at y=" << range.start + 1;
if (!unexpected_ranges.empty()) {
const Range& range = unexpected_ranges[0];
std::stringstream err_stream;
err_stream << "found unexpected optical bounds (red pixel) on left border "
<< "at y=" << range.start + 1;
return {};
}
HorizontalImageLine bottomRow(rows, 0, height - 1, width);
if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
&horizontalOpticalBounds, err)) {
HorizontalImageLine bottom_row(rows, 0, height - 1, width);
if (!FillRanges(&bottom_row, color_validator.get(), &horizontal_padding,
&horizontal_layout_bounds, out_err)) {
return {};
}
if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
ninePatch->horizontalStretchRegions, width - 2,
&ninePatch->padding.left, &ninePatch->padding.right,
&ninePatch->layoutBounds.left,
&ninePatch->layoutBounds.right, "bottom", err)) {
if (!PopulateBounds(horizontal_padding, horizontal_layout_bounds,
nine_patch->horizontal_stretch_regions, width - 2,
&nine_patch->padding.left, &nine_patch->padding.right,
&nine_patch->layout_bounds.left,
&nine_patch->layout_bounds.right, "bottom", out_err)) {
return {};
}
VerticalImageLine rightCol(rows, width - 1, 0, height);
if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
&verticalOpticalBounds, err)) {
VerticalImageLine right_col(rows, width - 1, 0, height);
if (!FillRanges(&right_col, color_validator.get(), &vertical_padding,
&vertical_layout_bounds, out_err)) {
return {};
}
if (!populateBounds(verticalPadding, verticalOpticalBounds,
ninePatch->verticalStretchRegions, height - 2,
&ninePatch->padding.top, &ninePatch->padding.bottom,
&ninePatch->layoutBounds.top,
&ninePatch->layoutBounds.bottom, "right", err)) {
if (!PopulateBounds(vertical_padding, vertical_layout_bounds,
nine_patch->vertical_stretch_regions, height - 2,
&nine_patch->padding.top, &nine_patch->padding.bottom,
&nine_patch->layout_bounds.top,
&nine_patch->layout_bounds.bottom, "right", out_err)) {
return {};
}
// Fill the region colors of the 9-patch.
const int32_t numRows =
calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
const int32_t numCols =
calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
if ((int64_t)numRows * (int64_t)numCols > 0x7f) {
*err = "too many regions in 9-patch";
const int32_t num_rows =
CalculateSegmentCount(nine_patch->horizontal_stretch_regions, width - 2);
const int32_t num_cols =
CalculateSegmentCount(nine_patch->vertical_stretch_regions, height - 2);
if ((int64_t)num_rows * (int64_t)num_cols > 0x7f) {
*out_err = "too many regions in 9-patch";
return {};
}
ninePatch->regionColors.reserve(numRows * numCols);
calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
ninePatch->verticalStretchRegions, width - 2,
height - 2, &ninePatch->regionColors);
nine_patch->region_colors.reserve(num_rows * num_cols);
CalculateRegionColors(rows, nine_patch->horizontal_stretch_regions,
nine_patch->vertical_stretch_regions, width - 2,
height - 2, &nine_patch->region_colors);
// Compute the outline based on opacity.
// Find left and right extent of 9-patch content on center row.
HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
findOutlineInsets(&midRow, &ninePatch->outline.left,
&ninePatch->outline.right);
HorizontalImageLine mid_row(rows, 1, height / 2, width - 2);
FindOutlineInsets(&mid_row, &nine_patch->outline.left,
&nine_patch->outline.right);
// Find top and bottom extent of 9-patch content on center column.
VerticalImageLine midCol(rows, width / 2, 1, height - 2);
findOutlineInsets(&midCol, &ninePatch->outline.top,
&ninePatch->outline.bottom);
VerticalImageLine mid_col(rows, width / 2, 1, height - 2);
FindOutlineInsets(&mid_col, &nine_patch->outline.top,
&nine_patch->outline.bottom);
const int32_t outlineWidth =
(width - 2) - ninePatch->outline.left - ninePatch->outline.right;
const int32_t outlineHeight =
(height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
const int32_t outline_width =
(width - 2) - nine_patch->outline.left - nine_patch->outline.right;
const int32_t outline_height =
(height - 2) - nine_patch->outline.top - nine_patch->outline.bottom;
// Find the largest alpha value within the outline area.
HorizontalImageLine outlineMidRow(
rows, 1 + ninePatch->outline.left,
1 + ninePatch->outline.top + (outlineHeight / 2), outlineWidth);
VerticalImageLine outlineMidCol(
rows, 1 + ninePatch->outline.left + (outlineWidth / 2),
1 + ninePatch->outline.top, outlineHeight);
ninePatch->outlineAlpha =
std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
HorizontalImageLine outline_mid_row(
rows, 1 + nine_patch->outline.left,
1 + nine_patch->outline.top + (outline_height / 2), outline_width);
VerticalImageLine outline_mid_col(
rows, 1 + nine_patch->outline.left + (outline_width / 2),
1 + nine_patch->outline.top, outline_height);
nine_patch->outline_alpha =
std::max(FindMaxAlpha(&outline_mid_row), FindMaxAlpha(&outline_mid_col));
// Assuming the image is a round rect, compute the radius by marching
// diagonally from the top left corner towards the center.
DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left,
1 + ninePatch->outline.top, 1, 1,
std::min(outlineWidth, outlineHeight));
int32_t topLeft, bottomRight;
findOutlineInsets(&diagonal, &topLeft, &bottomRight);
DiagonalImageLine diagonal(rows, 1 + nine_patch->outline.left,
1 + nine_patch->outline.top, 1, 1,
std::min(outline_width, outline_height));
int32_t top_left, bottom_right;
FindOutlineInsets(&diagonal, &top_left, &bottom_right);
/* Determine source radius based upon inset:
* sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
@@ -593,15 +599,15 @@ std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
* (sqrt(2) - 1) * r = sqrt(2) * i
* r = sqrt(2) / (sqrt(2) - 1) * i
*/
ninePatch->outlineRadius = 3.4142f * topLeft;
return ninePatch;
nine_patch->outline_radius = 3.4142f * top_left;
return nine_patch;
}
std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
std::unique_ptr<uint8_t[]> NinePatch::SerializeBase(size_t* outLen) const {
android::Res_png_9patch data;
data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
data.numColors = static_cast<uint8_t>(regionColors.size());
data.numXDivs = static_cast<uint8_t>(horizontal_stretch_regions.size()) * 2;
data.numYDivs = static_cast<uint8_t>(vertical_stretch_regions.size()) * 2;
data.numColors = static_cast<uint8_t>(region_colors.size());
data.paddingLeft = padding.left;
data.paddingRight = padding.right;
data.paddingTop = padding.top;
@@ -609,8 +615,8 @@ std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
android::Res_png_9patch::serialize(
data, (const int32_t*)horizontalStretchRegions.data(),
(const int32_t*)verticalStretchRegions.data(), regionColors.data(),
data, (const int32_t*)horizontal_stretch_regions.data(),
(const int32_t*)vertical_stretch_regions.data(), region_colors.data(),
buffer.get());
// Convert to file endianness.
reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
@@ -619,32 +625,32 @@ std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
return buffer;
}
std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(
size_t* outLen) const {
size_t chunkLen = sizeof(uint32_t) * 4;
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
std::unique_ptr<uint8_t[]> NinePatch::SerializeLayoutBounds(
size_t* out_len) const {
size_t chunk_len = sizeof(uint32_t) * 4;
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
uint8_t* cursor = buffer.get();
memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
cursor += sizeof(layoutBounds.left);
memcpy(cursor, &layout_bounds.left, sizeof(layout_bounds.left));
cursor += sizeof(layout_bounds.left);
memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
cursor += sizeof(layoutBounds.top);
memcpy(cursor, &layout_bounds.top, sizeof(layout_bounds.top));
cursor += sizeof(layout_bounds.top);
memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
cursor += sizeof(layoutBounds.right);
memcpy(cursor, &layout_bounds.right, sizeof(layout_bounds.right));
cursor += sizeof(layout_bounds.right);
memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
cursor += sizeof(layoutBounds.bottom);
memcpy(cursor, &layout_bounds.bottom, sizeof(layout_bounds.bottom));
cursor += sizeof(layout_bounds.bottom);
*outLen = chunkLen;
*out_len = chunk_len;
return buffer;
}
std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(
size_t* outLen) const {
size_t chunkLen = sizeof(uint32_t) * 6;
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
std::unique_ptr<uint8_t[]> NinePatch::SerializeRoundedRectOutline(
size_t* out_len) const {
size_t chunk_len = sizeof(uint32_t) * 6;
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
uint8_t* cursor = buffer.get();
memcpy(cursor, &outline.left, sizeof(outline.left));
@@ -659,12 +665,12 @@ std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(
memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
cursor += sizeof(outline.bottom);
*((float*)cursor) = outlineRadius;
cursor += sizeof(outlineRadius);
*((float*)cursor) = outline_radius;
cursor += sizeof(outline_radius);
*((uint32_t*)cursor) = outlineAlpha;
*((uint32_t*)cursor) = outline_alpha;
*outLen = chunkLen;
*out_len = chunk_len;
return buffer;
}
@@ -677,16 +683,16 @@ std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(
<< " r=" << bounds.right << " b=" << bounds.bottom;
}
::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch) {
return out << "horizontalStretch:"
<< util::joiner(ninePatch.horizontalStretchRegions, " ")
<< util::Joiner(nine_patch.horizontal_stretch_regions, " ")
<< " verticalStretch:"
<< util::joiner(ninePatch.verticalStretchRegions, " ")
<< " padding: " << ninePatch.padding
<< ", bounds: " << ninePatch.layoutBounds
<< ", outline: " << ninePatch.outline
<< " rad=" << ninePatch.outlineRadius
<< " alpha=" << ninePatch.outlineAlpha;
<< util::Joiner(nine_patch.vertical_stretch_regions, " ")
<< " padding: " << nine_patch.padding
<< ", bounds: " << nine_patch.layout_bounds
<< ", outline: " << nine_patch.outline
<< " rad=" << nine_patch.outline_radius
<< " alpha=" << nine_patch.outline_alpha;
}
} // namespace aapt

View File

@@ -15,6 +15,7 @@
*/
#include "compile/Image.h"
#include "test/Test.h"
namespace aapt {
@@ -182,169 +183,168 @@ static uint8_t* kStretchAndPadding5x5[] = {
TEST(NinePatchTest, Minimum3x3) {
std::string err;
EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
EXPECT_EQ(nullptr, NinePatch::Create(k2x2, 2, 2, &err));
EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, MixedNeutralColors) {
std::string err;
EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
EXPECT_EQ(nullptr, NinePatch::Create(kMixedNeutralColor3x3, 3, 3, &err));
EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, TransparentNeutralColor) {
std::string err;
EXPECT_NE(nullptr,
NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
NinePatch::Create(kTransparentNeutralColor3x3, 3, 3, &err));
}
TEST(NinePatchTest, SingleStretchRegion) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kSingleStretch7x6, 7, 6, &err);
ASSERT_NE(nullptr, ninePatch);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kSingleStretch7x6, 7, 6, &err);
ASSERT_NE(nullptr, nine_patch);
ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
ASSERT_EQ(1u, nine_patch->horizontal_stretch_regions.size());
ASSERT_EQ(1u, nine_patch->vertical_stretch_regions.size());
EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
EXPECT_EQ(Range(1, 4), nine_patch->horizontal_stretch_regions.front());
EXPECT_EQ(Range(1, 3), nine_patch->vertical_stretch_regions.front());
}
TEST(NinePatchTest, MultipleStretchRegions) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
ASSERT_NE(nullptr, ninePatch);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
ASSERT_NE(nullptr, nine_patch);
ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
ASSERT_EQ(3u, nine_patch->horizontal_stretch_regions.size());
ASSERT_EQ(2u, nine_patch->vertical_stretch_regions.size());
EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
EXPECT_EQ(Range(1, 2), nine_patch->horizontal_stretch_regions[0]);
EXPECT_EQ(Range(3, 5), nine_patch->horizontal_stretch_regions[1]);
EXPECT_EQ(Range(6, 7), nine_patch->horizontal_stretch_regions[2]);
EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
EXPECT_EQ(Range(0, 2), nine_patch->vertical_stretch_regions[0]);
EXPECT_EQ(Range(3, 5), nine_patch->vertical_stretch_regions[1]);
}
TEST(NinePatchTest, InferPaddingFromStretchRegions) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(1, 0, 1, 0), nine_patch->padding);
}
TEST(NinePatchTest, Padding) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kPadding6x5, 6, 5, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kPadding6x5, 6, 5, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
}
TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
std::string err;
EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
EXPECT_EQ(nullptr, NinePatch::Create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
std::string err;
EXPECT_EQ(nullptr,
NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
NinePatch::Create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBounds) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
nine_patch = NinePatch::Create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(1, 1, 0, 0), nine_patch->layout_bounds);
}
TEST(NinePatchTest, PaddingAndLayoutBounds) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
}
TEST(NinePatchTest, RegionColorsAreCorrect) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kColorfulImage5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kColorfulImage5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
std::vector<uint32_t> expectedColors = {
NinePatch::packRGBA((uint8_t*)RED),
std::vector<uint32_t> expected_colors = {
NinePatch::PackRGBA((uint8_t*)RED),
(uint32_t)android::Res_png_9patch::NO_COLOR,
NinePatch::packRGBA((uint8_t*)GREEN),
NinePatch::PackRGBA((uint8_t*)GREEN),
(uint32_t)android::Res_png_9patch::TRANSPARENT_COLOR,
NinePatch::packRGBA((uint8_t*)BLUE),
NinePatch::packRGBA((uint8_t*)GREEN),
NinePatch::PackRGBA((uint8_t*)BLUE),
NinePatch::PackRGBA((uint8_t*)GREEN),
};
EXPECT_EQ(expectedColors, ninePatch->regionColors);
EXPECT_EQ(expected_colors, nine_patch->region_colors);
}
TEST(NinePatchTest, OutlineFromOpaqueImage) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
EXPECT_EQ(0.0f, ninePatch->outlineRadius);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kOutlineOpaque10x10, 10, 10, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(2, 2, 2, 2), nine_patch->outline);
EXPECT_EQ(0x000000ffu, nine_patch->outline_alpha);
EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineFromTranslucentImage) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kOutlineTranslucent10x10, 10, 10, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
EXPECT_EQ(0.0f, ninePatch->outlineRadius);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kOutlineTranslucent10x10, 10, 10, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(3, 3, 3, 3), nine_patch->outline);
EXPECT_EQ(0x000000b3u, nine_patch->outline_alpha);
EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineFromOffCenterImage) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10, &err);
ASSERT_NE(nullptr, ninePatch);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kOutlineOffsetTranslucent12x10, 12, 10, &err);
ASSERT_NE(nullptr, nine_patch);
// TODO(adamlesinski): The old AAPT algorithm searches from the outside to the
// middle
// for each inset. If the outline is shifted, the search may not find a closer
// bounds.
// middle for each inset. If the outline is shifted, the search may not find a
// closer bounds.
// This check should be:
// EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
// but until I know what behaviour I'm breaking, I will leave it at the
// but until I know what behavior I'm breaking, I will leave it at the
// incorrect:
EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
EXPECT_EQ(Bounds(4, 3, 3, 3), nine_patch->outline);
EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
EXPECT_EQ(0.0f, ninePatch->outlineRadius);
EXPECT_EQ(0x000000b3u, nine_patch->outline_alpha);
EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineRadius) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kOutlineRadius5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
EXPECT_EQ(Bounds(0, 0, 0, 0), nine_patch->outline);
EXPECT_EQ(3.4142f, nine_patch->outline_radius);
}
::testing::AssertionResult bigEndianOne(uint8_t* cursor) {
::testing::AssertionResult BigEndianOne(uint8_t* cursor) {
if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
return ::testing::AssertionSuccess();
}
@@ -353,12 +353,12 @@ TEST(NinePatchTest, OutlineRadius) {
TEST(NinePatchTest, SerializePngEndianness) {
std::string err;
std::unique_ptr<NinePatch> ninePatch =
NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
ASSERT_NE(nullptr, ninePatch);
std::unique_ptr<NinePatch> nine_patch =
NinePatch::Create(kStretchAndPadding5x5, 5, 5, &err);
ASSERT_NE(nullptr, nine_patch);
size_t len;
std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
std::unique_ptr<uint8_t[]> data = nine_patch->SerializeBase(&len);
ASSERT_NE(nullptr, data);
ASSERT_NE(0u, len);
@@ -368,10 +368,10 @@ TEST(NinePatchTest, SerializePngEndianness) {
uint8_t* cursor = data.get() + 12;
// Check that padding is big-endian. Expecting value 1.
EXPECT_TRUE(bigEndianOne(cursor));
EXPECT_TRUE(bigEndianOne(cursor + 4));
EXPECT_TRUE(bigEndianOne(cursor + 8));
EXPECT_TRUE(bigEndianOne(cursor + 12));
EXPECT_TRUE(BigEndianOne(cursor));
EXPECT_TRUE(BigEndianOne(cursor + 4));
EXPECT_TRUE(BigEndianOne(cursor + 8));
EXPECT_TRUE(BigEndianOne(cursor + 12));
}
} // namespace aapt

View File

@@ -89,7 +89,7 @@ static void readDataFromStream(png_structp readPtr, png_bytep data,
static void writeDataToStream(png_structp writePtr, png_bytep data,
png_size_t length) {
BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
png_bytep buf = outBuffer->nextBlock<png_byte>(length);
png_bytep buf = outBuffer->NextBlock<png_byte>(length);
memcpy(buf, data, length);
}
@@ -98,13 +98,13 @@ static void flushDataToStream(png_structp /*writePtr*/) {}
static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
IDiagnostics* diag =
reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
diag->warn(DiagMessage() << warningMessage);
diag->Warn(DiagMessage() << warningMessage);
}
static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr,
PngInfo* outInfo) {
if (setjmp(png_jmpbuf(readPtr))) {
diag->error(DiagMessage() << "failed reading png");
diag->Error(DiagMessage() << "failed reading png");
return false;
}
@@ -373,7 +373,7 @@ static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo,
*colorType = PNG_COLOR_TYPE_PALETTE;
} else {
if (maxGrayDeviation <= grayscaleTolerance) {
diag->note(DiagMessage() << "forcing image to gray (max deviation = "
diag->Note(DiagMessage() << "forcing image to gray (max deviation = "
<< maxGrayDeviation << ")");
*colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
} else {
@@ -424,7 +424,7 @@ static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo,
static bool writePng(IDiagnostics* diag, png_structp writePtr,
png_infop infoPtr, PngInfo* info, int grayScaleTolerance) {
if (setjmp(png_jmpbuf(writePtr))) {
diag->error(DiagMessage() << "failed to write png");
diag->Error(DiagMessage() << "failed to write png");
return false;
}
@@ -453,7 +453,7 @@ static bool writePng(IDiagnostics* diag, png_structp writePtr,
png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
if (kDebug) {
diag->note(DiagMessage() << "writing image: w = " << info->width
diag->Note(DiagMessage() << "writing image: w = " << info->width
<< ", h = " << info->height);
}
@@ -476,23 +476,23 @@ static bool writePng(IDiagnostics* diag, png_structp writePtr,
if (kDebug) {
switch (colorType) {
case PNG_COLOR_TYPE_PALETTE:
diag->note(DiagMessage() << "has " << paletteEntries << " colors"
diag->Note(DiagMessage() << "has " << paletteEntries << " colors"
<< (hasTransparency ? " (with alpha)" : "")
<< ", using PNG_COLOR_TYPE_PALLETTE");
break;
case PNG_COLOR_TYPE_GRAY:
diag->note(DiagMessage()
diag->Note(DiagMessage()
<< "is opaque gray, using PNG_COLOR_TYPE_GRAY");
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
diag->note(DiagMessage()
diag->Note(DiagMessage()
<< "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
break;
case PNG_COLOR_TYPE_RGB:
diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
diag->Note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
diag->note(DiagMessage()
diag->Note(DiagMessage()
<< "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
break;
}
@@ -527,7 +527,7 @@ static bool writePng(IDiagnostics* diag, png_structp writePtr,
// base 9 patch data
if (kDebug) {
diag->note(DiagMessage() << "adding 9-patch info..");
diag->Note(DiagMessage() << "adding 9-patch info..");
}
strcpy((char*)unknowns[pIndex].name, "npTc");
unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
@@ -604,7 +604,7 @@ static bool writePng(IDiagnostics* diag, png_structp writePtr,
&interlaceType, &compressionType, nullptr);
if (kDebug) {
diag->note(DiagMessage() << "image written: w = " << width
diag->Note(DiagMessage() << "image written: w = " << width
<< ", h = " << height << ", d = " << bitDepth
<< ", colors = " << colorType
<< ", inter = " << interlaceType
@@ -1228,13 +1228,13 @@ bool Png::process(const Source& source, std::istream* input,
// Read the PNG signature first.
if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
mDiag->error(DiagMessage() << strerror(errno));
mDiag->Error(DiagMessage() << strerror(errno));
return false;
}
// If the PNG signature doesn't match, bail early.
if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
mDiag->error(DiagMessage() << "not a valid png file");
mDiag->Error(DiagMessage() << "not a valid png file");
return false;
}
@@ -1247,13 +1247,13 @@ bool Png::process(const Source& source, std::istream* input,
readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
if (!readPtr) {
mDiag->error(DiagMessage() << "failed to allocate read ptr");
mDiag->Error(DiagMessage() << "failed to allocate read ptr");
goto bail;
}
infoPtr = png_create_info_struct(readPtr);
if (!infoPtr) {
mDiag->error(DiagMessage() << "failed to allocate info ptr");
mDiag->Error(DiagMessage() << "failed to allocate info ptr");
goto bail;
}
@@ -1267,10 +1267,10 @@ bool Png::process(const Source& source, std::istream* input,
goto bail;
}
if (util::stringEndsWith(source.path, ".9.png")) {
if (util::EndsWith(source.path, ".9.png")) {
std::string errorMsg;
if (!do9Patch(&pngInfo, &errorMsg)) {
mDiag->error(DiagMessage() << errorMsg);
mDiag->Error(DiagMessage() << errorMsg);
goto bail;
}
}
@@ -1278,13 +1278,13 @@ bool Png::process(const Source& source, std::istream* input,
writePtr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
if (!writePtr) {
mDiag->error(DiagMessage() << "failed to allocate write ptr");
mDiag->Error(DiagMessage() << "failed to allocate write ptr");
goto bail;
}
writeInfoPtr = png_create_info_struct(writePtr);
if (!writeInfoPtr) {
mDiag->error(DiagMessage() << "failed to allocate write info ptr");
mDiag->Error(DiagMessage() << "failed to allocate write info ptr");
goto bail;
}
@@ -1295,7 +1295,7 @@ bool Png::process(const Source& source, std::istream* input,
flushDataToStream);
if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo,
options.grayScaleTolerance)) {
options.grayscale_tolerance)) {
goto bail;
}

View File

@@ -17,6 +17,11 @@
#ifndef AAPT_PNG_H
#define AAPT_PNG_H
#include <iostream>
#include <string>
#include "android-base/macros.h"
#include "Diagnostics.h"
#include "Source.h"
#include "compile/Image.h"
@@ -24,16 +29,15 @@
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
#include <android-base/macros.h>
#include <iostream>
#include <string>
namespace aapt {
struct PngOptions {
int grayScaleTolerance = 0;
int grayscale_tolerance = 0;
};
/**
* Deprecated. Removing once new PNG crunching code is proved to be correct.
*/
class Png {
public:
explicit Png(IDiagnostics* diag) : mDiag(diag) {}
@@ -59,18 +63,18 @@ class PngChunkFilter : public io::InputStream {
bool Skip(int count) override;
int64_t ByteCount() const override {
return static_cast<int64_t>(mWindowStart);
return static_cast<int64_t>(window_start_);
}
bool HadError() const override { return mError; }
bool HadError() const override { return error_; }
private:
bool consumeWindow(const void** buffer, int* len);
bool ConsumeWindow(const void** buffer, int* len);
StringPiece mData;
size_t mWindowStart = 0;
size_t mWindowEnd = 0;
bool mError = false;
StringPiece data_;
size_t window_start_ = 0;
size_t window_end_ = 0;
bool error_ = false;
DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
};
@@ -78,14 +82,14 @@ class PngChunkFilter : public io::InputStream {
/**
* Reads a PNG from the InputStream into memory as an RGBA Image.
*/
std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
std::unique_ptr<Image> ReadPng(IAaptContext* context, io::InputStream* in);
/**
* Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream
* as a PNG.
*/
bool writePng(IAaptContext* context, const Image* image,
const NinePatch* ninePatch, io::OutputStream* out,
bool WritePng(IAaptContext* context, const Image* image,
const NinePatch* nine_patch, io::OutputStream* out,
const PngOptions& options);
} // namespace aapt

View File

@@ -15,6 +15,7 @@
*/
#include "compile/Png.h"
#include "io/Io.h"
#include "util/StringPiece.h"
@@ -39,7 +40,7 @@ enum PngChunkTypes {
kPngChunksRGB = u32(115, 82, 71, 66),
};
static uint32_t peek32LE(const char* data) {
static uint32_t Peek32LE(const char* data) {
uint32_t word = ((uint32_t)data[0]) & 0x000000ff;
word <<= 8;
word |= ((uint32_t)data[1]) & 0x000000ff;
@@ -50,7 +51,7 @@ static uint32_t peek32LE(const char* data) {
return word;
}
static bool isPngChunkWhitelisted(uint32_t type) {
static bool IsPngChunkWhitelisted(uint32_t type) {
switch (type) {
case kPngChunkIHDR:
case kPngChunkIDAT:
@@ -64,93 +65,93 @@ static bool isPngChunkWhitelisted(uint32_t type) {
}
}
PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
if (util::stringStartsWith(mData, kPngSignature)) {
mWindowStart = 0;
mWindowEnd = strlen(kPngSignature);
PngChunkFilter::PngChunkFilter(const StringPiece& data) : data_(data) {
if (util::StartsWith(data_, kPngSignature)) {
window_start_ = 0;
window_end_ = strlen(kPngSignature);
} else {
mError = true;
error_ = true;
}
}
bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
if (mWindowStart != mWindowEnd) {
bool PngChunkFilter::ConsumeWindow(const void** buffer, int* len) {
if (window_start_ != window_end_) {
// We have bytes to give from our window.
const int bytesRead = (int)(mWindowEnd - mWindowStart);
*buffer = mData.data() + mWindowStart;
*len = bytesRead;
mWindowStart = mWindowEnd;
const int bytes_read = (int)(window_end_ - window_start_);
*buffer = data_.data() + window_start_;
*len = bytes_read;
window_start_ = window_end_;
return true;
}
return false;
}
bool PngChunkFilter::Next(const void** buffer, int* len) {
if (mError) {
if (error_) {
return false;
}
// In case BackUp was called, we must consume the window.
if (consumeWindow(buffer, len)) {
if (ConsumeWindow(buffer, len)) {
return true;
}
// Advance the window as far as possible (until we meet a chunk that
// we want to strip).
while (mWindowEnd < mData.size()) {
while (window_end_ < data_.size()) {
// Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
// Is there enough room for a chunk header?
if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
mError = true;
if (data_.size() - window_start_ < kMinChunkHeaderSize) {
error_ = true;
return false;
}
// Verify the chunk length.
const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
if (((uint64_t)chunkLen) + ((uint64_t)mWindowEnd) + sizeof(uint32_t) >
mData.size()) {
const uint32_t chunk_len = Peek32LE(data_.data() + window_end_);
if (((uint64_t)chunk_len) + ((uint64_t)window_end_) + sizeof(uint32_t) >
data_.size()) {
// Overflow.
mError = true;
error_ = true;
return false;
}
// Do we strip this chunk?
const uint32_t chunkType =
peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
if (isPngChunkWhitelisted(chunkType)) {
const uint32_t chunk_type =
Peek32LE(data_.data() + window_end_ + sizeof(uint32_t));
if (IsPngChunkWhitelisted(chunk_type)) {
// Advance the window to include this chunk.
mWindowEnd += kMinChunkHeaderSize + chunkLen;
window_end_ += kMinChunkHeaderSize + chunk_len;
} else {
// We want to strip this chunk. If we accumulated a window,
// we must return the window now.
if (mWindowStart != mWindowEnd) {
if (window_start_ != window_end_) {
break;
}
// The window is empty, so we can advance past this chunk
// and keep looking for the next good chunk,
mWindowEnd += kMinChunkHeaderSize + chunkLen;
mWindowStart = mWindowEnd;
window_end_ += kMinChunkHeaderSize + chunk_len;
window_start_ = window_end_;
}
}
if (consumeWindow(buffer, len)) {
if (ConsumeWindow(buffer, len)) {
return true;
}
return false;
}
void PngChunkFilter::BackUp(int count) {
if (mError) {
if (error_) {
return;
}
mWindowStart -= count;
window_start_ -= count;
}
bool PngChunkFilter::Skip(int count) {
if (mError) {
if (error_) {
return false;
}

View File

@@ -16,14 +16,17 @@
#include "compile/Png.h"
#include <android-base/errors.h>
#include <android-base/macros.h>
#include <png.h>
#include <zlib.h>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include "android-base/errors.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
namespace aapt {
// Size in bytes of the PNG signature.
@@ -34,16 +37,16 @@ constexpr size_t kPngSignatureSize = 8u;
*/
class PngReadStructDeleter {
public:
explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr)
: mReadPtr(readPtr), mInfoPtr(infoPtr) {}
PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
: read_ptr_(read_ptr), info_ptr_(info_ptr) {}
~PngReadStructDeleter() {
png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
png_destroy_read_struct(&read_ptr_, &info_ptr_, nullptr);
}
private:
png_structp mReadPtr;
png_infop mInfoPtr;
png_structp read_ptr_;
png_infop info_ptr_;
DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
};
@@ -53,226 +56,229 @@ class PngReadStructDeleter {
*/
class PngWriteStructDeleter {
public:
explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr)
: mWritePtr(writePtr), mInfoPtr(infoPtr) {}
PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
: write_ptr_(write_ptr), info_ptr_(info_ptr) {}
~PngWriteStructDeleter() { png_destroy_write_struct(&mWritePtr, &mInfoPtr); }
~PngWriteStructDeleter() {
png_destroy_write_struct(&write_ptr_, &info_ptr_);
}
private:
png_structp mWritePtr;
png_infop mInfoPtr;
png_structp write_ptr_;
png_infop info_ptr_;
DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
};
// Custom warning logging method that uses IDiagnostics.
static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
diag->warn(DiagMessage() << warningMsg);
static void LogWarning(png_structp png_ptr, png_const_charp warning_msg) {
IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
diag->Warn(DiagMessage() << warning_msg);
}
// Custom error logging method that uses IDiagnostics.
static void logError(png_structp pngPtr, png_const_charp errorMsg) {
IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
diag->error(DiagMessage() << errorMsg);
static void LogError(png_structp png_ptr, png_const_charp error_msg) {
IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
diag->Error(DiagMessage() << error_msg);
}
static void readDataFromStream(png_structp pngPtr, png_bytep buffer,
static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer,
png_size_t len) {
io::InputStream* in = (io::InputStream*)png_get_io_ptr(pngPtr);
io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
const void* inBuffer;
int inLen;
if (!in->Next(&inBuffer, &inLen)) {
const void* in_buffer;
int in_len;
if (!in->Next(&in_buffer, &in_len)) {
if (in->HadError()) {
std::string err = in->GetError();
png_error(pngPtr, err.c_str());
png_error(png_ptr, err.c_str());
}
return;
}
const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
memcpy(buffer, inBuffer, bytesRead);
if (bytesRead != static_cast<size_t>(inLen)) {
in->BackUp(inLen - static_cast<int>(bytesRead));
const size_t bytes_read = std::min(static_cast<size_t>(in_len), len);
memcpy(buffer, in_buffer, bytes_read);
if (bytes_read != static_cast<size_t>(in_len)) {
in->BackUp(in_len - static_cast<int>(bytes_read));
}
}
static void writeDataToStream(png_structp pngPtr, png_bytep buffer,
static void WriteDataToStream(png_structp png_ptr, png_bytep buffer,
png_size_t len) {
io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(pngPtr);
io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
void* outBuffer;
int outLen;
void* out_buffer;
int out_len;
while (len > 0) {
if (!out->Next(&outBuffer, &outLen)) {
if (!out->Next(&out_buffer, &out_len)) {
if (out->HadError()) {
std::string err = out->GetError();
png_error(pngPtr, err.c_str());
png_error(png_ptr, err.c_str());
}
return;
}
const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
memcpy(outBuffer, buffer, bytesWritten);
const size_t bytes_written = std::min(static_cast<size_t>(out_len), len);
memcpy(out_buffer, buffer, bytes_written);
// Advance the input buffer.
buffer += bytesWritten;
len -= bytesWritten;
buffer += bytes_written;
len -= bytes_written;
// Advance the output buffer.
outLen -= static_cast<int>(bytesWritten);
out_len -= static_cast<int>(bytes_written);
}
// If the entire output buffer wasn't used, backup.
if (outLen > 0) {
out->BackUp(outLen);
if (out_len > 0) {
out->BackUp(out_len);
}
}
std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
std::unique_ptr<Image> ReadPng(IAaptContext* context, io::InputStream* in) {
// Read the first 8 bytes of the file looking for the PNG signature.
// Bail early if it does not match.
const png_byte* signature;
int bufferSize;
if (!in->Next((const void**)&signature, &bufferSize)) {
context->getDiagnostics()->error(
int buffer_size;
if (!in->Next((const void**)&signature, &buffer_size)) {
context->GetDiagnostics()->Error(
DiagMessage() << android::base::SystemErrorCodeToString(errno));
return {};
}
if (static_cast<size_t>(bufferSize) < kPngSignatureSize ||
if (static_cast<size_t>(buffer_size) < kPngSignatureSize ||
png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
context->getDiagnostics()->error(
context->GetDiagnostics()->Error(
DiagMessage() << "file signature does not match PNG signature");
return {};
}
// Start at the beginning of the first chunk.
in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
in->BackUp(buffer_size - static_cast<int>(kPngSignatureSize));
// Create and initialize the png_struct with the default error and warning
// handlers.
// The header version is also passed in to ensure that this was built against
// the same
// version of libpng.
png_structp readPtr =
png_structp read_ptr =
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (readPtr == nullptr) {
context->getDiagnostics()->error(
if (read_ptr == nullptr) {
context->GetDiagnostics()->Error(
DiagMessage() << "failed to create libpng read png_struct");
return {};
}
// Create and initialize the memory for image header and data.
png_infop infoPtr = png_create_info_struct(readPtr);
if (infoPtr == nullptr) {
context->getDiagnostics()->error(
png_infop info_ptr = png_create_info_struct(read_ptr);
if (info_ptr == nullptr) {
context->GetDiagnostics()->Error(
DiagMessage() << "failed to create libpng read png_info");
png_destroy_read_struct(&readPtr, nullptr, nullptr);
png_destroy_read_struct(&read_ptr, nullptr, nullptr);
return {};
}
// Automatically release PNG resources at end of scope.
PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
PngReadStructDeleter png_read_deleter(read_ptr, info_ptr);
// libpng uses longjmp to jump to an error handling routine.
// setjmp will only return true if it was jumped to, aka there was
// an error.
if (setjmp(png_jmpbuf(readPtr))) {
if (setjmp(png_jmpbuf(read_ptr))) {
return {};
}
// Handle warnings ourselves via IDiagnostics.
png_set_error_fn(readPtr, (png_voidp)context->getDiagnostics(), logError,
logWarning);
png_set_error_fn(read_ptr, (png_voidp)context->GetDiagnostics(), LogError,
LogWarning);
// Set up the read functions which read from our custom data sources.
png_set_read_fn(readPtr, (png_voidp)in, readDataFromStream);
png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
// Skip the signature that we already read.
png_set_sig_bytes(readPtr, kPngSignatureSize);
png_set_sig_bytes(read_ptr, kPngSignatureSize);
// Read the chunk headers.
png_read_info(readPtr, infoPtr);
png_read_info(read_ptr, info_ptr);
// Extract image meta-data from the various chunk headers.
uint32_t width, height;
int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType,
&interlaceMethod, &compressionMethod, &filterMethod);
int bit_depth, color_type, interlace_method, compression_method,
filter_method;
png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_method, &compression_method, &filter_method);
// When the image is read, expand it so that it is in RGBA 8888 format
// so that image handling is uniform.
if (colorType == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(readPtr);
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(read_ptr);
}
if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
png_set_expand_gray_1_2_4_to_8(readPtr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(read_ptr);
}
if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(readPtr);
if (png_get_valid(read_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(read_ptr);
}
if (bitDepth == 16) {
png_set_strip_16(readPtr);
if (bit_depth == 16) {
png_set_strip_16(read_ptr);
}
if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
}
if (colorType == PNG_COLOR_TYPE_GRAY ||
colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(readPtr);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(read_ptr);
}
if (interlaceMethod != PNG_INTERLACE_NONE) {
png_set_interlace_handling(readPtr);
if (interlace_method != PNG_INTERLACE_NONE) {
png_set_interlace_handling(read_ptr);
}
// Once all the options for reading have been set, we need to flush
// them to libpng.
png_read_update_info(readPtr, infoPtr);
png_read_update_info(read_ptr, info_ptr);
// 9-patch uses int32_t to index images, so we cap the image dimensions to
// something
// that can always be represented by 9-patch.
if (width > std::numeric_limits<int32_t>::max() ||
height > std::numeric_limits<int32_t>::max()) {
context->getDiagnostics()->error(DiagMessage()
context->GetDiagnostics()->Error(DiagMessage()
<< "PNG image dimensions are too large: "
<< width << "x" << height);
return {};
}
std::unique_ptr<Image> outputImage = util::make_unique<Image>();
outputImage->width = static_cast<int32_t>(width);
outputImage->height = static_cast<int32_t>(height);
std::unique_ptr<Image> output_image = util::make_unique<Image>();
output_image->width = static_cast<int32_t>(width);
output_image->height = static_cast<int32_t>(height);
const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
assert(rowBytes == 4 * width); // RGBA
const size_t row_bytes = png_get_rowbytes(read_ptr, info_ptr);
CHECK(row_bytes == 4 * width); // RGBA
// Allocate one large block to hold the image.
outputImage->data =
std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
output_image->data =
std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
// Create an array of rows that index into the data block.
outputImage->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
for (uint32_t h = 0; h < height; h++) {
outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
output_image->rows[h] = output_image->data.get() + (h * row_bytes);
}
// Actually read the image pixels.
png_read_image(readPtr, outputImage->rows.get());
png_read_image(read_ptr, output_image->rows.get());
// Finish reading. This will read any other chunks after the image data.
png_read_end(readPtr, infoPtr);
png_read_end(read_ptr, info_ptr);
return outputImage;
return output_image;
}
/**
@@ -309,57 +315,58 @@ constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
// - Grayscale + cheap alpha
// - Grayscale + alpha
//
static int pickColorType(int32_t width, int32_t height, bool grayScale,
bool convertibleToGrayScale, bool hasNinePatch,
size_t colorPaletteSize, size_t alphaPaletteSize) {
const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
const size_t alphaChunkSize = 16 + alphaPaletteSize;
const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
const size_t colorDataChunkSize = 16 + 3 * width * height;
const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
const size_t paletteDataChunkSize = 16 + width * height;
static int PickColorType(int32_t width, int32_t height, bool grayscale,
bool convertible_to_grayscale, bool has_nine_patch,
size_t color_palette_size, size_t alpha_palette_size) {
const size_t palette_chunk_size = 16 + color_palette_size * 3;
const size_t alpha_chunk_size = 16 + alpha_palette_size;
const size_t color_alpha_data_chunk_size = 16 + 4 * width * height;
const size_t color_data_chunk_size = 16 + 3 * width * height;
const size_t grayscale_alpha_data_chunk_size = 16 + 2 * width * height;
const size_t palette_data_chunk_size = 16 + width * height;
if (grayScale) {
if (alphaPaletteSize == 0) {
if (grayscale) {
if (alpha_palette_size == 0) {
// This is the smallest the data can be.
return PNG_COLOR_TYPE_GRAY;
} else if (colorPaletteSize <= 256 && !hasNinePatch) {
} else if (color_palette_size <= 256 && !has_nine_patch) {
// This grayscale has alpha and can fit within a palette.
// See if it is worth fitting into a palette.
const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
paletteDataChunkSize +
kPaletteOverheadConstant;
if (grayScaleAlphaDataChunkSize > paletteThreshold) {
const size_t palette_threshold = palette_chunk_size + alpha_chunk_size +
palette_data_chunk_size +
kPaletteOverheadConstant;
if (grayscale_alpha_data_chunk_size > palette_threshold) {
return PNG_COLOR_TYPE_PALETTE;
}
}
return PNG_COLOR_TYPE_GRAY_ALPHA;
}
if (colorPaletteSize <= 256 && !hasNinePatch) {
if (color_palette_size <= 256 && !has_nine_patch) {
// This image can fit inside a palette. Let's see if it is worth it.
size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
size_t totalSizeWithoutPalette = colorDataChunkSize;
if (alphaPaletteSize > 0) {
totalSizeWithPalette += alphaPaletteSize;
totalSizeWithoutPalette = colorAlphaDataChunkSize;
size_t total_size_with_palette =
palette_data_chunk_size + palette_chunk_size;
size_t total_size_without_palette = color_data_chunk_size;
if (alpha_palette_size > 0) {
total_size_with_palette += alpha_palette_size;
total_size_without_palette = color_alpha_data_chunk_size;
}
if (totalSizeWithoutPalette >
totalSizeWithPalette + kPaletteOverheadConstant) {
if (total_size_without_palette >
total_size_with_palette + kPaletteOverheadConstant) {
return PNG_COLOR_TYPE_PALETTE;
}
}
if (convertibleToGrayScale) {
if (alphaPaletteSize == 0) {
if (convertible_to_grayscale) {
if (alpha_palette_size == 0) {
return PNG_COLOR_TYPE_GRAY;
} else {
return PNG_COLOR_TYPE_GRAY_ALPHA;
}
}
if (alphaPaletteSize == 0) {
if (alpha_palette_size == 0) {
return PNG_COLOR_TYPE_RGB;
}
return PNG_COLOR_TYPE_RGBA;
@@ -371,11 +378,11 @@ static int pickColorType(int32_t width, int32_t height, bool grayScale,
// This must be done before writing image data.
// Image data must be transformed to use the indices assigned within the
// palette.
static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
std::unordered_map<uint32_t, int>* colorPalette,
std::unordered_set<uint32_t>* alphaPalette) {
assert(colorPalette->size() <= 256);
assert(alphaPalette->size() <= 256);
static void WritePalette(png_structp write_ptr, png_infop write_info_ptr,
std::unordered_map<uint32_t, int>* color_palette,
std::unordered_set<uint32_t>* alpha_palette) {
CHECK(color_palette->size() <= 256);
CHECK(alpha_palette->size() <= 256);
// Populate the PNG palette struct and assign indices to the color
// palette.
@@ -384,160 +391,161 @@ static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
// This will ensure that we can truncate the alpha palette if it is
// smaller than the color palette.
int index = 0;
for (uint32_t color : *alphaPalette) {
(*colorPalette)[color] = index++;
for (uint32_t color : *alpha_palette) {
(*color_palette)[color] = index++;
}
// Assign the rest of the entries.
for (auto& entry : *colorPalette) {
for (auto& entry : *color_palette) {
if (entry.second == -1) {
entry.second = index++;
}
}
// Create the PNG color palette struct.
auto colorPaletteBytes =
std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
auto color_palette_bytes =
std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
std::unique_ptr<png_byte[]> alphaPaletteBytes;
if (!alphaPalette->empty()) {
alphaPaletteBytes =
std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
std::unique_ptr<png_byte[]> alpha_palette_bytes;
if (!alpha_palette->empty()) {
alpha_palette_bytes =
std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
}
for (const auto& entry : *colorPalette) {
for (const auto& entry : *color_palette) {
const uint32_t color = entry.first;
const int index = entry.second;
assert(index >= 0);
assert(static_cast<size_t>(index) < colorPalette->size());
CHECK(index >= 0);
CHECK(static_cast<size_t>(index) < color_palette->size());
png_colorp slot = colorPaletteBytes.get() + index;
png_colorp slot = color_palette_bytes.get() + index;
slot->red = color >> 24;
slot->green = color >> 16;
slot->blue = color >> 8;
const png_byte alpha = color & 0x000000ff;
if (alpha != 0xff && alphaPaletteBytes) {
assert(static_cast<size_t>(index) < alphaPalette->size());
alphaPaletteBytes[index] = alpha;
if (alpha != 0xff && alpha_palette_bytes) {
CHECK(static_cast<size_t>(index) < alpha_palette->size());
alpha_palette_bytes[index] = alpha;
}
}
// The bytes get copied here, so it is safe to release colorPaletteBytes at
// The bytes get copied here, so it is safe to release color_palette_bytes at
// the end of function
// scope.
png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(),
colorPalette->size());
png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(),
color_palette->size());
if (alphaPaletteBytes) {
png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(),
alphaPalette->size(), nullptr);
if (alpha_palette_bytes) {
png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(),
alpha_palette->size(), nullptr);
}
}
// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
// Write the 9-patch custom PNG chunks to write_info_ptr. This must be done
// before
// writing image data.
static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
const NinePatch* ninePatch) {
static void WriteNinePatch(png_structp write_ptr, png_infop write_info_ptr,
const NinePatch* nine_patch) {
// The order of the chunks is important.
// 9-patch code in older platforms expects the 9-patch chunk to
// be last.
png_unknown_chunk unknownChunks[3];
memset(unknownChunks, 0, sizeof(unknownChunks));
png_unknown_chunk unknown_chunks[3];
memset(unknown_chunks, 0, sizeof(unknown_chunks));
size_t index = 0;
size_t chunkLen = 0;
size_t chunk_len = 0;
std::unique_ptr<uint8_t[]> serializedOutline =
ninePatch->serializeRoundedRectOutline(&chunkLen);
strcpy((char*)unknownChunks[index].name, "npOl");
unknownChunks[index].size = chunkLen;
unknownChunks[index].data = (png_bytep)serializedOutline.get();
unknownChunks[index].location = PNG_HAVE_PLTE;
std::unique_ptr<uint8_t[]> serialized_outline =
nine_patch->SerializeRoundedRectOutline(&chunk_len);
strcpy((char*)unknown_chunks[index].name, "npOl");
unknown_chunks[index].size = chunk_len;
unknown_chunks[index].data = (png_bytep)serialized_outline.get();
unknown_chunks[index].location = PNG_HAVE_PLTE;
index++;
std::unique_ptr<uint8_t[]> serializedLayoutBounds;
if (ninePatch->layoutBounds.nonZero()) {
serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
strcpy((char*)unknownChunks[index].name, "npLb");
unknownChunks[index].size = chunkLen;
unknownChunks[index].data = (png_bytep)serializedLayoutBounds.get();
unknownChunks[index].location = PNG_HAVE_PLTE;
std::unique_ptr<uint8_t[]> serialized_layout_bounds;
if (nine_patch->layout_bounds.nonZero()) {
serialized_layout_bounds = nine_patch->SerializeLayoutBounds(&chunk_len);
strcpy((char*)unknown_chunks[index].name, "npLb");
unknown_chunks[index].size = chunk_len;
unknown_chunks[index].data = (png_bytep)serialized_layout_bounds.get();
unknown_chunks[index].location = PNG_HAVE_PLTE;
index++;
}
std::unique_ptr<uint8_t[]> serializedNinePatch =
ninePatch->serializeBase(&chunkLen);
strcpy((char*)unknownChunks[index].name, "npTc");
unknownChunks[index].size = chunkLen;
unknownChunks[index].data = (png_bytep)serializedNinePatch.get();
unknownChunks[index].location = PNG_HAVE_PLTE;
std::unique_ptr<uint8_t[]> serialized_nine_patch =
nine_patch->SerializeBase(&chunk_len);
strcpy((char*)unknown_chunks[index].name, "npTc");
unknown_chunks[index].size = chunk_len;
unknown_chunks[index].data = (png_bytep)serialized_nine_patch.get();
unknown_chunks[index].location = PNG_HAVE_PLTE;
index++;
// Handle all unknown chunks. We are manually setting the chunks here,
// so we will only ever handle our custom chunks.
png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
// Set the actual chunks here. The data gets copied, so our buffers can
// safely go out of scope.
png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
png_set_unknown_chunks(write_ptr, write_info_ptr, unknown_chunks, index);
}
bool writePng(IAaptContext* context, const Image* image,
const NinePatch* ninePatch, io::OutputStream* out,
bool WritePng(IAaptContext* context, const Image* image,
const NinePatch* nine_patch, io::OutputStream* out,
const PngOptions& options) {
// Create and initialize the write png_struct with the default error and
// warning handlers.
// The header version is also passed in to ensure that this was built against
// the same
// version of libpng.
png_structp writePtr =
png_structp write_ptr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (writePtr == nullptr) {
context->getDiagnostics()->error(
if (write_ptr == nullptr) {
context->GetDiagnostics()->Error(
DiagMessage() << "failed to create libpng write png_struct");
return false;
}
// Allocate memory to store image header data.
png_infop writeInfoPtr = png_create_info_struct(writePtr);
if (writeInfoPtr == nullptr) {
context->getDiagnostics()->error(
png_infop write_info_ptr = png_create_info_struct(write_ptr);
if (write_info_ptr == nullptr) {
context->GetDiagnostics()->Error(
DiagMessage() << "failed to create libpng write png_info");
png_destroy_write_struct(&writePtr, nullptr);
png_destroy_write_struct(&write_ptr, nullptr);
return false;
}
// Automatically release PNG resources at end of scope.
PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
PngWriteStructDeleter png_write_deleter(write_ptr, write_info_ptr);
// libpng uses longjmp to jump to error handling routines.
// setjmp will return true only if it was jumped to, aka, there was an error.
if (setjmp(png_jmpbuf(writePtr))) {
if (setjmp(png_jmpbuf(write_ptr))) {
return false;
}
// Handle warnings with our IDiagnostics.
png_set_error_fn(writePtr, (png_voidp)context->getDiagnostics(), logError,
logWarning);
png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError,
LogWarning);
// Set up the write functions which write to our custom data sources.
png_set_write_fn(writePtr, (png_voidp)out, writeDataToStream, nullptr);
png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
// We want small files and can take the performance hit to achieve this goal.
png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
// Begin analysis of the image data.
// Scan the entire image and determine if:
// 1. Every pixel has R == G == B (grayscale)
// 2. Every pixel has A == 255 (opaque)
// 3. There are no more than 256 distinct RGBA colors (palette).
std::unordered_map<uint32_t, int> colorPalette;
std::unordered_set<uint32_t> alphaPalette;
bool needsToZeroRGBChannelsOfTransparentPixels = false;
bool grayScale = true;
int maxGrayDeviation = 0;
std::unordered_map<uint32_t, int> color_palette;
std::unordered_set<uint32_t> alpha_palette;
bool needs_to_zero_rgb_channels_of_transparent_pixels = false;
bool grayscale = true;
int max_gray_deviation = 0;
for (int32_t y = 0; y < image->height; y++) {
const uint8_t* row = image->rows[y];
@@ -551,60 +559,60 @@ bool writePng(IAaptContext* context, const Image* image,
// The color is completely transparent.
// For purposes of palettes and grayscale optimization,
// treat all channels as 0x00.
needsToZeroRGBChannelsOfTransparentPixels =
needsToZeroRGBChannelsOfTransparentPixels ||
needs_to_zero_rgb_channels_of_transparent_pixels =
needs_to_zero_rgb_channels_of_transparent_pixels ||
(red != 0 || green != 0 || blue != 0);
red = green = blue = 0;
}
// Insert the color into the color palette.
const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
colorPalette[color] = -1;
color_palette[color] = -1;
// If the pixel has non-opaque alpha, insert it into the
// alpha palette.
if (alpha != 0xff) {
alphaPalette.insert(color);
alpha_palette.insert(color);
}
// Check if the image is indeed grayscale.
if (grayScale) {
if (grayscale) {
if (red != green || red != blue) {
grayScale = false;
grayscale = false;
}
}
// Calculate the gray scale deviation so that it can be compared
// with the threshold.
maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
max_gray_deviation = std::max(std::abs(red - green), max_gray_deviation);
max_gray_deviation = std::max(std::abs(green - blue), max_gray_deviation);
max_gray_deviation = std::max(std::abs(blue - red), max_gray_deviation);
}
}
if (context->verbose()) {
if (context->IsVerbose()) {
DiagMessage msg;
msg << " paletteSize=" << colorPalette.size()
<< " alphaPaletteSize=" << alphaPalette.size()
<< " maxGrayDeviation=" << maxGrayDeviation
<< " grayScale=" << (grayScale ? "true" : "false");
context->getDiagnostics()->note(msg);
msg << " paletteSize=" << color_palette.size()
<< " alphaPaletteSize=" << alpha_palette.size()
<< " maxGrayDeviation=" << max_gray_deviation
<< " grayScale=" << (grayscale ? "true" : "false");
context->GetDiagnostics()->Note(msg);
}
const bool convertibleToGrayScale =
maxGrayDeviation <= options.grayScaleTolerance;
const bool convertible_to_grayscale =
max_gray_deviation <= options.grayscale_tolerance;
const int newColorType = pickColorType(
image->width, image->height, grayScale, convertibleToGrayScale,
ninePatch != nullptr, colorPalette.size(), alphaPalette.size());
const int new_color_type = PickColorType(
image->width, image->height, grayscale, convertible_to_grayscale,
nine_patch != nullptr, color_palette.size(), alpha_palette.size());
if (context->verbose()) {
if (context->IsVerbose()) {
DiagMessage msg;
msg << "encoding PNG ";
if (ninePatch) {
if (nine_patch) {
msg << "(with 9-patch) as ";
}
switch (newColorType) {
switch (new_color_type) {
case PNG_COLOR_TYPE_GRAY:
msg << "GRAY";
break;
@@ -621,137 +629,138 @@ bool writePng(IAaptContext* context, const Image* image,
msg << "PALETTE";
break;
default:
msg << "unknown type " << newColorType;
msg << "unknown type " << new_color_type;
break;
}
context->getDiagnostics()->note(msg);
context->GetDiagnostics()->Note(msg);
}
png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8,
newColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8,
new_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
if (newColorType & PNG_COLOR_MASK_PALETTE) {
if (new_color_type & PNG_COLOR_MASK_PALETTE) {
// Assigns indices to the palette, and writes the encoded palette to the
// libpng writePtr.
writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
png_set_filter(writePtr, 0, PNG_NO_FILTERS);
WritePalette(write_ptr, write_info_ptr, &color_palette, &alpha_palette);
png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
} else {
png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
}
if (ninePatch) {
writeNinePatch(writePtr, writeInfoPtr, ninePatch);
if (nine_patch) {
WriteNinePatch(write_ptr, write_info_ptr, nine_patch);
}
// Flush our updates to the header.
png_write_info(writePtr, writeInfoPtr);
png_write_info(write_ptr, write_info_ptr);
// Write out each row of image data according to its encoding.
if (newColorType == PNG_COLOR_TYPE_PALETTE) {
if (new_color_type == PNG_COLOR_TYPE_PALETTE) {
// 1 byte/pixel.
auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
for (int32_t y = 0; y < image->height; y++) {
png_const_bytep inRow = image->rows[y];
png_const_bytep in_row = image->rows[y];
for (int32_t x = 0; x < image->width; x++) {
int rr = *inRow++;
int gg = *inRow++;
int bb = *inRow++;
int aa = *inRow++;
int rr = *in_row++;
int gg = *in_row++;
int bb = *in_row++;
int aa = *in_row++;
if (aa == 0) {
// Zero out color channels when transparent.
rr = gg = bb = 0;
}
const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
const int idx = colorPalette[color];
assert(idx != -1);
outRow[x] = static_cast<png_byte>(idx);
const int idx = color_palette[color];
CHECK(idx != -1);
out_row[x] = static_cast<png_byte>(idx);
}
png_write_row(writePtr, outRow.get());
png_write_row(write_ptr, out_row.get());
}
} else if (newColorType == PNG_COLOR_TYPE_GRAY ||
newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
} else if (new_color_type == PNG_COLOR_TYPE_GRAY ||
new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
const size_t bpp = new_color_type == PNG_COLOR_TYPE_GRAY ? 1 : 2;
auto out_row =
std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
for (int32_t y = 0; y < image->height; y++) {
png_const_bytep inRow = image->rows[y];
png_const_bytep in_row = image->rows[y];
for (int32_t x = 0; x < image->width; x++) {
int rr = inRow[x * 4];
int gg = inRow[x * 4 + 1];
int bb = inRow[x * 4 + 2];
int aa = inRow[x * 4 + 3];
int rr = in_row[x * 4];
int gg = in_row[x * 4 + 1];
int bb = in_row[x * 4 + 2];
int aa = in_row[x * 4 + 3];
if (aa == 0) {
// Zero out the gray channel when transparent.
rr = gg = bb = 0;
}
if (grayScale) {
if (grayscale) {
// The image was already grayscale, red == green == blue.
outRow[x * bpp] = inRow[x * 4];
out_row[x * bpp] = in_row[x * 4];
} else {
// The image is convertible to grayscale, use linear-luminance of
// sRGB colorspace:
// https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
outRow[x * bpp] =
out_row[x * bpp] =
(png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
}
if (bpp == 2) {
// Write out alpha if we have it.
outRow[x * bpp + 1] = aa;
out_row[x * bpp + 1] = aa;
}
}
png_write_row(writePtr, outRow.get());
png_write_row(write_ptr, out_row.get());
}
} else if (newColorType == PNG_COLOR_TYPE_RGB ||
newColorType == PNG_COLOR_TYPE_RGBA) {
const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
if (needsToZeroRGBChannelsOfTransparentPixels) {
} else if (new_color_type == PNG_COLOR_TYPE_RGB ||
new_color_type == PNG_COLOR_TYPE_RGBA) {
const size_t bpp = new_color_type == PNG_COLOR_TYPE_RGB ? 3 : 4;
if (needs_to_zero_rgb_channels_of_transparent_pixels) {
// The source RGBA data can't be used as-is, because we need to zero out
// the RGB
// values of transparent pixels.
auto outRow =
auto out_row =
std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
for (int32_t y = 0; y < image->height; y++) {
png_const_bytep inRow = image->rows[y];
png_const_bytep in_row = image->rows[y];
for (int32_t x = 0; x < image->width; x++) {
int rr = *inRow++;
int gg = *inRow++;
int bb = *inRow++;
int aa = *inRow++;
int rr = *in_row++;
int gg = *in_row++;
int bb = *in_row++;
int aa = *in_row++;
if (aa == 0) {
// Zero out the RGB channels when transparent.
rr = gg = bb = 0;
}
outRow[x * bpp] = rr;
outRow[x * bpp + 1] = gg;
outRow[x * bpp + 2] = bb;
out_row[x * bpp] = rr;
out_row[x * bpp + 1] = gg;
out_row[x * bpp + 2] = bb;
if (bpp == 4) {
outRow[x * bpp + 3] = aa;
out_row[x * bpp + 3] = aa;
}
}
png_write_row(writePtr, outRow.get());
png_write_row(write_ptr, out_row.get());
}
} else {
// The source image can be used as-is, just tell libpng whether or not to
// ignore
// the alpha channel.
if (newColorType == PNG_COLOR_TYPE_RGB) {
if (new_color_type == PNG_COLOR_TYPE_RGB) {
// Delete the extraneous alpha values that we appended to our buffer
// when reading the original values.
png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
}
png_write_image(writePtr, image->rows.get());
png_write_image(write_ptr, image->rows.get());
}
} else {
assert(false && "unreachable");
LOG(FATAL) << "unreachable";
}
png_write_end(writePtr, writeInfoPtr);
png_write_end(write_ptr, write_info_ptr);
return true;
}

View File

@@ -15,27 +15,29 @@
*/
#include "compile/PseudolocaleGenerator.h"
#include <algorithm>
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "compile/Pseudolocalizer.h"
#include <algorithm>
namespace aapt {
std::unique_ptr<StyledString> pseudolocalizeStyledString(
std::unique_ptr<StyledString> PseudolocalizeStyledString(
StyledString* string, Pseudolocalizer::Method method, StringPool* pool) {
Pseudolocalizer localizer(method);
const StringPiece originalText = *string->value->str;
const StringPiece original_text = *string->value->str;
StyleString localized;
// Copy the spans. We will update their offsets when we localize.
localized.spans.reserve(string->value->spans.size());
for (const StringPool::Span& span : string->value->spans) {
localized.spans.push_back(Span{*span.name, span.firstChar, span.lastChar});
localized.spans.push_back(
Span{*span.name, span.first_char, span.last_char});
}
// The ranges are all represented with a single value. This is the start of
@@ -49,8 +51,8 @@ std::unique_ptr<StyledString> pseudolocalizeStyledString(
// Since this struct represents the start of one range and end of another,
// we have
// the two pointers respectively.
uint32_t* updateStart;
uint32_t* updateEnd;
uint32_t* update_start;
uint32_t* update_end;
};
auto cmp = [](const Range& r, size_t index) -> bool {
@@ -64,109 +66,113 @@ std::unique_ptr<StyledString> pseudolocalizeStyledString(
//
std::vector<Range> ranges;
ranges.push_back(Range{0});
ranges.push_back(Range{originalText.size() - 1});
ranges.push_back(Range{original_text.size() - 1});
for (size_t i = 0; i < string->value->spans.size(); i++) {
const StringPool::Span& span = string->value->spans[i];
// Insert or update the Range marker for the start of this span.
auto iter =
std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
if (iter != ranges.end() && iter->start == span.firstChar) {
iter->updateStart = &localized.spans[i].firstChar;
std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp);
if (iter != ranges.end() && iter->start == span.first_char) {
iter->update_start = &localized.spans[i].first_char;
} else {
ranges.insert(
iter, Range{span.firstChar, &localized.spans[i].firstChar, nullptr});
ranges.insert(iter, Range{span.first_char, &localized.spans[i].first_char,
nullptr});
}
// Insert or update the Range marker for the end of this span.
iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
if (iter != ranges.end() && iter->start == span.lastChar) {
iter->updateEnd = &localized.spans[i].lastChar;
iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp);
if (iter != ranges.end() && iter->start == span.last_char) {
iter->update_end = &localized.spans[i].last_char;
} else {
ranges.insert(
iter, Range{span.lastChar, nullptr, &localized.spans[i].lastChar});
iter, Range{span.last_char, nullptr, &localized.spans[i].last_char});
}
}
localized.str += localizer.start();
localized.str += localizer.Start();
// Iterate over the ranges and localize each section.
for (size_t i = 0; i < ranges.size(); i++) {
const size_t start = ranges[i].start;
size_t len = originalText.size() - start;
size_t len = original_text.size() - start;
if (i + 1 < ranges.size()) {
len = ranges[i + 1].start - start;
}
if (ranges[i].updateStart) {
*ranges[i].updateStart = localized.str.size();
if (ranges[i].update_start) {
*ranges[i].update_start = localized.str.size();
}
if (ranges[i].updateEnd) {
*ranges[i].updateEnd = localized.str.size();
if (ranges[i].update_end) {
*ranges[i].update_end = localized.str.size();
}
localized.str += localizer.text(originalText.substr(start, len));
localized.str += localizer.Text(original_text.substr(start, len));
}
localized.str += localizer.end();
localized.str += localizer.End();
std::unique_ptr<StyledString> localizedString =
util::make_unique<StyledString>(pool->makeRef(localized));
localizedString->setSource(string->getSource());
return localizedString;
std::unique_ptr<StyledString> localized_string =
util::make_unique<StyledString>(pool->MakeRef(localized));
localized_string->SetSource(string->GetSource());
return localized_string;
}
namespace {
struct Visitor : public RawValueVisitor {
StringPool* mPool;
Pseudolocalizer::Method mMethod;
Pseudolocalizer mLocalizer;
class Visitor : public RawValueVisitor {
public:
// Either value or item will be populated upon visiting the value.
std::unique_ptr<Value> mValue;
std::unique_ptr<Item> mItem;
std::unique_ptr<Value> value;
std::unique_ptr<Item> item;
Visitor(StringPool* pool, Pseudolocalizer::Method method)
: mPool(pool), mMethod(method), mLocalizer(method) {}
: pool_(pool), method_(method), localizer_(method) {}
void visit(Plural* plural) override {
void Visit(Plural* plural) override {
std::unique_ptr<Plural> localized = util::make_unique<Plural>();
for (size_t i = 0; i < plural->values.size(); i++) {
Visitor subVisitor(mPool, mMethod);
Visitor sub_visitor(pool_, method_);
if (plural->values[i]) {
plural->values[i]->accept(&subVisitor);
if (subVisitor.mValue) {
localized->values[i] = std::move(subVisitor.mItem);
plural->values[i]->Accept(&sub_visitor);
if (sub_visitor.value) {
localized->values[i] = std::move(sub_visitor.item);
} else {
localized->values[i] =
std::unique_ptr<Item>(plural->values[i]->clone(mPool));
std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
}
}
}
localized->setSource(plural->getSource());
localized->setWeak(true);
mValue = std::move(localized);
localized->SetSource(plural->GetSource());
localized->SetWeak(true);
value = std::move(localized);
}
void visit(String* string) override {
void Visit(String* string) override {
std::string result =
mLocalizer.start() + mLocalizer.text(*string->value) + mLocalizer.end();
localizer_.Start() + localizer_.Text(*string->value) + localizer_.End();
std::unique_ptr<String> localized =
util::make_unique<String>(mPool->makeRef(result));
localized->setSource(string->getSource());
localized->setWeak(true);
mItem = std::move(localized);
util::make_unique<String>(pool_->MakeRef(result));
localized->SetSource(string->GetSource());
localized->SetWeak(true);
item = std::move(localized);
}
void visit(StyledString* string) override {
mItem = pseudolocalizeStyledString(string, mMethod, mPool);
mItem->setWeak(true);
void Visit(StyledString* string) override {
item = PseudolocalizeStyledString(string, method_, pool_);
item->SetWeak(true);
}
private:
DISALLOW_COPY_AND_ASSIGN(Visitor);
StringPool* pool_;
Pseudolocalizer::Method method_;
Pseudolocalizer localizer_;
};
ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
ConfigDescription ModifyConfigForPseudoLocale(const ConfigDescription& base,
Pseudolocalizer::Method m) {
ConfigDescription modified = base;
switch (m) {
@@ -189,31 +195,31 @@ ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
return modified;
}
void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
ResourceConfigValue* originalValue,
void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
ResourceConfigValue* original_value,
StringPool* pool, ResourceEntry* entry) {
Visitor visitor(pool, method);
originalValue->value->accept(&visitor);
original_value->value->Accept(&visitor);
std::unique_ptr<Value> localizedValue;
if (visitor.mValue) {
localizedValue = std::move(visitor.mValue);
} else if (visitor.mItem) {
localizedValue = std::move(visitor.mItem);
std::unique_ptr<Value> localized_value;
if (visitor.value) {
localized_value = std::move(visitor.value);
} else if (visitor.item) {
localized_value = std::move(visitor.item);
}
if (!localizedValue) {
if (!localized_value) {
return;
}
ConfigDescription configWithAccent =
modifyConfigForPseudoLocale(originalValue->config, method);
ConfigDescription config_with_accent =
ModifyConfigForPseudoLocale(original_value->config, method);
ResourceConfigValue* newConfigValue =
entry->findOrCreateValue(configWithAccent, originalValue->product);
if (!newConfigValue->value) {
ResourceConfigValue* new_config_value =
entry->FindOrCreateValue(config_with_accent, original_value->product);
if (!new_config_value->value) {
// Only use auto-generated pseudo-localization if none is defined.
newConfigValue->value = std::move(localizedValue);
new_config_value->value = std::move(localized_value);
}
}
@@ -222,29 +228,30 @@ void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
* default locale)
* and is translateable.
*/
static bool isPseudolocalizable(ResourceConfigValue* configValue) {
const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
static bool IsPseudolocalizable(ResourceConfigValue* config_value) {
const int diff =
config_value->config.diff(ConfigDescription::DefaultConfig());
if (diff & ConfigDescription::CONFIG_LOCALE) {
return false;
}
return configValue->value->isTranslateable();
return config_value->value->IsTranslateable();
}
} // namespace
bool PseudolocaleGenerator::consume(IAaptContext* context,
bool PseudolocaleGenerator::Consume(IAaptContext* context,
ResourceTable* table) {
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
std::vector<ResourceConfigValue*> values =
entry->findValuesIf(isPseudolocalizable);
entry->FindValuesIf(IsPseudolocalizable);
for (ResourceConfigValue* value : values) {
pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
&table->stringPool, entry.get());
pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
&table->stringPool, entry.get());
PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
&table->string_pool, entry.get());
PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
&table->string_pool, entry.get());
}
}
}

View File

@@ -23,11 +23,11 @@
namespace aapt {
std::unique_ptr<StyledString> pseudolocalizeStyledString(
std::unique_ptr<StyledString> PseudolocalizeStyledString(
StyledString* string, Pseudolocalizer::Method method, StringPool* pool);
struct PseudolocaleGenerator : public IResourceTableConsumer {
bool consume(IAaptContext* context, ResourceTable* table) override;
bool Consume(IAaptContext* context, ResourceTable* table) override;
};
} // namespace aapt

View File

@@ -15,118 +15,119 @@
*/
#include "compile/PseudolocaleGenerator.h"
#include "test/Test.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
StringPool pool;
StyleString originalStyle;
originalStyle.str = "Hello world!";
originalStyle.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
StyleString original_style;
original_style.str = "Hello world!";
original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
std::unique_ptr<StyledString> newString = pseudolocalizeStyledString(
util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
Pseudolocalizer::Method::kNone, &pool);
EXPECT_EQ(originalStyle.str, *newString->value->str);
ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
EXPECT_EQ(original_style.str, *new_string->value->str);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
EXPECT_EQ(std::string("He").size(), newString->value->spans[0].firstChar);
EXPECT_EQ(std::string("Hel").size(), newString->value->spans[0].lastChar);
EXPECT_EQ(std::string("b"), *newString->value->spans[0].name);
EXPECT_EQ(std::string("He").size(), new_string->value->spans[0].first_char);
EXPECT_EQ(std::string("Hel").size(), new_string->value->spans[0].last_char);
EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
EXPECT_EQ(std::string("Hello ").size(), newString->value->spans[1].firstChar);
EXPECT_EQ(std::string("Hello w").size(), newString->value->spans[1].lastChar);
EXPECT_EQ(std::string("b"), *newString->value->spans[1].name);
EXPECT_EQ(std::string("Hello ").size(),
new_string->value->spans[1].first_char);
EXPECT_EQ(std::string("Hello w").size(),
new_string->value->spans[1].last_char);
EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name);
EXPECT_EQ(std::string("H").size(), newString->value->spans[2].firstChar);
EXPECT_EQ(std::string("H").size(), new_string->value->spans[2].first_char);
EXPECT_EQ(std::string("Hello worl").size(),
newString->value->spans[2].lastChar);
EXPECT_EQ(std::string("i"), *newString->value->spans[2].name);
new_string->value->spans[2].last_char);
EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
originalStyle.spans.push_back(Span{"em", 0, 11u});
original_style.spans.push_back(Span{"em", 0, 11u});
newString = pseudolocalizeStyledString(
util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
new_string = PseudolocalizeStyledString(
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
Pseudolocalizer::Method::kAccent, &pool);
EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str);
ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
EXPECT_EQ(std::string("[Ĥé").size(), newString->value->spans[0].firstChar);
EXPECT_EQ(std::string("[Ĥéļ").size(), newString->value->spans[0].lastChar);
EXPECT_EQ(std::string("[Ĥé").size(), new_string->value->spans[0].first_char);
EXPECT_EQ(std::string("[Ĥéļ").size(), new_string->value->spans[0].last_char);
EXPECT_EQ(std::string("[Ĥéļļö ").size(),
newString->value->spans[1].firstChar);
new_string->value->spans[1].first_char);
EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(),
newString->value->spans[1].lastChar);
new_string->value->spans[1].last_char);
EXPECT_EQ(std::string("").size(), newString->value->spans[2].firstChar);
EXPECT_EQ(std::string("").size(), new_string->value->spans[2].first_char);
EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(),
newString->value->spans[2].lastChar);
new_string->value->spans[2].last_char);
EXPECT_EQ(std::string("[").size(), newString->value->spans[3].firstChar);
EXPECT_EQ(std::string("[").size(), new_string->value->spans[3].first_char);
EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(),
newString->value->spans[3].lastChar);
new_string->value->spans[3].last_char);
}
TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.addString("android:string/one", "one")
.addString("android:string/two", ResourceId{},
test::parseConfigOrDie("en"), "two")
.addString("android:string/three", "three")
.addString("android:string/three", ResourceId{},
test::parseConfigOrDie("en-rXA"), "three")
.addString("android:string/four", "four")
.build();
.AddString("android:string/one", "one")
.AddString("android:string/two", ResourceId{},
test::ParseConfigOrDie("en"), "two")
.AddString("android:string/three", "three")
.AddString("android:string/three", ResourceId{},
test::ParseConfigOrDie("en-rXA"), "three")
.AddString("android:string/four", "four")
.Build();
String* val = test::getValue<String>(table.get(), "android:string/four");
String* val = test::GetValue<String>(table.get(), "android:string/four");
ASSERT_NE(nullptr, val);
val->setTranslateable(false);
val->SetTranslateable(false);
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
PseudolocaleGenerator generator;
ASSERT_TRUE(generator.consume(context.get(), table.get()));
ASSERT_TRUE(generator.Consume(context.get(), table.get()));
// Normal pseudolocalization should take place.
ASSERT_NE(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/one",
test::parseConfigOrDie("en-rXA")));
test::GetValueForConfig<String>(table.get(), "android:string/one",
test::ParseConfigOrDie("en-rXA")));
ASSERT_NE(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/one",
test::parseConfigOrDie("ar-rXB")));
test::GetValueForConfig<String>(table.get(), "android:string/one",
test::ParseConfigOrDie("ar-rXB")));
// No default config for android:string/two, so no pseudlocales should exist.
ASSERT_EQ(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/two",
test::parseConfigOrDie("en-rXA")));
test::GetValueForConfig<String>(table.get(), "android:string/two",
test::ParseConfigOrDie("en-rXA")));
ASSERT_EQ(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/two",
test::parseConfigOrDie("ar-rXB")));
test::GetValueForConfig<String>(table.get(), "android:string/two",
test::ParseConfigOrDie("ar-rXB")));
// Check that we didn't override manual pseudolocalization.
val = test::getValueForConfig<String>(table.get(), "android:string/three",
test::parseConfigOrDie("en-rXA"));
val = test::GetValueForConfig<String>(table.get(), "android:string/three",
test::ParseConfigOrDie("en-rXA"));
ASSERT_NE(nullptr, val);
EXPECT_EQ(std::string("three"), *val->value);
ASSERT_NE(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/three",
test::parseConfigOrDie("ar-rXB")));
test::GetValueForConfig<String>(table.get(), "android:string/three",
test::ParseConfigOrDie("ar-rXB")));
// Check that four's translateable marker was honored.
ASSERT_EQ(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/four",
test::parseConfigOrDie("en-rXA")));
test::GetValueForConfig<String>(table.get(), "android:string/four",
test::ParseConfigOrDie("en-rXA")));
ASSERT_EQ(nullptr,
test::getValueForConfig<String>(table.get(), "android:string/four",
test::parseConfigOrDie("ar-rXB")));
test::GetValueForConfig<String>(table.get(), "android:string/four",
test::ParseConfigOrDie("ar-rXB")));
}
} // namespace aapt

View File

@@ -15,77 +15,78 @@
*/
#include "compile/Pseudolocalizer.h"
#include "util/Util.h"
namespace aapt {
// String basis to generate expansion
static const std::string k_expansion_string =
static const std::string kExpansionString =
"one two three "
"four five six seven eight nine ten eleven twelve thirteen "
"fourteen fiveteen sixteen seventeen nineteen twenty";
// Special unicode characters to override directionality of the words
static const std::string k_rlm = "\u200f";
static const std::string k_rlo = "\u202e";
static const std::string k_pdf = "\u202c";
static const std::string kRlm = "\u200f";
static const std::string kRlo = "\u202e";
static const std::string kPdf = "\u202c";
// Placeholder marks
static const std::string k_placeholder_open = "\u00bb";
static const std::string k_placeholder_close = "\u00ab";
static const std::string kPlaceholderOpen = "\u00bb";
static const std::string kPlaceholderClose = "\u00ab";
static const char k_arg_start = '{';
static const char k_arg_end = '}';
static const char kArgStart = '{';
static const char kArgEnd = '}';
class PseudoMethodNone : public PseudoMethodImpl {
public:
std::string text(const StringPiece& text) override { return text.toString(); }
std::string placeholder(const StringPiece& text) override {
return text.toString();
std::string Text(const StringPiece& text) override { return text.ToString(); }
std::string Placeholder(const StringPiece& text) override {
return text.ToString();
}
};
class PseudoMethodBidi : public PseudoMethodImpl {
public:
std::string text(const StringPiece& text) override;
std::string placeholder(const StringPiece& text) override;
std::string Text(const StringPiece& text) override;
std::string Placeholder(const StringPiece& text) override;
};
class PseudoMethodAccent : public PseudoMethodImpl {
public:
PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {}
std::string start() override;
std::string end() override;
std::string text(const StringPiece& text) override;
std::string placeholder(const StringPiece& text) override;
PseudoMethodAccent() : depth_(0), word_count_(0), length_(0) {}
std::string Start() override;
std::string End() override;
std::string Text(const StringPiece& text) override;
std::string Placeholder(const StringPiece& text) override;
private:
size_t mDepth;
size_t mWordCount;
size_t mLength;
size_t depth_;
size_t word_count_;
size_t length_;
};
Pseudolocalizer::Pseudolocalizer(Method method) : mLastDepth(0) {
setMethod(method);
Pseudolocalizer::Pseudolocalizer(Method method) : last_depth_(0) {
SetMethod(method);
}
void Pseudolocalizer::setMethod(Method method) {
void Pseudolocalizer::SetMethod(Method method) {
switch (method) {
case Method::kNone:
mImpl = util::make_unique<PseudoMethodNone>();
impl_ = util::make_unique<PseudoMethodNone>();
break;
case Method::kAccent:
mImpl = util::make_unique<PseudoMethodAccent>();
impl_ = util::make_unique<PseudoMethodAccent>();
break;
case Method::kBidi:
mImpl = util::make_unique<PseudoMethodBidi>();
impl_ = util::make_unique<PseudoMethodBidi>();
break;
}
}
std::string Pseudolocalizer::text(const StringPiece& text) {
std::string Pseudolocalizer::Text(const StringPiece& text) {
std::string out;
size_t depth = mLastDepth;
size_t depth = last_depth_;
size_t lastpos, pos;
const size_t length = text.size();
const char* str = text.data();
@@ -101,42 +102,41 @@ std::string Pseudolocalizer::text(const StringPiece& text) {
continue;
}
if (c == k_arg_start) {
if (c == kArgStart) {
depth++;
} else if (c == k_arg_end && depth) {
} else if (c == kArgEnd && depth) {
depth--;
}
if (mLastDepth != depth || pos == length - 1) {
bool pseudo = ((mLastDepth % 2) == 0);
if (last_depth_ != depth || pos == length - 1) {
bool pseudo = ((last_depth_ % 2) == 0);
size_t nextpos = pos;
if (!pseudo || depth == mLastDepth) {
if (!pseudo || depth == last_depth_) {
nextpos++;
}
size_t size = nextpos - lastpos;
if (size) {
std::string chunk = text.substr(lastpos, size).toString();
std::string chunk = text.substr(lastpos, size).ToString();
if (pseudo) {
chunk = mImpl->text(chunk);
} else if (str[lastpos] == k_arg_start &&
str[nextpos - 1] == k_arg_end) {
chunk = mImpl->placeholder(chunk);
chunk = impl_->Text(chunk);
} else if (str[lastpos] == kArgStart && str[nextpos - 1] == kArgEnd) {
chunk = impl_->Placeholder(chunk);
}
out.append(chunk);
}
if (pseudo && depth < mLastDepth) { // End of message
out.append(mImpl->end());
} else if (!pseudo && depth > mLastDepth) { // Start of message
out.append(mImpl->start());
if (pseudo && depth < last_depth_) { // End of message
out.append(impl_->End());
} else if (!pseudo && depth > last_depth_) { // Start of message
out.append(impl_->Start());
}
lastpos = nextpos;
mLastDepth = depth;
last_depth_ = depth;
}
}
return out;
}
static const char* pseudolocalizeChar(const char c) {
static const char* PseudolocalizeChar(const char c) {
switch (c) {
case 'a':
return "\u00e5";
@@ -251,7 +251,7 @@ static const char* pseudolocalizeChar(const char c) {
}
}
static bool isPossibleNormalPlaceholderEnd(const char c) {
static bool IsPossibleNormalPlaceholderEnd(const char c) {
switch (c) {
case 's':
return true;
@@ -300,12 +300,12 @@ static bool isPossibleNormalPlaceholderEnd(const char c) {
}
}
static std::string pseudoGenerateExpansion(const unsigned int length) {
std::string result = k_expansion_string;
static std::string PseudoGenerateExpansion(const unsigned int length) {
std::string result = kExpansionString;
const char* s = result.data();
if (result.size() < length) {
result += " ";
result += pseudoGenerateExpansion(length - result.size());
result += PseudoGenerateExpansion(length - result.size());
} else {
int ext = 0;
// Should contain only whole words, so looking for a space
@@ -320,25 +320,25 @@ static std::string pseudoGenerateExpansion(const unsigned int length) {
return result;
}
std::string PseudoMethodAccent::start() {
std::string PseudoMethodAccent::Start() {
std::string result;
if (mDepth == 0) {
if (depth_ == 0) {
result = "[";
}
mWordCount = mLength = 0;
mDepth++;
word_count_ = length_ = 0;
depth_++;
return result;
}
std::string PseudoMethodAccent::end() {
std::string PseudoMethodAccent::End() {
std::string result;
if (mLength) {
if (length_) {
result += " ";
result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
result += PseudoGenerateExpansion(word_count_ > 3 ? length_ : length_ / 2);
}
mWordCount = mLength = 0;
mDepth--;
if (mDepth == 0) {
word_count_ = length_ = 0;
depth_--;
if (depth_ == 0) {
result += "]";
}
return result;
@@ -349,7 +349,7 @@ std::string PseudoMethodAccent::end() {
*
* Note: This leaves placeholder syntax untouched.
*/
std::string PseudoMethodAccent::text(const StringPiece& source) {
std::string PseudoMethodAccent::Text(const StringPiece& source) {
const char* s = source.data();
std::string result;
const size_t I = source.size();
@@ -365,7 +365,7 @@ std::string PseudoMethodAccent::text(const StringPiece& source) {
++i;
c = s[i];
chunk.append(&c, 1);
if (isPossibleNormalPlaceholderEnd(c)) {
if (IsPossibleNormalPlaceholderEnd(c)) {
end = true;
} else if (i + 1 < I && c == 't') {
++i;
@@ -375,24 +375,24 @@ std::string PseudoMethodAccent::text(const StringPiece& source) {
}
}
// Treat chunk as a placeholder unless it ends with %.
result += ((c == '%') ? chunk : placeholder(chunk));
result += ((c == '%') ? chunk : Placeholder(chunk));
} else if (c == '<' || c == '&') {
// html syntax, no need to pseudolocalize
bool tag_closed = false;
while (!tag_closed && i < I) {
if (c == '&') {
std::string escapeText;
escapeText.append(&c, 1);
std::string escape_text;
escape_text.append(&c, 1);
bool end = false;
size_t htmlCodePos = i;
while (!end && htmlCodePos < I) {
++htmlCodePos;
c = s[htmlCodePos];
escapeText.append(&c, 1);
size_t html_code_pos = i;
while (!end && html_code_pos < I) {
++html_code_pos;
c = s[html_code_pos];
escape_text.append(&c, 1);
// Valid html code
if (c == ';') {
end = true;
i = htmlCodePos;
i = html_code_pos;
}
// Wrong html code
else if (!((c == '#' || (c >= 'a' && c <= 'z') ||
@@ -400,8 +400,8 @@ std::string PseudoMethodAccent::text(const StringPiece& source) {
end = true;
}
}
result += escapeText;
if (escapeText != "&lt;") {
result += escape_text;
if (escape_text != "&lt;") {
tag_closed = true;
}
continue;
@@ -417,30 +417,30 @@ std::string PseudoMethodAccent::text(const StringPiece& source) {
}
} else {
// This is a pure text that should be pseudolocalized
const char* p = pseudolocalizeChar(c);
const char* p = PseudolocalizeChar(c);
if (p != nullptr) {
result += p;
} else {
bool space = isspace(c);
if (lastspace && !space) {
mWordCount++;
word_count_++;
}
lastspace = space;
result.append(&c, 1);
}
// Count only pseudolocalizable chars and delimiters
mLength++;
length_++;
}
}
return result;
}
std::string PseudoMethodAccent::placeholder(const StringPiece& source) {
std::string PseudoMethodAccent::Placeholder(const StringPiece& source) {
// Surround a placeholder with brackets
return k_placeholder_open + source.toString() + k_placeholder_close;
return kPlaceholderOpen + source.ToString() + kPlaceholderClose;
}
std::string PseudoMethodBidi::text(const StringPiece& source) {
std::string PseudoMethodBidi::Text(const StringPiece& source) {
const char* s = source.data();
std::string result;
bool lastspace = true;
@@ -450,24 +450,24 @@ std::string PseudoMethodBidi::text(const StringPiece& source) {
space = isspace(c);
if (lastspace && !space) {
// Word start
result += k_rlm + k_rlo;
result += kRlm + kRlo;
} else if (!lastspace && space) {
// Word end
result += k_pdf + k_rlm;
result += kPdf + kRlm;
}
lastspace = space;
result.append(&c, 1);
}
if (!lastspace) {
// End of last word
result += k_pdf + k_rlm;
result += kPdf + kRlm;
}
return result;
}
std::string PseudoMethodBidi::placeholder(const StringPiece& source) {
std::string PseudoMethodBidi::Placeholder(const StringPiece& source) {
// Surround a placeholder with directionality change sequence
return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm;
return kRlm + kRlo + source.ToString() + kPdf + kRlm;
}
} // namespace aapt

View File

@@ -17,22 +17,23 @@
#ifndef AAPT_COMPILE_PSEUDOLOCALIZE_H
#define AAPT_COMPILE_PSEUDOLOCALIZE_H
#include <memory>
#include "android-base/macros.h"
#include "ResourceValues.h"
#include "StringPool.h"
#include "util/StringPiece.h"
#include <android-base/macros.h>
#include <memory>
namespace aapt {
class PseudoMethodImpl {
public:
virtual ~PseudoMethodImpl() {}
virtual std::string start() { return {}; }
virtual std::string end() { return {}; }
virtual std::string text(const StringPiece& text) = 0;
virtual std::string placeholder(const StringPiece& text) = 0;
virtual std::string Start() { return {}; }
virtual std::string End() { return {}; }
virtual std::string Text(const StringPiece& text) = 0;
virtual std::string Placeholder(const StringPiece& text) = 0;
};
class Pseudolocalizer {
@@ -44,14 +45,14 @@ class Pseudolocalizer {
};
explicit Pseudolocalizer(Method method);
void setMethod(Method method);
std::string start() { return mImpl->start(); }
std::string end() { return mImpl->end(); }
std::string text(const StringPiece& text);
void SetMethod(Method method);
std::string Start() { return impl_->Start(); }
std::string End() { return impl_->End(); }
std::string Text(const StringPiece& text);
private:
std::unique_ptr<PseudoMethodImpl> mImpl;
size_t mLastDepth;
std::unique_ptr<PseudoMethodImpl> impl_;
size_t last_depth_;
};
} // namespace aapt

View File

@@ -15,33 +15,32 @@
*/
#include "compile/Pseudolocalizer.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <gtest/gtest.h>
#include "test/Test.h"
#include "util/Util.h"
namespace aapt {
// In this context, 'Axis' represents a particular field in the configuration,
// such as language or density.
static ::testing::AssertionResult simpleHelper(const char* input,
static ::testing::AssertionResult SimpleHelper(const char* input,
const char* expected,
Pseudolocalizer::Method method) {
Pseudolocalizer pseudo(method);
std::string result = pseudo.start() + pseudo.text(input) + pseudo.end();
std::string result = pseudo.Start() + pseudo.Text(input) + pseudo.End();
if (result != expected) {
return ::testing::AssertionFailure() << expected << " != " << result;
}
return ::testing::AssertionSuccess();
}
static ::testing::AssertionResult compoundHelper(
static ::testing::AssertionResult CompoundHelper(
const char* in1, const char* in2, const char* in3, const char* expected,
Pseudolocalizer::Method method) {
Pseudolocalizer pseudo(method);
std::string result = pseudo.start() + pseudo.text(in1) + pseudo.text(in2) +
pseudo.text(in3) + pseudo.end();
std::string result = pseudo.Start() + pseudo.Text(in1) + pseudo.Text(in2) +
pseudo.Text(in3) + pseudo.End();
if (result != expected) {
return ::testing::AssertionFailure() << expected << " != " << result;
}
@@ -49,49 +48,49 @@ static ::testing::AssertionResult compoundHelper(
}
TEST(PseudolocalizerTest, NoPseudolocalization) {
EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kNone));
EXPECT_TRUE(simpleHelper("Hello, world", "Hello, world",
EXPECT_TRUE(SimpleHelper("", "", Pseudolocalizer::Method::kNone));
EXPECT_TRUE(SimpleHelper("Hello, world", "Hello, world",
Pseudolocalizer::Method::kNone));
EXPECT_TRUE(compoundHelper("Hello,", " world", "", "Hello, world",
EXPECT_TRUE(CompoundHelper("Hello,", " world", "", "Hello, world",
Pseudolocalizer::Method::kNone));
}
TEST(PseudolocalizerTest, PlaintextAccent) {
EXPECT_TRUE(simpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("Hello, world", "[Ĥéļļö, ŵöŕļð one two]",
EXPECT_TRUE(SimpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(SimpleHelper("Hello, world", "[Ĥéļļö, ŵöŕļð one two]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("Hello, %1d", "[Ĥéļļö, »%1d« one two]",
EXPECT_TRUE(SimpleHelper("Hello, %1d", "[Ĥéļļö, »%1d« one two]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("Battery %1d%%", "[βåţţéŕý »%1d«%% one two]",
EXPECT_TRUE(SimpleHelper("Battery %1d%%", "[βåţţéŕý »%1d«%% one two]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(
simpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent));
SimpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(
compoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(compoundHelper("Hello,", " world", "", "[Ĥéļļö, ŵöŕļð one two]",
CompoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(CompoundHelper("Hello,", " world", "", "[Ĥéļļö, ŵöŕļð one two]",
Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, PlaintextBidi) {
EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper("", "", Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(SimpleHelper(
"word", "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
" word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
" word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(
simpleHelper("hello\n world\n",
SimpleHelper("hello\n world\n",
"\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
" \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(compoundHelper(
EXPECT_TRUE(CompoundHelper(
"hello", "\n ", " world\n",
"\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
" \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
@@ -100,33 +99,33 @@ TEST(PseudolocalizerTest, PlaintextBidi) {
TEST(PseudolocalizerTest, SimpleICU) {
// Single-fragment messages
EXPECT_TRUE(simpleHelper("{placeholder}", "[»{placeholder}«]",
EXPECT_TRUE(SimpleHelper("{placeholder}", "[»{placeholder}«]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("{USER} is offline", "[»{USER}« îš öƒƒļîñé one two]",
EXPECT_TRUE(SimpleHelper("{USER} is offline", "[»{USER}« îš öƒƒļîñé one two]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("Copy from {path1} to {path2}",
EXPECT_TRUE(SimpleHelper("Copy from {path1} to {path2}",
"[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper("Today is {1,date} {1,time}",
EXPECT_TRUE(SimpleHelper("Today is {1,date} {1,time}",
"[Ţöðåý îš »{1,date}« »{1,time}« one two]",
Pseudolocalizer::Method::kAccent));
// Multi-fragment messages
EXPECT_TRUE(compoundHelper("{USER}", " ", "is offline",
EXPECT_TRUE(CompoundHelper("{USER}", " ", "is offline",
"[»{USER}« îš öƒƒļîñé one two]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(compoundHelper("Copy from ", "{path1}", " to {path2}",
EXPECT_TRUE(CompoundHelper("Copy from ", "{path1}", " to {path2}",
"[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, ICUBidi) {
// Single-fragment messages
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
"{placeholder}",
"\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
Pseudolocalizer::Method::kBidi));
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
"{COUNT, plural, one {one} other {other}}",
"{COUNT, plural, "
"one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} "
@@ -136,30 +135,30 @@ TEST(PseudolocalizerTest, ICUBidi) {
TEST(PseudolocalizerTest, Escaping) {
// Single-fragment messages
EXPECT_TRUE(simpleHelper("'{USER'} is offline",
EXPECT_TRUE(SimpleHelper("'{USER'} is offline",
"['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
Pseudolocalizer::Method::kAccent));
// Multi-fragment messages
EXPECT_TRUE(compoundHelper("'{USER}", " ", "''is offline",
EXPECT_TRUE(CompoundHelper("'{USER}", " ", "''is offline",
"['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, PluralsAndSelects) {
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
"{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
"[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
"other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(
simpleHelper("Distance is {COUNT, plural, one {# mile} other {# miles}}",
SimpleHelper("Distance is {COUNT, plural, one {# mile} other {# miles}}",
"[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} "
"other {# ḿîļéš one two}}]",
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(simpleHelper(
EXPECT_TRUE(SimpleHelper(
"{1, select, female {{1} added you} "
"male {{1} added you} other {{1} added you}}",
"[{1, select, female {»{1}« åððéð ýöû one two} "
@@ -167,7 +166,7 @@ TEST(PseudolocalizerTest, PluralsAndSelects) {
Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(
compoundHelper("{COUNT, plural, one {Delete a file} "
CompoundHelper("{COUNT, plural, one {Delete a file} "
"other {Delete ",
"{COUNT}", " files}}",
"[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
@@ -177,7 +176,7 @@ TEST(PseudolocalizerTest, PluralsAndSelects) {
TEST(PseudolocalizerTest, NestedICU) {
EXPECT_TRUE(
simpleHelper("{person, select, "
SimpleHelper("{person, select, "
"female {"
"{num_circles, plural,"
"=0{{person} didn't add you to any of her circles.}"
@@ -222,9 +221,9 @@ TEST(PseudolocalizerTest, NestedICU) {
TEST(PseudolocalizerTest, RedefineMethod) {
Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
std::string result = pseudo.text("Hello, ");
pseudo.setMethod(Pseudolocalizer::Method::kNone);
result += pseudo.text("world!");
std::string result = pseudo.Text("Hello, ");
pseudo.SetMethod(Pseudolocalizer::Method::kNone);
result += pseudo.Text("world!");
ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result);
}

View File

@@ -15,55 +15,59 @@
*/
#include "compile/XmlIdCollector.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "xml/XmlDom.h"
#include <algorithm>
#include <vector>
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "xml/XmlDom.h"
namespace aapt {
namespace {
static bool cmpName(const SourcedResourceName& a, const ResourceNameRef& b) {
static bool cmp_name(const SourcedResourceName& a, const ResourceNameRef& b) {
return a.name < b;
}
struct IdCollector : public xml::Visitor {
using xml::Visitor::visit;
public:
using xml::Visitor::Visit;
std::vector<SourcedResourceName>* mOutSymbols;
explicit IdCollector(std::vector<SourcedResourceName>* out_symbols)
: out_symbols_(out_symbols) {}
explicit IdCollector(std::vector<SourcedResourceName>* outSymbols)
: mOutSymbols(outSymbols) {}
void visit(xml::Element* element) override {
void Visit(xml::Element* element) override {
for (xml::Attribute& attr : element->attributes) {
ResourceNameRef name;
bool create = false;
if (ResourceUtils::parseReference(attr.value, &name, &create, nullptr)) {
if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
if (create && name.type == ResourceType::kId) {
auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(),
name, cmpName);
if (iter == mOutSymbols->end() || iter->name != name) {
mOutSymbols->insert(iter, SourcedResourceName{name.toResourceName(),
element->lineNumber});
auto iter = std::lower_bound(out_symbols_->begin(),
out_symbols_->end(), name, cmp_name);
if (iter == out_symbols_->end() || iter->name != name) {
out_symbols_->insert(iter,
SourcedResourceName{name.ToResourceName(),
element->line_number});
}
}
}
}
xml::Visitor::visit(element);
xml::Visitor::Visit(element);
}
private:
std::vector<SourcedResourceName>* out_symbols_;
};
} // namespace
bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) {
xmlRes->file.exportedSymbols.clear();
IdCollector collector(&xmlRes->file.exportedSymbols);
xmlRes->root->accept(&collector);
bool XmlIdCollector::Consume(IAaptContext* context, xml::XmlResource* xmlRes) {
xmlRes->file.exported_symbols.clear();
IdCollector collector(&xmlRes->file.exported_symbols);
xmlRes->root->Accept(&collector);
return true;
}

View File

@@ -23,7 +23,7 @@
namespace aapt {
struct XmlIdCollector : public IXmlResourceConsumer {
bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
bool Consume(IAaptContext* context, xml::XmlResource* xml_res) override;
};
} // namespace aapt

View File

@@ -15,18 +15,17 @@
*/
#include "compile/XmlIdCollector.h"
#include "test/Builders.h"
#include "test/Context.h"
#include <gtest/gtest.h>
#include <algorithm>
#include "test/Test.h"
namespace aapt {
TEST(XmlIdCollectorTest, CollectsIds) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/foo"
text="@+id/bar">
@@ -35,34 +34,34 @@ TEST(XmlIdCollectorTest, CollectsIds) {
</View>)EOF");
XmlIdCollector collector;
ASSERT_TRUE(collector.consume(context.get(), doc.get()));
ASSERT_TRUE(collector.Consume(context.get(), doc.get()));
EXPECT_EQ(
1, std::count(doc->file.exportedSymbols.begin(),
doc->file.exportedSymbols.end(),
SourcedResourceName{test::parseNameOrDie("id/foo"), 3u}));
1, std::count(doc->file.exported_symbols.begin(),
doc->file.exported_symbols.end(),
SourcedResourceName{test::ParseNameOrDie("id/foo"), 3u}));
EXPECT_EQ(
1, std::count(doc->file.exportedSymbols.begin(),
doc->file.exportedSymbols.end(),
SourcedResourceName{test::parseNameOrDie("id/bar"), 3u}));
1, std::count(doc->file.exported_symbols.begin(),
doc->file.exported_symbols.end(),
SourcedResourceName{test::ParseNameOrDie("id/bar"), 3u}));
EXPECT_EQ(
1, std::count(doc->file.exportedSymbols.begin(),
doc->file.exportedSymbols.end(),
SourcedResourceName{test::parseNameOrDie("id/car"), 6u}));
1, std::count(doc->file.exported_symbols.begin(),
doc->file.exported_symbols.end(),
SourcedResourceName{test::ParseNameOrDie("id/car"), 6u}));
}
TEST(XmlIdCollectorTest, DontCollectNonIds) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> doc =
test::buildXmlDom("<View foo=\"@+string/foo\"/>");
test::BuildXmlDom("<View foo=\"@+string/foo\"/>");
XmlIdCollector collector;
ASSERT_TRUE(collector.consume(context.get(), doc.get()));
ASSERT_TRUE(collector.Consume(context.get(), doc.get()));
EXPECT_TRUE(doc->file.exportedSymbols.empty());
EXPECT_TRUE(doc->file.exported_symbols.empty());
}
} // namespace aapt

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include "android-base/macros.h"
#include "Flags.h"
#include "ResourceTable.h"
#include "ValueVisitor.h"
@@ -22,74 +24,72 @@
#include "process/SymbolTable.h"
#include "unflatten/BinaryResourceParser.h"
#include <android-base/macros.h>
namespace aapt {
class DiffContext : public IAaptContext {
public:
const std::string& getCompilationPackage() override { return mEmpty; }
const std::string& GetCompilationPackage() override { return empty_; }
uint8_t getPackageId() override { return 0x0; }
uint8_t GetPackageId() override { return 0x0; }
IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
NameMangler* getNameMangler() override { return &mNameMangler; }
NameMangler* GetNameMangler() override { return &name_mangler_; }
SymbolTable* getExternalSymbols() override { return &mSymbolTable; }
SymbolTable* GetExternalSymbols() override { return &symbol_table_; }
bool verbose() override { return false; }
bool IsVerbose() override { return false; }
int getMinSdkVersion() override { return 0; }
int GetMinSdkVersion() override { return 0; }
private:
std::string mEmpty;
StdErrDiagnostics mDiagnostics;
NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
SymbolTable mSymbolTable;
std::string empty_;
StdErrDiagnostics diagnostics_;
NameMangler name_mangler_ = NameMangler(NameManglerPolicy{});
SymbolTable symbol_table_;
};
class LoadedApk {
public:
LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
std::unique_ptr<ResourceTable> table)
: mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {}
: source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
io::IFileCollection* getFileCollection() { return mApk.get(); }
io::IFileCollection* GetFileCollection() { return apk_.get(); }
ResourceTable* getResourceTable() { return mTable.get(); }
ResourceTable* GetResourceTable() { return table_.get(); }
const Source& getSource() { return mSource; }
const Source& GetSource() { return source_; }
private:
Source mSource;
std::unique_ptr<io::IFileCollection> mApk;
std::unique_ptr<ResourceTable> mTable;
Source source_;
std::unique_ptr<io::IFileCollection> apk_;
std::unique_ptr<ResourceTable> table_;
DISALLOW_COPY_AND_ASSIGN(LoadedApk);
};
static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context,
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
const StringPiece& path) {
Source source(path);
std::string error;
std::unique_ptr<io::ZipFileCollection> apk =
io::ZipFileCollection::create(path, &error);
io::ZipFileCollection::Create(path, &error);
if (!apk) {
context->getDiagnostics()->error(DiagMessage(source) << error);
context->GetDiagnostics()->Error(DiagMessage(source) << error);
return {};
}
io::IFile* file = apk->findFile("resources.arsc");
io::IFile* file = apk->FindFile("resources.arsc");
if (!file) {
context->getDiagnostics()->error(DiagMessage(source)
context->GetDiagnostics()->Error(DiagMessage(source)
<< "no resources.arsc found");
return {};
}
std::unique_ptr<io::IData> data = file->openAsData();
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
context->getDiagnostics()->error(DiagMessage(source)
context->GetDiagnostics()->Error(DiagMessage(source)
<< "could not open resources.arsc");
return {};
}
@@ -97,276 +97,281 @@ static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context,
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
BinaryResourceParser parser(context, table.get(), source, data->data(),
data->size());
if (!parser.parse()) {
if (!parser.Parse()) {
return {};
}
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
static void emitDiffLine(const Source& source, const StringPiece& message) {
static void EmitDiffLine(const Source& source, const StringPiece& message) {
std::cerr << source << ": " << message << "\n";
}
static bool isSymbolVisibilityDifferent(const Symbol& symbolA,
const Symbol& symbolB) {
return symbolA.state != symbolB.state;
static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a,
const Symbol& symbol_b) {
return symbol_a.state != symbol_b.state;
}
template <typename Id>
static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
const Symbol& symbolB, const Maybe<Id>& idB) {
if (symbolA.state == SymbolState::kPublic ||
symbolB.state == SymbolState::kPublic) {
return idA != idB;
static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a,
const Symbol& symbol_b, const Maybe<Id>& id_b) {
if (symbol_a.state == SymbolState::kPublic ||
symbol_b.state == SymbolState::kPublic) {
return id_a != id_b;
}
return false;
}
static bool emitResourceConfigValueDiff(
IAaptContext* context, LoadedApk* apkA, ResourceTablePackage* pkgA,
ResourceTableType* typeA, ResourceEntry* entryA,
ResourceConfigValue* configValueA, LoadedApk* apkB,
ResourceTablePackage* pkgB, ResourceTableType* typeB, ResourceEntry* entryB,
ResourceConfigValue* configValueB) {
Value* valueA = configValueA->value.get();
Value* valueB = configValueB->value.get();
if (!valueA->equals(valueB)) {
std::stringstream strStream;
strStream << "value " << pkgA->name << ":" << typeA->type << "/"
<< entryA->name << " config=" << configValueA->config
<< " does not match:\n";
valueA->print(&strStream);
strStream << "\n vs \n";
valueB->print(&strStream);
emitDiffLine(apkB->getSource(), strStream.str());
static bool EmitResourceConfigValueDiff(
IAaptContext* context, LoadedApk* apk_a, ResourceTablePackage* pkg_a,
ResourceTableType* type_a, ResourceEntry* entry_a,
ResourceConfigValue* config_value_a, LoadedApk* apk_b,
ResourceTablePackage* pkg_b, ResourceTableType* type_b,
ResourceEntry* entry_b, ResourceConfigValue* config_value_b) {
Value* value_a = config_value_a->value.get();
Value* value_b = config_value_b->value.get();
if (!value_a->Equals(value_b)) {
std::stringstream str_stream;
str_stream << "value " << pkg_a->name << ":" << type_a->type << "/"
<< entry_a->name << " config=" << config_value_a->config
<< " does not match:\n";
value_a->Print(&str_stream);
str_stream << "\n vs \n";
value_b->Print(&str_stream);
EmitDiffLine(apk_b->GetSource(), str_stream.str());
return true;
}
return false;
}
static bool emitResourceEntryDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
ResourceTableType* typeA,
ResourceEntry* entryA, LoadedApk* apkB,
ResourceTablePackage* pkgB,
ResourceTableType* typeB,
ResourceEntry* entryB) {
static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
ResourceTablePackage* pkg_a,
ResourceTableType* type_a,
ResourceEntry* entry_a, LoadedApk* apk_b,
ResourceTablePackage* pkg_b,
ResourceTableType* type_b,
ResourceEntry* entry_b) {
bool diff = false;
for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
if (!configValueB) {
std::stringstream strStream;
strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
<< entryA->name << " config=" << configValueA->config;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
ResourceConfigValue* config_value_b =
entry_b->FindValue(config_value_a->config);
if (!config_value_b) {
std::stringstream str_stream;
str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
<< entry_a->name << " config=" << config_value_a->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
configValueA.get(), apkB, pkgB, typeB,
entryB, configValueB);
diff |= EmitResourceConfigValueDiff(
context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(), apk_b,
pkg_b, type_b, entry_b, config_value_b);
}
}
// Check for any newly added config values.
for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
if (!configValueA) {
std::stringstream strStream;
strStream << "new config " << pkgB->name << ":" << typeB->type << "/"
<< entryB->name << " config=" << configValueB->config;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
ResourceConfigValue* config_value_a =
entry_a->FindValue(config_value_b->config);
if (!config_value_a) {
std::stringstream str_stream;
str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/"
<< entry_b->name << " config=" << config_value_b->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
}
return false;
}
static bool emitResourceTypeDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
ResourceTableType* typeA, LoadedApk* apkB,
ResourceTablePackage* pkgB,
ResourceTableType* typeB) {
static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
ResourceTablePackage* pkg_a,
ResourceTableType* type_a, LoadedApk* apk_b,
ResourceTablePackage* pkg_b,
ResourceTableType* type_b) {
bool diff = false;
for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
ResourceEntry* entryB = typeB->findEntry(entryA->name);
if (!entryB) {
std::stringstream strStream;
strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
<< entryA->name;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
if (!entry_b) {
std::stringstream str_stream;
str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
<< entry_a->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
if (isSymbolVisibilityDifferent(entryA->symbolStatus,
entryB->symbolStatus)) {
std::stringstream strStream;
strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
<< " has different visibility (";
if (entryB->symbolStatus.state == SymbolState::kPublic) {
strStream << "PUBLIC";
if (IsSymbolVisibilityDifferent(entry_a->symbol_status,
entry_b->symbol_status)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
<< " has different visibility (";
if (entry_b->symbol_status.state == SymbolState::kPublic) {
str_stream << "PUBLIC";
} else {
strStream << "PRIVATE";
str_stream << "PRIVATE";
}
strStream << " vs ";
if (entryA->symbolStatus.state == SymbolState::kPublic) {
strStream << "PUBLIC";
str_stream << " vs ";
if (entry_a->symbol_status.state == SymbolState::kPublic) {
str_stream << "PUBLIC";
} else {
strStream << "PRIVATE";
str_stream << "PRIVATE";
}
strStream << ")";
emitDiffLine(apkB->getSource(), strStream.str());
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else if (isIdDiff(entryA->symbolStatus, entryA->id,
entryB->symbolStatus, entryB->id)) {
std::stringstream strStream;
strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
<< " has different public ID (";
if (entryB->id) {
strStream << "0x" << std::hex << entryB->id.value();
} else if (IsIdDiff(entry_a->symbol_status, entry_a->id,
entry_b->symbol_status, entry_b->id)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
<< " has different public ID (";
if (entry_b->id) {
str_stream << "0x" << std::hex << entry_b->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << " vs ";
if (entryA->id) {
strStream << "0x " << std::hex << entryA->id.value();
str_stream << " vs ";
if (entry_a->id) {
str_stream << "0x " << std::hex << entry_a->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << ")";
emitDiffLine(apkB->getSource(), strStream.str());
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
apkB, pkgB, typeB, entryB);
diff |=
EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(),
apk_b, pkg_b, type_b, entry_b);
}
}
// Check for any newly added entries.
for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
ResourceEntry* entryA = typeA->findEntry(entryB->name);
if (!entryA) {
std::stringstream strStream;
strStream << "new entry " << pkgB->name << ":" << typeB->type << "/"
<< entryB->name;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
if (!entry_a) {
std::stringstream str_stream;
str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/"
<< entry_b->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
}
return diff;
}
static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA, LoadedApk* apkB,
ResourceTablePackage* pkgB) {
static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
ResourceTablePackage* pkg_a,
LoadedApk* apk_b,
ResourceTablePackage* pkg_b) {
bool diff = false;
for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
ResourceTableType* typeB = pkgB->findType(typeA->type);
if (!typeB) {
std::stringstream strStream;
strStream << "missing " << pkgA->name << ":" << typeA->type;
emitDiffLine(apkA->getSource(), strStream.str());
for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
ResourceTableType* type_b = pkg_b->FindType(type_a->type);
if (!type_b) {
std::stringstream str_stream;
str_stream << "missing " << pkg_a->name << ":" << type_a->type;
EmitDiffLine(apk_a->GetSource(), str_stream.str());
diff = true;
} else {
if (isSymbolVisibilityDifferent(typeA->symbolStatus,
typeB->symbolStatus)) {
std::stringstream strStream;
strStream << pkgA->name << ":" << typeA->type
<< " has different visibility (";
if (typeB->symbolStatus.state == SymbolState::kPublic) {
strStream << "PUBLIC";
if (IsSymbolVisibilityDifferent(type_a->symbol_status,
type_b->symbol_status)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type
<< " has different visibility (";
if (type_b->symbol_status.state == SymbolState::kPublic) {
str_stream << "PUBLIC";
} else {
strStream << "PRIVATE";
str_stream << "PRIVATE";
}
strStream << " vs ";
if (typeA->symbolStatus.state == SymbolState::kPublic) {
strStream << "PUBLIC";
str_stream << " vs ";
if (type_a->symbol_status.state == SymbolState::kPublic) {
str_stream << "PUBLIC";
} else {
strStream << "PRIVATE";
str_stream << "PRIVATE";
}
strStream << ")";
emitDiffLine(apkB->getSource(), strStream.str());
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus,
typeB->id)) {
std::stringstream strStream;
strStream << pkgA->name << ":" << typeA->type
<< " has different public ID (";
if (typeB->id) {
strStream << "0x" << std::hex << typeB->id.value();
} else if (IsIdDiff(type_a->symbol_status, type_a->id,
type_b->symbol_status, type_b->id)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type
<< " has different public ID (";
if (type_b->id) {
str_stream << "0x" << std::hex << type_b->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << " vs ";
if (typeA->id) {
strStream << "0x " << std::hex << typeA->id.value();
str_stream << " vs ";
if (type_a->id) {
str_stream << "0x " << std::hex << type_a->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << ")";
emitDiffLine(apkB->getSource(), strStream.str());
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB,
typeB);
diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b,
pkg_b, type_b);
}
}
// Check for any newly added types.
for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
ResourceTableType* typeA = pkgA->findType(typeB->type);
if (!typeA) {
std::stringstream strStream;
strStream << "new type " << pkgB->name << ":" << typeB->type;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
ResourceTableType* type_a = pkg_a->FindType(type_b->type);
if (!type_a) {
std::stringstream str_stream;
str_stream << "new type " << pkg_b->name << ":" << type_b->type;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
}
return diff;
}
static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA,
LoadedApk* apkB) {
ResourceTable* tableA = apkA->getResourceTable();
ResourceTable* tableB = apkB->getResourceTable();
static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a,
LoadedApk* apk_b) {
ResourceTable* table_a = apk_a->GetResourceTable();
ResourceTable* table_b = apk_b->GetResourceTable();
bool diff = false;
for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
if (!pkgB) {
std::stringstream strStream;
strStream << "missing package " << pkgA->name;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
if (!pkg_b) {
std::stringstream str_stream;
str_stream << "missing package " << pkg_a->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
if (pkgA->id != pkgB->id) {
std::stringstream strStream;
strStream << "package '" << pkgA->name << "' has different id (";
if (pkgB->id) {
strStream << "0x" << std::hex << pkgB->id.value();
if (pkg_a->id != pkg_b->id) {
std::stringstream str_stream;
str_stream << "package '" << pkg_a->name << "' has different id (";
if (pkg_b->id) {
str_stream << "0x" << std::hex << pkg_b->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << " vs ";
if (pkgA->id) {
strStream << "0x" << std::hex << pkgA->id.value();
str_stream << " vs ";
if (pkg_a->id) {
str_stream << "0x" << std::hex << pkg_a->id.value();
} else {
strStream << "none";
str_stream << "none";
}
strStream << ")";
emitDiffLine(apkB->getSource(), strStream.str());
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
diff |=
EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
}
}
// Check for any newly added packages.
for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
if (!pkgA) {
std::stringstream strStream;
strStream << "new package " << pkgB->name;
emitDiffLine(apkB->getSource(), strStream.str());
for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
if (!pkg_a) {
std::stringstream str_stream;
str_stream << "new package " << pkg_b->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
}
@@ -375,49 +380,49 @@ static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA,
class ZeroingReferenceVisitor : public ValueVisitor {
public:
using ValueVisitor::visit;
using ValueVisitor::Visit;
void visit(Reference* ref) override {
void Visit(Reference* ref) override {
if (ref->name && ref->id) {
if (ref->id.value().packageId() == 0x7f) {
if (ref->id.value().package_id() == 0x7f) {
ref->id = {};
}
}
}
};
static void zeroOutAppReferences(ResourceTable* table) {
static void ZeroOutAppReferences(ResourceTable* table) {
ZeroingReferenceVisitor visitor;
visitAllValuesInTable(table, &visitor);
VisitAllValuesInTable(table, &visitor);
}
int diff(const std::vector<StringPiece>& args) {
int Diff(const std::vector<StringPiece>& args) {
DiffContext context;
Flags flags;
if (!flags.parse("aapt2 diff", args, &std::cerr)) {
if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
return 1;
}
if (flags.getArgs().size() != 2u) {
if (flags.GetArgs().size() != 2u) {
std::cerr << "must have two apks as arguments.\n\n";
flags.usage("aapt2 diff", &std::cerr);
flags.Usage("aapt2 diff", &std::cerr);
return 1;
}
std::unique_ptr<LoadedApk> apkA =
loadApkFromPath(&context, flags.getArgs()[0]);
std::unique_ptr<LoadedApk> apkB =
loadApkFromPath(&context, flags.getArgs()[1]);
if (!apkA || !apkB) {
std::unique_ptr<LoadedApk> apk_a =
LoadApkFromPath(&context, flags.GetArgs()[0]);
std::unique_ptr<LoadedApk> apk_b =
LoadApkFromPath(&context, flags.GetArgs()[1]);
if (!apk_a || !apk_b) {
return 1;
}
// Zero out Application IDs in references.
zeroOutAppReferences(apkA->getResourceTable());
zeroOutAppReferences(apkB->getResourceTable());
ZeroOutAppReferences(apk_a->GetResourceTable());
ZeroOutAppReferences(apk_b->GetResourceTable());
if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
// We emitted a diff, so return 1 (failure).
return 1;
}

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <vector>
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
@@ -24,20 +26,14 @@
#include "util/Files.h"
#include "util/StringPiece.h"
#include <vector>
namespace aapt {
// struct DumpOptions {
//
//};
void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data,
void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data,
size_t len, const Source& source, IAaptContext* context) {
std::unique_ptr<ResourceFile> file =
deserializeCompiledFileFromPb(pbFile, source, context->getDiagnostics());
DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
if (!file) {
context->getDiagnostics()->warn(DiagMessage()
context->GetDiagnostics()->Warn(DiagMessage()
<< "failed to read compiled file");
return;
}
@@ -47,50 +43,50 @@ void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data,
<< "Source: " << file->source << "\n";
}
void tryDumpFile(IAaptContext* context, const std::string& filePath) {
void TryDumpFile(IAaptContext* context, const std::string& file_path) {
std::unique_ptr<ResourceTable> table;
std::string err;
std::unique_ptr<io::ZipFileCollection> zip =
io::ZipFileCollection::create(filePath, &err);
io::ZipFileCollection::Create(file_path, &err);
if (zip) {
io::IFile* file = zip->findFile("resources.arsc.flat");
io::IFile* file = zip->FindFile("resources.arsc.flat");
if (file) {
std::unique_ptr<io::IData> data = file->openAsData();
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
context->getDiagnostics()->error(
DiagMessage(filePath) << "failed to open resources.arsc.flat");
context->GetDiagnostics()->Error(
DiagMessage(file_path) << "failed to open resources.arsc.flat");
return;
}
pb::ResourceTable pbTable;
if (!pbTable.ParseFromArray(data->data(), data->size())) {
context->getDiagnostics()->error(DiagMessage(filePath)
pb::ResourceTable pb_table;
if (!pb_table.ParseFromArray(data->data(), data->size())) {
context->GetDiagnostics()->Error(DiagMessage(file_path)
<< "invalid resources.arsc.flat");
return;
}
table = deserializeTableFromPb(pbTable, Source(filePath),
context->getDiagnostics());
table = DeserializeTableFromPb(pb_table, Source(file_path),
context->GetDiagnostics());
if (!table) {
return;
}
}
if (!table) {
file = zip->findFile("resources.arsc");
file = zip->FindFile("resources.arsc");
if (file) {
std::unique_ptr<io::IData> data = file->openAsData();
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
context->getDiagnostics()->error(DiagMessage(filePath)
context->GetDiagnostics()->Error(DiagMessage(file_path)
<< "failed to open resources.arsc");
return;
}
table = util::make_unique<ResourceTable>();
BinaryResourceParser parser(context, table.get(), Source(filePath),
BinaryResourceParser parser(context, table.get(), Source(file_path),
data->data(), data->size());
if (!parser.parse()) {
if (!parser.Parse()) {
return;
}
}
@@ -98,109 +94,109 @@ void tryDumpFile(IAaptContext* context, const std::string& filePath) {
}
if (!table) {
Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
if (!file) {
context->getDiagnostics()->error(DiagMessage(filePath) << err);
context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
return;
}
android::FileMap* fileMap = &file.value();
android::FileMap* file_map = &file.value();
// Try as a compiled table.
pb::ResourceTable pbTable;
if (pbTable.ParseFromArray(fileMap->getDataPtr(),
fileMap->getDataLength())) {
table = deserializeTableFromPb(pbTable, Source(filePath),
context->getDiagnostics());
pb::ResourceTable pb_table;
if (pb_table.ParseFromArray(file_map->getDataPtr(),
file_map->getDataLength())) {
table = DeserializeTableFromPb(pb_table, Source(file_path),
context->GetDiagnostics());
}
if (!table) {
// Try as a compiled file.
CompiledFileInputStream input(fileMap->getDataPtr(),
fileMap->getDataLength());
CompiledFileInputStream input(file_map->getDataPtr(),
file_map->getDataLength());
uint32_t numFiles = 0;
if (!input.ReadLittleEndian32(&numFiles)) {
uint32_t num_files = 0;
if (!input.ReadLittleEndian32(&num_files)) {
return;
}
for (uint32_t i = 0; i < numFiles; i++) {
pb::CompiledFile compiledFile;
if (!input.ReadCompiledFile(&compiledFile)) {
context->getDiagnostics()->warn(DiagMessage()
for (uint32_t i = 0; i < num_files; i++) {
pb::CompiledFile compiled_file;
if (!input.ReadCompiledFile(&compiled_file)) {
context->GetDiagnostics()->Warn(DiagMessage()
<< "failed to read compiled file");
return;
}
uint64_t offset, len;
if (!input.ReadDataMetaData(&offset, &len)) {
context->getDiagnostics()->warn(DiagMessage()
context->GetDiagnostics()->Warn(DiagMessage()
<< "failed to read meta data");
return;
}
const void* data =
static_cast<const uint8_t*>(fileMap->getDataPtr()) + offset;
dumpCompiledFile(compiledFile, data, len, Source(filePath), context);
static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
DumpCompiledFile(compiled_file, data, len, Source(file_path), context);
}
}
}
if (table) {
DebugPrintTableOptions debugPrintTableOptions;
debugPrintTableOptions.showSources = true;
Debug::printTable(table.get(), debugPrintTableOptions);
DebugPrintTableOptions options;
options.show_sources = true;
Debug::PrintTable(table.get(), options);
}
}
class DumpContext : public IAaptContext {
public:
IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
NameMangler* getNameMangler() override {
NameMangler* GetNameMangler() override {
abort();
return nullptr;
}
const std::string& getCompilationPackage() override {
const std::string& GetCompilationPackage() override {
static std::string empty;
return empty;
}
uint8_t getPackageId() override { return 0; }
uint8_t GetPackageId() override { return 0; }
SymbolTable* getExternalSymbols() override {
SymbolTable* GetExternalSymbols() override {
abort();
return nullptr;
}
bool verbose() override { return mVerbose; }
bool IsVerbose() override { return verbose_; }
void setVerbose(bool val) { mVerbose = val; }
void SetVerbose(bool val) { verbose_ = val; }
int getMinSdkVersion() override { return 0; }
int GetMinSdkVersion() override { return 0; }
private:
StdErrDiagnostics mDiagnostics;
bool mVerbose = false;
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
};
/**
* Entry point for dump command.
*/
int dump(const std::vector<StringPiece>& args) {
int Dump(const std::vector<StringPiece>& args) {
bool verbose = false;
Flags flags =
Flags().optionalSwitch("-v", "increase verbosity of output", &verbose);
if (!flags.parse("aapt2 dump", args, &std::cerr)) {
Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose);
if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
return 1;
}
DumpContext context;
context.setVerbose(verbose);
context.SetVerbose(verbose);
for (const std::string& arg : flags.getArgs()) {
tryDumpFile(&context, arg);
for (const std::string& arg : flags.GetArgs()) {
TryDumpFile(&context, arg);
}
return 0;
}

View File

@@ -15,44 +15,45 @@
*/
#include "filter/ConfigFilter.h"
#include "ConfigDescription.h"
#include <androidfw/ResourceTypes.h>
#include "androidfw/ResourceTypes.h"
#include "ConfigDescription.h"
namespace aapt {
void AxisConfigFilter::addConfig(ConfigDescription config) {
uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
void AxisConfigFilter::AddConfig(ConfigDescription config) {
uint32_t diff_mask = ConfigDescription::DefaultConfig().diff(config);
// Ignore the version
diffMask &= ~android::ResTable_config::CONFIG_VERSION;
diff_mask &= ~android::ResTable_config::CONFIG_VERSION;
// Ignore any densities. Those are best handled in --preferred-density
if ((diffMask & android::ResTable_config::CONFIG_DENSITY) != 0) {
if ((diff_mask & android::ResTable_config::CONFIG_DENSITY) != 0) {
config.density = 0;
diffMask &= ~android::ResTable_config::CONFIG_DENSITY;
diff_mask &= ~android::ResTable_config::CONFIG_DENSITY;
}
mConfigs.insert(std::make_pair(config, diffMask));
mConfigMask |= diffMask;
configs_.insert(std::make_pair(config, diff_mask));
config_mask_ |= diff_mask;
}
bool AxisConfigFilter::match(const ConfigDescription& config) const {
const uint32_t mask = ConfigDescription::defaultConfig().diff(config);
if ((mConfigMask & mask) == 0) {
bool AxisConfigFilter::Match(const ConfigDescription& config) const {
const uint32_t mask = ConfigDescription::DefaultConfig().diff(config);
if ((config_mask_ & mask) == 0) {
// The two configurations don't have any common axis.
return true;
}
uint32_t matchedAxis = 0;
for (const auto& entry : mConfigs) {
uint32_t matched_axis = 0;
for (const auto& entry : configs_) {
const ConfigDescription& target = entry.first;
const uint32_t diffMask = entry.second;
const uint32_t diff_mask = entry.second;
uint32_t diff = target.diff(config);
if ((diff & diffMask) == 0) {
if ((diff & diff_mask) == 0) {
// Mark the axis that was matched.
matchedAxis |= diffMask;
} else if ((diff & diffMask) == android::ResTable_config::CONFIG_LOCALE) {
matched_axis |= diff_mask;
} else if ((diff & diff_mask) == android::ResTable_config::CONFIG_LOCALE) {
// If the locales differ, but the languages are the same and
// the locale we are matching only has a language specified,
// we match.
@@ -60,10 +61,10 @@ bool AxisConfigFilter::match(const ConfigDescription& config) const {
memcmp(config.language, target.language, sizeof(config.language)) ==
0) {
if (config.country[0] == 0) {
matchedAxis |= android::ResTable_config::CONFIG_LOCALE;
matched_axis |= android::ResTable_config::CONFIG_LOCALE;
}
}
} else if ((diff & diffMask) ==
} else if ((diff & diff_mask) ==
android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
// Special case if the smallest screen width doesn't match. We check that
// the
@@ -71,11 +72,11 @@ bool AxisConfigFilter::match(const ConfigDescription& config) const {
// specified.
if (config.smallestScreenWidthDp != 0 &&
config.smallestScreenWidthDp < target.smallestScreenWidthDp) {
matchedAxis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
matched_axis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
}
}
}
return matchedAxis == (mConfigMask & mask);
return matched_axis == (config_mask_ & mask);
}
} // namespace aapt

View File

@@ -17,11 +17,11 @@
#ifndef AAPT_FILTER_CONFIGFILTER_H
#define AAPT_FILTER_CONFIGFILTER_H
#include "ConfigDescription.h"
#include <set>
#include <utility>
#include "ConfigDescription.h"
namespace aapt {
/**
@@ -34,7 +34,7 @@ class IConfigFilter {
/**
* Returns true if the filter matches the configuration, false otherwise.
*/
virtual bool match(const ConfigDescription& config) const = 0;
virtual bool Match(const ConfigDescription& config) const = 0;
};
/**
@@ -50,13 +50,13 @@ class IConfigFilter {
*/
class AxisConfigFilter : public IConfigFilter {
public:
void addConfig(ConfigDescription config);
void AddConfig(ConfigDescription config);
bool match(const ConfigDescription& config) const override;
bool Match(const ConfigDescription& config) const override;
private:
std::set<std::pair<ConfigDescription, uint32_t>> mConfigs;
uint32_t mConfigMask = 0;
std::set<std::pair<ConfigDescription, uint32_t>> configs_;
uint32_t config_mask_ = 0;
};
} // namespace aapt

View File

@@ -15,99 +15,98 @@
*/
#include "filter/ConfigFilter.h"
#include "test/Common.h"
#include <gtest/gtest.h>
#include "test/Test.h"
namespace aapt {
TEST(ConfigFilterTest, EmptyFilterMatchesAnything) {
AxisConfigFilter filter;
EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("320dpi")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithUnrelatedAxis) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr"));
filter.AddConfig(test::ParseConfigOrDie("fr"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxis) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr"));
filter.AddConfig(test::ParseConfigOrDie("fr"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr"));
filter.AddConfig(test::ParseConfigOrDie("fr"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-320dpi")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr-320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithOneMatchingAxis) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr-rFR"));
filter.addConfig(test::parseConfigOrDie("sw360dp"));
filter.addConfig(test::parseConfigOrDie("normal"));
filter.addConfig(test::parseConfigOrDie("en-rUS"));
filter.AddConfig(test::ParseConfigOrDie("fr-rFR"));
filter.AddConfig(test::ParseConfigOrDie("sw360dp"));
filter.AddConfig(test::ParseConfigOrDie("normal"));
filter.AddConfig(test::ParseConfigOrDie("en-rUS"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("en")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("en")));
}
TEST(ConfigFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr"));
filter.AddConfig(test::ParseConfigOrDie("fr"));
EXPECT_FALSE(filter.match(test::parseConfigOrDie("de")));
EXPECT_FALSE(filter.Match(test::ParseConfigOrDie("de")));
}
TEST(ConfigFilterTest, DoesNotMatchWhenOneQualifierIsExplicitlyNotMatched) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("fr-rFR"));
filter.addConfig(test::parseConfigOrDie("en-rUS"));
filter.addConfig(test::parseConfigOrDie("normal"));
filter.addConfig(test::parseConfigOrDie("large"));
filter.addConfig(test::parseConfigOrDie("xxhdpi"));
filter.addConfig(test::parseConfigOrDie("sw320dp"));
filter.AddConfig(test::ParseConfigOrDie("fr-rFR"));
filter.AddConfig(test::ParseConfigOrDie("en-rUS"));
filter.AddConfig(test::ParseConfigOrDie("normal"));
filter.AddConfig(test::ParseConfigOrDie("large"));
filter.AddConfig(test::ParseConfigOrDie("xxhdpi"));
filter.AddConfig(test::ParseConfigOrDie("sw320dp"));
EXPECT_FALSE(filter.match(test::parseConfigOrDie("fr-sw600dp-v13")));
EXPECT_FALSE(filter.Match(test::ParseConfigOrDie("fr-sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesSmallestWidthWhenSmaller) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("sw600dp"));
filter.AddConfig(test::ParseConfigOrDie("sw600dp"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-sw320dp-v13")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr-sw320dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("de-rDE"));
filter.AddConfig(test::ParseConfigOrDie("de-rDE"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("de")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("de")));
}
TEST(ConfigFilterTest, IgnoresVersion) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("normal-v4"));
filter.AddConfig(test::ParseConfigOrDie("normal-v4"));
// The configs don't match on any axis besides version, which should be
// ignored.
EXPECT_TRUE(filter.match(test::parseConfigOrDie("sw600dp-v13")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithRegion) {
AxisConfigFilter filter;
filter.addConfig(test::parseConfigOrDie("kok"));
filter.addConfig(test::parseConfigOrDie("kok-rIN"));
filter.addConfig(test::parseConfigOrDie("kok-v419"));
filter.AddConfig(test::ParseConfigOrDie("kok"));
filter.AddConfig(test::ParseConfigOrDie("kok-rIN"));
filter.AddConfig(test::ParseConfigOrDie("kok-v419"));
EXPECT_TRUE(filter.match(test::parseConfigOrDie("kok-rIN")));
EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("kok-rIN")));
}
} // namespace aapt

View File

@@ -15,131 +15,139 @@
*/
#include "flatten/Archive.h"
#include "util/Files.h"
#include "util/StringPiece.h"
#include <ziparchive/zip_writer.h>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "android-base/macros.h"
#include "ziparchive/zip_writer.h"
#include "util/Files.h"
#include "util/StringPiece.h"
namespace aapt {
namespace {
struct DirectoryWriter : public IArchiveWriter {
std::string mOutDir;
std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
class DirectoryWriter : public IArchiveWriter {
public:
DirectoryWriter() = default;
bool open(IDiagnostics* diag, const StringPiece& outDir) {
mOutDir = outDir.toString();
file::FileType type = file::getFileType(mOutDir);
bool Open(IDiagnostics* diag, const StringPiece& out_dir) {
dir_ = out_dir.ToString();
file::FileType type = file::GetFileType(dir_);
if (type == file::FileType::kNonexistant) {
diag->error(DiagMessage() << "directory " << mOutDir
<< " does not exist");
diag->Error(DiagMessage() << "directory " << dir_ << " does not exist");
return false;
} else if (type != file::FileType::kDirectory) {
diag->error(DiagMessage() << mOutDir << " is not a directory");
diag->Error(DiagMessage() << dir_ << " is not a directory");
return false;
}
return true;
}
bool startEntry(const StringPiece& path, uint32_t flags) override {
if (mFile) {
bool StartEntry(const StringPiece& path, uint32_t flags) override {
if (file_) {
return false;
}
std::string fullPath = mOutDir;
file::appendPath(&fullPath, path);
file::mkdirs(file::getStem(fullPath));
std::string full_path = dir_;
file::AppendPath(&full_path, path);
file::mkdirs(file::GetStem(full_path));
mFile = {fopen(fullPath.data(), "wb"), fclose};
if (!mFile) {
file_ = {fopen(full_path.data(), "wb"), fclose};
if (!file_) {
return false;
}
return true;
}
bool writeEntry(const BigBuffer& buffer) override {
if (!mFile) {
bool WriteEntry(const BigBuffer& buffer) override {
if (!file_) {
return false;
}
for (const BigBuffer::Block& b : buffer) {
if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
mFile.reset(nullptr);
if (fwrite(b.buffer.get(), 1, b.size, file_.get()) != b.size) {
file_.reset(nullptr);
return false;
}
}
return true;
}
bool writeEntry(const void* data, size_t len) override {
if (fwrite(data, 1, len, mFile.get()) != len) {
mFile.reset(nullptr);
bool WriteEntry(const void* data, size_t len) override {
if (fwrite(data, 1, len, file_.get()) != len) {
file_.reset(nullptr);
return false;
}
return true;
}
bool finishEntry() override {
if (!mFile) {
bool FinishEntry() override {
if (!file_) {
return false;
}
mFile.reset(nullptr);
file_.reset(nullptr);
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
std::string dir_;
std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
};
struct ZipFileWriter : public IArchiveWriter {
std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
std::unique_ptr<ZipWriter> mWriter;
class ZipFileWriter : public IArchiveWriter {
public:
ZipFileWriter() = default;
bool open(IDiagnostics* diag, const StringPiece& path) {
mFile = {fopen(path.data(), "w+b"), fclose};
if (!mFile) {
diag->error(DiagMessage() << "failed to open " << path << ": "
bool Open(IDiagnostics* diag, const StringPiece& path) {
file_ = {fopen(path.data(), "w+b"), fclose};
if (!file_) {
diag->Error(DiagMessage() << "failed to Open " << path << ": "
<< strerror(errno));
return false;
}
mWriter = util::make_unique<ZipWriter>(mFile.get());
writer_ = util::make_unique<ZipWriter>(file_.get());
return true;
}
bool startEntry(const StringPiece& path, uint32_t flags) override {
if (!mWriter) {
bool StartEntry(const StringPiece& path, uint32_t flags) override {
if (!writer_) {
return false;
}
size_t zipFlags = 0;
size_t zip_flags = 0;
if (flags & ArchiveEntry::kCompress) {
zipFlags |= ZipWriter::kCompress;
zip_flags |= ZipWriter::kCompress;
}
if (flags & ArchiveEntry::kAlign) {
zipFlags |= ZipWriter::kAlign32;
zip_flags |= ZipWriter::kAlign32;
}
int32_t result = mWriter->StartEntry(path.data(), zipFlags);
int32_t result = writer_->StartEntry(path.data(), zip_flags);
if (result != 0) {
return false;
}
return true;
}
bool writeEntry(const void* data, size_t len) override {
int32_t result = mWriter->WriteBytes(data, len);
bool WriteEntry(const void* data, size_t len) override {
int32_t result = writer_->WriteBytes(data, len);
if (result != 0) {
return false;
}
return true;
}
bool writeEntry(const BigBuffer& buffer) override {
bool WriteEntry(const BigBuffer& buffer) override {
for (const BigBuffer::Block& b : buffer) {
int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
int32_t result = writer_->WriteBytes(b.buffer.get(), b.size);
if (result != 0) {
return false;
}
@@ -147,8 +155,8 @@ struct ZipFileWriter : public IArchiveWriter {
return true;
}
bool finishEntry() override {
int32_t result = mWriter->FinishEntry();
bool FinishEntry() override {
int32_t result = writer_->FinishEntry();
if (result != 0) {
return false;
}
@@ -156,28 +164,34 @@ struct ZipFileWriter : public IArchiveWriter {
}
virtual ~ZipFileWriter() {
if (mWriter) {
mWriter->Finish();
if (writer_) {
writer_->Finish();
}
}
private:
DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
std::unique_ptr<ZipWriter> writer_;
};
} // namespace
std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(
std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
IDiagnostics* diag, const StringPiece& path) {
std::unique_ptr<DirectoryWriter> writer =
util::make_unique<DirectoryWriter>();
if (!writer->open(diag, path)) {
if (!writer->Open(diag, path)) {
return {};
}
return std::move(writer);
}
std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(
std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
IDiagnostics* diag, const StringPiece& path) {
std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
if (!writer->open(diag, path)) {
if (!writer->Open(diag, path)) {
return {};
}
return std::move(writer);

View File

@@ -17,17 +17,18 @@
#ifndef AAPT_FLATTEN_ARCHIVE_H
#define AAPT_FLATTEN_ARCHIVE_H
#include "Diagnostics.h"
#include "util/BigBuffer.h"
#include "util/Files.h"
#include "util/StringPiece.h"
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "Diagnostics.h"
#include "util/BigBuffer.h"
#include "util/Files.h"
#include "util/StringPiece.h"
namespace aapt {
struct ArchiveEntry {
@@ -38,28 +39,28 @@ struct ArchiveEntry {
std::string path;
uint32_t flags;
size_t uncompressedSize;
size_t uncompressed_size;
};
class IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
public:
virtual ~IArchiveWriter() = default;
virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
virtual bool writeEntry(const BigBuffer& buffer) = 0;
virtual bool writeEntry(const void* data, size_t len) = 0;
virtual bool finishEntry() = 0;
virtual bool StartEntry(const StringPiece& path, uint32_t flags) = 0;
virtual bool WriteEntry(const BigBuffer& buffer) = 0;
virtual bool WriteEntry(const void* data, size_t len) = 0;
virtual bool FinishEntry() = 0;
// CopyingOutputStream implementations.
bool Write(const void* buffer, int size) override {
return writeEntry(buffer, size);
return WriteEntry(buffer, size);
}
};
std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(
std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
IDiagnostics* diag, const StringPiece& path);
std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(
std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
IDiagnostics* diag, const StringPiece& path);
} // namespace aapt

View File

@@ -17,62 +17,62 @@
#ifndef AAPT_FLATTEN_CHUNKWRITER_H
#define AAPT_FLATTEN_CHUNKWRITER_H
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
#include "util/BigBuffer.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
class ChunkWriter {
private:
BigBuffer* mBuffer;
size_t mStartSize = 0;
android::ResChunk_header* mHeader = nullptr;
public:
explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {}
ChunkWriter(const ChunkWriter&) = delete;
ChunkWriter& operator=(const ChunkWriter&) = delete;
explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {}
ChunkWriter(ChunkWriter&&) = default;
ChunkWriter& operator=(ChunkWriter&&) = default;
template <typename T>
inline T* startChunk(uint16_t type) {
mStartSize = mBuffer->size();
T* chunk = mBuffer->nextBlock<T>();
mHeader = &chunk->header;
mHeader->type = util::hostToDevice16(type);
mHeader->headerSize = util::hostToDevice16(sizeof(T));
inline T* StartChunk(uint16_t type) {
start_size_ = buffer_->size();
T* chunk = buffer_->NextBlock<T>();
header_ = &chunk->header;
header_->type = util::HostToDevice16(type);
header_->headerSize = util::HostToDevice16(sizeof(T));
return chunk;
}
template <typename T>
inline T* nextBlock(size_t count = 1) {
return mBuffer->nextBlock<T>(count);
inline T* NextBlock(size_t count = 1) {
return buffer_->NextBlock<T>(count);
}
inline BigBuffer* getBuffer() { return mBuffer; }
inline BigBuffer* buffer() { return buffer_; }
inline android::ResChunk_header* getChunkHeader() { return mHeader; }
inline android::ResChunk_header* chunk_header() { return header_; }
inline size_t size() { return mBuffer->size() - mStartSize; }
inline size_t size() { return buffer_->size() - start_size_; }
inline android::ResChunk_header* finish() {
mBuffer->align4();
mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
return mHeader;
inline android::ResChunk_header* Finish() {
buffer_->Align4();
header_->size = util::HostToDevice32(buffer_->size() - start_size_);
return header_;
}
private:
DISALLOW_COPY_AND_ASSIGN(ChunkWriter);
BigBuffer* buffer_;
size_t start_size_ = 0;
android::ResChunk_header* header_ = nullptr;
};
template <>
inline android::ResChunk_header* ChunkWriter::startChunk(uint16_t type) {
mStartSize = mBuffer->size();
mHeader = mBuffer->nextBlock<android::ResChunk_header>();
mHeader->type = util::hostToDevice16(type);
mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
return mHeader;
inline android::ResChunk_header* ChunkWriter::StartChunk(uint16_t type) {
start_size_ = buffer_->size();
header_ = buffer_->NextBlock<android::ResChunk_header>();
header_->type = util::HostToDevice16(type);
header_->headerSize = util::HostToDevice16(sizeof(android::ResChunk_header));
return header_;
}
} // namespace aapt

View File

@@ -17,7 +17,7 @@
#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
#include <androidfw/ResourceTypes.h>
#include "androidfw/ResourceTypes.h"
namespace aapt {

View File

@@ -14,21 +14,23 @@
* limitations under the License.
*/
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
#include "flatten/TableFlattener.h"
#include "util/BigBuffer.h"
#include <android-base/macros.h>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <type_traits>
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
#include "util/BigBuffer.h"
using namespace android;
namespace aapt {
@@ -36,7 +38,7 @@ namespace aapt {
namespace {
template <typename T>
static bool cmpIds(const T* a, const T* b) {
static bool cmp_ids(const T* a, const T* b) {
return a->id.value() < b->id.value();
}
@@ -46,14 +48,14 @@ static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) {
}
size_t i;
const char16_t* srcData = src.data();
const char16_t* src_data = src.data();
for (i = 0; i < len - 1 && i < src.size(); i++) {
dst[i] = util::hostToDevice16((uint16_t)srcData[i]);
dst[i] = util::HostToDevice16((uint16_t)src_data[i]);
}
dst[i] = 0;
}
static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
if (a.key.id) {
if (b.key.id) {
return a.key.id.value() < b.key.id.value();
@@ -70,75 +72,75 @@ struct FlatEntry {
Value* value;
// The entry string pool index to the entry's name.
uint32_t entryKey;
uint32_t entry_key;
};
class MapFlattenVisitor : public RawValueVisitor {
public:
using RawValueVisitor::visit;
using RawValueVisitor::Visit;
MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer)
: mOutEntry(outEntry), mBuffer(buffer) {}
MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
: out_entry_(out_entry), buffer_(buffer) {}
void visit(Attribute* attr) override {
void Visit(Attribute* attr) override {
{
Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
flattenEntry(&key, &val);
BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->type_mask);
FlattenEntry(&key, &val);
}
if (attr->minInt != std::numeric_limits<int32_t>::min()) {
if (attr->min_int != std::numeric_limits<int32_t>::min()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
BinaryPrimitive val(Res_value::TYPE_INT_DEC,
static_cast<uint32_t>(attr->minInt));
flattenEntry(&key, &val);
static_cast<uint32_t>(attr->min_int));
FlattenEntry(&key, &val);
}
if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
if (attr->max_int != std::numeric_limits<int32_t>::max()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
BinaryPrimitive val(Res_value::TYPE_INT_DEC,
static_cast<uint32_t>(attr->maxInt));
flattenEntry(&key, &val);
static_cast<uint32_t>(attr->max_int));
FlattenEntry(&key, &val);
}
for (Attribute::Symbol& s : attr->symbols) {
BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
flattenEntry(&s.symbol, &val);
FlattenEntry(&s.symbol, &val);
}
}
void visit(Style* style) override {
void Visit(Style* style) override {
if (style->parent) {
const Reference& parentRef = style->parent.value();
assert(parentRef.id && "parent has no ID");
mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
const Reference& parent_ref = style->parent.value();
CHECK(bool(parent_ref.id)) << "parent has no ID";
out_entry_->parent.ident = util::HostToDevice32(parent_ref.id.value().id);
}
// Sort the style.
std::sort(style->entries.begin(), style->entries.end(), cmpStyleEntries);
std::sort(style->entries.begin(), style->entries.end(), cmp_style_entries);
for (Style::Entry& entry : style->entries) {
flattenEntry(&entry.key, entry.value.get());
FlattenEntry(&entry.key, entry.value.get());
}
}
void visit(Styleable* styleable) override {
for (auto& attrRef : styleable->entries) {
void Visit(Styleable* styleable) override {
for (auto& attr_ref : styleable->entries) {
BinaryPrimitive val(Res_value{});
flattenEntry(&attrRef, &val);
FlattenEntry(&attr_ref, &val);
}
}
void visit(Array* array) override {
void Visit(Array* array) override {
for (auto& item : array->items) {
ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
flattenValue(item.get(), outEntry);
outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
mEntryCount++;
ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
FlattenValue(item.get(), out_entry);
out_entry->value.size = util::HostToDevice16(sizeof(out_entry->value));
entry_count_++;
}
}
void visit(Plural* plural) override {
void Visit(Plural* plural) override {
const size_t count = plural->values.size();
for (size_t i = 0; i < count; i++) {
if (!plural->values[i]) {
@@ -172,12 +174,12 @@ class MapFlattenVisitor : public RawValueVisitor {
break;
default:
assert(false);
LOG(FATAL) << "unhandled plural type";
break;
}
Reference key(q);
flattenEntry(&key, plural->values[i].get());
FlattenEntry(&key, plural->values[i].get());
}
}
@@ -185,267 +187,264 @@ class MapFlattenVisitor : public RawValueVisitor {
* Call this after visiting a Value. This will finish any work that
* needs to be done to prepare the entry.
*/
void finish() { mOutEntry->count = util::hostToDevice32(mEntryCount); }
void Finish() { out_entry_->count = util::HostToDevice32(entry_count_); }
private:
void flattenKey(Reference* key, ResTable_map* outEntry) {
assert(key->id && "key has no ID");
outEntry->name.ident = util::hostToDevice32(key->id.value().id);
DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
void FlattenKey(Reference* key, ResTable_map* out_entry) {
CHECK(bool(key->id)) << "key has no ID";
out_entry->name.ident = util::HostToDevice32(key->id.value().id);
}
void flattenValue(Item* value, ResTable_map* outEntry) {
bool result = value->flatten(&outEntry->value);
assert(result && "flatten failed");
void FlattenValue(Item* value, ResTable_map* out_entry) {
CHECK(value->Flatten(&out_entry->value)) << "flatten failed";
}
void flattenEntry(Reference* key, Item* value) {
ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
flattenKey(key, outEntry);
flattenValue(value, outEntry);
outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
mEntryCount++;
void FlattenEntry(Reference* key, Item* value) {
ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
FlattenKey(key, out_entry);
FlattenValue(value, out_entry);
out_entry->value.size = util::HostToDevice16(sizeof(out_entry->value));
entry_count_++;
}
ResTable_entry_ext* mOutEntry;
BigBuffer* mBuffer;
size_t mEntryCount = 0;
ResTable_entry_ext* out_entry_;
BigBuffer* buffer_;
size_t entry_count_ = 0;
};
class PackageFlattener {
public:
PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package)
: mDiag(diag), mPackage(package) {}
: diag_(diag), package_(package) {}
bool flattenPackage(BigBuffer* buffer) {
ChunkWriter pkgWriter(buffer);
ResTable_package* pkgHeader =
pkgWriter.startChunk<ResTable_package>(RES_TABLE_PACKAGE_TYPE);
pkgHeader->id = util::hostToDevice32(mPackage->id.value());
bool FlattenPackage(BigBuffer* buffer) {
ChunkWriter pkg_writer(buffer);
ResTable_package* pkg_header =
pkg_writer.StartChunk<ResTable_package>(RES_TABLE_PACKAGE_TYPE);
pkg_header->id = util::HostToDevice32(package_->id.value());
if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
mDiag->error(DiagMessage() << "package name '" << mPackage->name
if (package_->name.size() >= arraysize(pkg_header->name)) {
diag_->Error(DiagMessage() << "package name '" << package_->name
<< "' is too long");
return false;
}
// Copy the package name in device endianness.
strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name),
util::utf8ToUtf16(mPackage->name));
strcpy16_htod(pkg_header->name, arraysize(pkg_header->name),
util::Utf8ToUtf16(package_->name));
// Serialize the types. We do this now so that our type and key strings
// are populated. We write those first.
BigBuffer typeBuffer(1024);
flattenTypes(&typeBuffer);
BigBuffer type_buffer(1024);
FlattenTypes(&type_buffer);
pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
pkg_header->typeStrings = util::HostToDevice32(pkg_writer.size());
StringPool::FlattenUtf16(pkg_writer.buffer(), type_pool_);
pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
pkg_header->keyStrings = util::HostToDevice32(pkg_writer.size());
StringPool::FlattenUtf8(pkg_writer.buffer(), key_pool_);
// Append the types.
buffer->appendBuffer(std::move(typeBuffer));
buffer->AppendBuffer(std::move(type_buffer));
pkgWriter.finish();
pkg_writer.Finish();
return true;
}
private:
IDiagnostics* mDiag;
ResourceTablePackage* mPackage;
StringPool mTypePool;
StringPool mKeyPool;
DISALLOW_COPY_AND_ASSIGN(PackageFlattener);
template <typename T, bool IsItem>
T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) {
static_assert(std::is_same<ResTable_entry, T>::value ||
std::is_same<ResTable_entry_ext, T>::value,
"T must be ResTable_entry or ResTable_entry_ext");
T* result = buffer->nextBlock<T>();
ResTable_entry* outEntry = (ResTable_entry*)(result);
if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
outEntry->flags |= ResTable_entry::FLAG_PUBLIC;
T* result = buffer->NextBlock<T>();
ResTable_entry* out_entry = (ResTable_entry*)result;
if (entry->entry->symbol_status.state == SymbolState::kPublic) {
out_entry->flags |= ResTable_entry::FLAG_PUBLIC;
}
if (entry->value->isWeak()) {
outEntry->flags |= ResTable_entry::FLAG_WEAK;
if (entry->value->IsWeak()) {
out_entry->flags |= ResTable_entry::FLAG_WEAK;
}
if (!IsItem) {
outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
out_entry->flags |= ResTable_entry::FLAG_COMPLEX;
}
outEntry->flags = util::hostToDevice16(outEntry->flags);
outEntry->key.index = util::hostToDevice32(entry->entryKey);
outEntry->size = util::hostToDevice16(sizeof(T));
out_entry->flags = util::HostToDevice16(out_entry->flags);
out_entry->key.index = util::HostToDevice32(entry->entry_key);
out_entry->size = util::HostToDevice16(sizeof(T));
return result;
}
bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
if (Item* item = valueCast<Item>(entry->value)) {
writeEntry<ResTable_entry, true>(entry, buffer);
Res_value* outValue = buffer->nextBlock<Res_value>();
bool result = item->flatten(outValue);
assert(result && "flatten failed");
outValue->size = util::hostToDevice16(sizeof(*outValue));
bool FlattenValue(FlatEntry* entry, BigBuffer* buffer) {
if (Item* item = ValueCast<Item>(entry->value)) {
WriteEntry<ResTable_entry, true>(entry, buffer);
Res_value* outValue = buffer->NextBlock<Res_value>();
CHECK(item->Flatten(outValue)) << "flatten failed";
outValue->size = util::HostToDevice16(sizeof(*outValue));
} else {
ResTable_entry_ext* outEntry =
writeEntry<ResTable_entry_ext, false>(entry, buffer);
MapFlattenVisitor visitor(outEntry, buffer);
entry->value->accept(&visitor);
visitor.finish();
ResTable_entry_ext* out_entry =
WriteEntry<ResTable_entry_ext, false>(entry, buffer);
MapFlattenVisitor visitor(out_entry, buffer);
entry->value->Accept(&visitor);
visitor.Finish();
}
return true;
}
bool flattenConfig(const ResourceTableType* type,
bool FlattenConfig(const ResourceTableType* type,
const ConfigDescription& config,
std::vector<FlatEntry>* entries, BigBuffer* buffer) {
ChunkWriter typeWriter(buffer);
ResTable_type* typeHeader =
typeWriter.startChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
typeHeader->id = type->id.value();
typeHeader->config = config;
typeHeader->config.swapHtoD();
ChunkWriter type_writer(buffer);
ResTable_type* type_header =
type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
type_header->id = type->id.value();
type_header->config = config;
type_header->config.swapHtoD();
auto maxAccum = [](uint32_t max,
const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
auto max_accum = [](uint32_t max,
const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
return std::max(max, (uint32_t)a->id.value());
};
// Find the largest entry ID. That is how many entries we will have.
const uint32_t entryCount =
const uint32_t entry_count =
std::accumulate(type->entries.begin(), type->entries.end(), 0,
maxAccum) +
max_accum) +
1;
typeHeader->entryCount = util::hostToDevice32(entryCount);
uint32_t* indices = typeWriter.nextBlock<uint32_t>(entryCount);
type_header->entryCount = util::HostToDevice32(entry_count);
uint32_t* indices = type_writer.NextBlock<uint32_t>(entry_count);
assert((size_t)entryCount <= std::numeric_limits<uint16_t>::max() + 1);
memset(indices, 0xff, entryCount * sizeof(uint32_t));
CHECK((size_t)entry_count <= std::numeric_limits<uint16_t>::max());
memset(indices, 0xff, entry_count * sizeof(uint32_t));
typeHeader->entriesStart = util::hostToDevice32(typeWriter.size());
type_header->entriesStart = util::HostToDevice32(type_writer.size());
const size_t entryStart = typeWriter.getBuffer()->size();
for (FlatEntry& flatEntry : *entries) {
assert(flatEntry.entry->id.value() < entryCount);
indices[flatEntry.entry->id.value()] =
util::hostToDevice32(typeWriter.getBuffer()->size() - entryStart);
if (!flattenValue(&flatEntry, typeWriter.getBuffer())) {
mDiag->error(DiagMessage()
const size_t entry_start = type_writer.buffer()->size();
for (FlatEntry& flat_entry : *entries) {
CHECK(flat_entry.entry->id.value() < entry_count);
indices[flat_entry.entry->id.value()] =
util::HostToDevice32(type_writer.buffer()->size() - entry_start);
if (!FlattenValue(&flat_entry, type_writer.buffer())) {
diag_->Error(DiagMessage()
<< "failed to flatten resource '"
<< ResourceNameRef(mPackage->name, type->type,
flatEntry.entry->name)
<< ResourceNameRef(package_->name, type->type,
flat_entry.entry->name)
<< "' for configuration '" << config << "'");
return false;
}
}
typeWriter.finish();
type_writer.Finish();
return true;
}
std::vector<ResourceTableType*> collectAndSortTypes() {
std::vector<ResourceTableType*> sortedTypes;
for (auto& type : mPackage->types) {
std::vector<ResourceTableType*> CollectAndSortTypes() {
std::vector<ResourceTableType*> sorted_types;
for (auto& type : package_->types) {
if (type->type == ResourceType::kStyleable) {
// Styleables aren't real Resource Types, they are represented in the
// R.java
// file.
// R.java file.
continue;
}
assert(type->id && "type must have an ID set");
CHECK(bool(type->id)) << "type must have an ID set";
sortedTypes.push_back(type.get());
sorted_types.push_back(type.get());
}
std::sort(sortedTypes.begin(), sortedTypes.end(),
cmpIds<ResourceTableType>);
return sortedTypes;
std::sort(sorted_types.begin(), sorted_types.end(),
cmp_ids<ResourceTableType>);
return sorted_types;
}
std::vector<ResourceEntry*> collectAndSortEntries(ResourceTableType* type) {
std::vector<ResourceEntry*> CollectAndSortEntries(ResourceTableType* type) {
// Sort the entries by entry ID.
std::vector<ResourceEntry*> sortedEntries;
std::vector<ResourceEntry*> sorted_entries;
for (auto& entry : type->entries) {
assert(entry->id && "entry must have an ID set");
sortedEntries.push_back(entry.get());
CHECK(bool(entry->id)) << "entry must have an ID set";
sorted_entries.push_back(entry.get());
}
std::sort(sortedEntries.begin(), sortedEntries.end(),
cmpIds<ResourceEntry>);
return sortedEntries;
std::sort(sorted_entries.begin(), sorted_entries.end(),
cmp_ids<ResourceEntry>);
return sorted_entries;
}
bool flattenTypeSpec(ResourceTableType* type,
std::vector<ResourceEntry*>* sortedEntries,
bool FlattenTypeSpec(ResourceTableType* type,
std::vector<ResourceEntry*>* sorted_entries,
BigBuffer* buffer) {
ChunkWriter typeSpecWriter(buffer);
ResTable_typeSpec* specHeader =
typeSpecWriter.startChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
specHeader->id = type->id.value();
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
type_spec_writer.StartChunk<ResTable_typeSpec>(
RES_TABLE_TYPE_SPEC_TYPE);
spec_header->id = type->id.value();
if (sortedEntries->empty()) {
typeSpecWriter.finish();
if (sorted_entries->empty()) {
type_spec_writer.Finish();
return true;
}
// We can't just take the size of the vector. There may be holes in the
// entry ID space.
// Since the entries are sorted by ID, the last one will be the biggest.
const size_t numEntries = sortedEntries->back()->id.value() + 1;
const size_t num_entries = sorted_entries->back()->id.value() + 1;
specHeader->entryCount = util::hostToDevice32(numEntries);
spec_header->entryCount = util::HostToDevice32(num_entries);
// Reserve space for the masks of each resource in this type. These
// show for which configuration axis the resource changes.
uint32_t* configMasks = typeSpecWriter.nextBlock<uint32_t>(numEntries);
uint32_t* config_masks = type_spec_writer.NextBlock<uint32_t>(num_entries);
const size_t actualNumEntries = sortedEntries->size();
for (size_t entryIndex = 0; entryIndex < actualNumEntries; entryIndex++) {
ResourceEntry* entry = sortedEntries->at(entryIndex);
const size_t actual_num_entries = sorted_entries->size();
for (size_t entryIndex = 0; entryIndex < actual_num_entries; entryIndex++) {
ResourceEntry* entry = sorted_entries->at(entryIndex);
// Populate the config masks for this entry.
if (entry->symbolStatus.state == SymbolState::kPublic) {
configMasks[entry->id.value()] |=
util::hostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
if (entry->symbol_status.state == SymbolState::kPublic) {
config_masks[entry->id.value()] |=
util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
const size_t configCount = entry->values.size();
for (size_t i = 0; i < configCount; i++) {
const size_t config_count = entry->values.size();
for (size_t i = 0; i < config_count; i++) {
const ConfigDescription& config = entry->values[i]->config;
for (size_t j = i + 1; j < configCount; j++) {
configMasks[entry->id.value()] |=
util::hostToDevice32(config.diff(entry->values[j]->config));
for (size_t j = i + 1; j < config_count; j++) {
config_masks[entry->id.value()] |=
util::HostToDevice32(config.diff(entry->values[j]->config));
}
}
}
typeSpecWriter.finish();
type_spec_writer.Finish();
return true;
}
bool flattenTypes(BigBuffer* buffer) {
bool FlattenTypes(BigBuffer* buffer) {
// Sort the types by their IDs. They will be inserted into the StringPool in
// this order.
std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
std::vector<ResourceTableType*> sorted_types = CollectAndSortTypes();
size_t expectedTypeId = 1;
for (ResourceTableType* type : sortedTypes) {
size_t expected_type_id = 1;
for (ResourceTableType* type : sorted_types) {
// If there is a gap in the type IDs, fill in the StringPool
// with empty values until we reach the ID we expect.
while (type->id.value() > expectedTypeId) {
std::stringstream typeName;
typeName << "?" << expectedTypeId;
mTypePool.makeRef(typeName.str());
expectedTypeId++;
while (type->id.value() > expected_type_id) {
std::stringstream type_name;
type_name << "?" << expected_type_id;
type_pool_.MakeRef(type_name.str());
expected_type_id++;
}
expectedTypeId++;
mTypePool.makeRef(toString(type->type));
expected_type_id++;
type_pool_.MakeRef(ToString(type->type));
std::vector<ResourceEntry*> sortedEntries = collectAndSortEntries(type);
std::vector<ResourceEntry*> sorted_entries = CollectAndSortEntries(type);
if (!flattenTypeSpec(type, &sortedEntries, buffer)) {
if (!FlattenTypeSpec(type, &sorted_entries, buffer)) {
return false;
}
@@ -455,35 +454,41 @@ class PackageFlattener {
// each
// configuration available. Here we reverse this to match the binary
// table.
std::map<ConfigDescription, std::vector<FlatEntry>> configToEntryListMap;
for (ResourceEntry* entry : sortedEntries) {
const uint32_t keyIndex =
(uint32_t)mKeyPool.makeRef(entry->name).getIndex();
std::map<ConfigDescription, std::vector<FlatEntry>>
config_to_entry_list_map;
for (ResourceEntry* entry : sorted_entries) {
const uint32_t key_index =
(uint32_t)key_pool_.MakeRef(entry->name).index();
// Group values by configuration.
for (auto& configValue : entry->values) {
configToEntryListMap[configValue->config].push_back(
FlatEntry{entry, configValue->value.get(), keyIndex});
for (auto& config_value : entry->values) {
config_to_entry_list_map[config_value->config].push_back(
FlatEntry{entry, config_value->value.get(), key_index});
}
}
// Flatten a configuration value.
for (auto& entry : configToEntryListMap) {
if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
for (auto& entry : config_to_entry_list_map) {
if (!FlattenConfig(type, entry.first, &entry.second, buffer)) {
return false;
}
}
}
return true;
}
IDiagnostics* diag_;
ResourceTablePackage* package_;
StringPool type_pool_;
StringPool key_pool_;
};
} // namespace
bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) {
bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
// We must do this before writing the resources, since the string pool IDs may
// change.
table->stringPool.sort(
table->string_pool.Sort(
[](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
int diff = a.context.priority - b.context.priority;
if (diff < 0) return true;
@@ -493,30 +498,30 @@ bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) {
if (diff > 0) return false;
return a.value < b.value;
});
table->stringPool.prune();
table->string_pool.Prune();
// Write the ResTable header.
ChunkWriter tableWriter(mBuffer);
ResTable_header* tableHeader =
tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
tableHeader->packageCount = util::hostToDevice32(table->packages.size());
ChunkWriter table_writer(buffer_);
ResTable_header* table_header =
table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
table_header->packageCount = util::HostToDevice32(table->packages.size());
// Flatten the values string pool.
StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool);
BigBuffer packageBuffer(1024);
BigBuffer package_buffer(1024);
// Flatten each package.
for (auto& package : table->packages) {
PackageFlattener flattener(context->getDiagnostics(), package.get());
if (!flattener.flattenPackage(&packageBuffer)) {
PackageFlattener flattener(context->GetDiagnostics(), package.get());
if (!flattener.FlattenPackage(&package_buffer)) {
return false;
}
}
// Finally merge all the packages into the main buffer.
tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
tableWriter.finish();
table_writer.buffer()->AppendBuffer(std::move(package_buffer));
table_writer.Finish();
return true;
}

View File

@@ -17,21 +17,24 @@
#ifndef AAPT_FLATTEN_TABLEFLATTENER_H
#define AAPT_FLATTEN_TABLEFLATTENER_H
#include "android-base/macros.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
namespace aapt {
class BigBuffer;
class ResourceTable;
class TableFlattener : public IResourceTableConsumer {
public:
explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {}
explicit TableFlattener(BigBuffer* buffer) : buffer_(buffer) {}
bool consume(IAaptContext* context, ResourceTable* table) override;
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
BigBuffer* mBuffer;
DISALLOW_COPY_AND_ASSIGN(TableFlattener);
BigBuffer* buffer_;
};
} // namespace aapt

View File

@@ -15,6 +15,7 @@
*/
#include "flatten/TableFlattener.h"
#include "ResourceUtils.h"
#include "test/Test.h"
#include "unflatten/BinaryResourceParser.h"
@@ -27,162 +28,165 @@ namespace aapt {
class TableFlattenerTest : public ::testing::Test {
public:
void SetUp() override {
mContext = test::ContextBuilder()
.setCompilationPackage("com.app.test")
.setPackageId(0x7f)
.build();
context_ = test::ContextBuilder()
.SetCompilationPackage("com.app.test")
.SetPackageId(0x7f)
.Build();
}
::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
::testing::AssertionResult Flatten(ResourceTable* table,
ResTable* out_table) {
BigBuffer buffer(1024);
TableFlattener flattener(&buffer);
if (!flattener.consume(mContext.get(), table)) {
if (!flattener.Consume(context_.get(), table)) {
return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
if (outTable->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
if (out_table->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
}
return ::testing::AssertionSuccess();
}
::testing::AssertionResult flatten(ResourceTable* table,
ResourceTable* outTable) {
::testing::AssertionResult Flatten(ResourceTable* table,
ResourceTable* out_table) {
BigBuffer buffer(1024);
TableFlattener flattener(&buffer);
if (!flattener.consume(mContext.get(), table)) {
if (!flattener.Consume(context_.get(), table)) {
return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
BinaryResourceParser parser(mContext.get(), outTable, {}, data.get(),
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
BinaryResourceParser parser(context_.get(), out_table, {}, data.get(),
buffer.size());
if (!parser.parse()) {
if (!parser.Parse()) {
return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
}
return ::testing::AssertionSuccess();
}
::testing::AssertionResult exists(ResTable* table,
const StringPiece& expectedName,
const ResourceId& expectedId,
const ConfigDescription& expectedConfig,
const uint8_t expectedDataType,
const uint32_t expectedData,
const uint32_t expectedSpecFlags) {
const ResourceName expectedResName = test::parseNameOrDie(expectedName);
::testing::AssertionResult Exists(ResTable* table,
const StringPiece& expected_name,
const ResourceId& expected_id,
const ConfigDescription& expected_config,
const uint8_t expected_data_type,
const uint32_t expected_data,
const uint32_t expected_spec_flags) {
const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
table->setParameters(&expectedConfig);
table->setParameters(&expected_config);
ResTable_config config;
Res_value val;
uint32_t specFlags;
if (table->getResource(expectedId.id, &val, false, 0, &specFlags, &config) <
0) {
uint32_t spec_flags;
if (table->getResource(expected_id.id, &val, false, 0, &spec_flags,
&config) < 0) {
return ::testing::AssertionFailure() << "could not find resource with";
}
if (expectedDataType != val.dataType) {
if (expected_data_type != val.dataType) {
return ::testing::AssertionFailure()
<< "expected data type " << std::hex << (int)expectedDataType
<< "expected data type " << std::hex << (int)expected_data_type
<< " but got data type " << (int)val.dataType << std::dec
<< " instead";
}
if (expectedData != val.data) {
if (expected_data != val.data) {
return ::testing::AssertionFailure()
<< "expected data " << std::hex << expectedData << " but got data "
<< val.data << std::dec << " instead";
<< "expected data " << std::hex << expected_data
<< " but got data " << val.data << std::dec << " instead";
}
if (expectedSpecFlags != specFlags) {
if (expected_spec_flags != spec_flags) {
return ::testing::AssertionFailure()
<< "expected specFlags " << std::hex << expectedSpecFlags
<< " but got specFlags " << specFlags << std::dec << " instead";
<< "expected specFlags " << std::hex << expected_spec_flags
<< " but got specFlags " << spec_flags << std::dec << " instead";
}
ResTable::resource_name actualName;
if (!table->getResourceName(expectedId.id, false, &actualName)) {
ResTable::resource_name actual_name;
if (!table->getResourceName(expected_id.id, false, &actual_name)) {
return ::testing::AssertionFailure() << "failed to find resource name";
}
Maybe<ResourceName> resName = ResourceUtils::toResourceName(actualName);
Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
if (!resName) {
return ::testing::AssertionFailure()
<< "expected name '" << expectedResName << "' but got '"
<< StringPiece16(actualName.package, actualName.packageLen) << ":"
<< StringPiece16(actualName.type, actualName.typeLen) << "/"
<< StringPiece16(actualName.name, actualName.nameLen) << "'";
<< "expected name '" << expected_res_name << "' but got '"
<< StringPiece16(actual_name.package, actual_name.packageLen)
<< ":" << StringPiece16(actual_name.type, actual_name.typeLen)
<< "/" << StringPiece16(actual_name.name, actual_name.nameLen)
<< "'";
}
if (expectedConfig != config) {
if (expected_config != config) {
return ::testing::AssertionFailure() << "expected config '"
<< expectedConfig << "' but got '"
<< expected_config << "' but got '"
<< ConfigDescription(config) << "'";
}
return ::testing::AssertionSuccess();
}
private:
std::unique_ptr<IAaptContext> mContext;
std::unique_ptr<IAaptContext> context_;
};
TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.setPackageId("com.app.test", 0x7f)
.addSimple("com.app.test:id/one", ResourceId(0x7f020000))
.addSimple("com.app.test:id/two", ResourceId(0x7f020001))
.addValue("com.app.test:id/three", ResourceId(0x7f020002),
test::buildReference("com.app.test:id/one",
.SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
test::BuildReference("com.app.test:id/one",
ResourceId(0x7f020000)))
.addValue("com.app.test:integer/one", ResourceId(0x7f030000),
.AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
util::make_unique<BinaryPrimitive>(
uint8_t(Res_value::TYPE_INT_DEC), 1u))
.addValue("com.app.test:integer/one", test::parseConfigOrDie("v1"),
.AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
ResourceId(0x7f030000),
util::make_unique<BinaryPrimitive>(
uint8_t(Res_value::TYPE_INT_DEC), 2u))
.addString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
.addString("com.app.test:layout/bar", ResourceId(0x7f050000),
.AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
.AddString("com.app.test:layout/bar", ResourceId(0x7f050000),
"res/layout/bar.xml")
.build();
.Build();
ResTable resTable;
ASSERT_TRUE(flatten(table.get(), &resTable));
ResTable res_table;
ASSERT_TRUE(Flatten(table.get(), &res_table));
EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020000),
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000),
{}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(exists(&resTable, "com.app.test:id/two", ResourceId(0x7f020001),
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001),
{}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020002),
{}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE,
0x7f020000u, 0u));
EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one",
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
ResTable_config::CONFIG_VERSION));
EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one",
ResourceId(0x7f030000), test::parseConfigOrDie("v1"),
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
ResourceId(0x7f030000), test::ParseConfigOrDie("v1"),
Res_value::TYPE_INT_DEC, 2u,
ResTable_config::CONFIG_VERSION));
std::u16string fooStr = u"foo";
ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(),
fooStr.size());
std::u16string foo_str = u"foo";
ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(),
foo_str.size());
ASSERT_GE(idx, 0);
EXPECT_TRUE(exists(&resTable, "com.app.test:string/test",
EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test",
ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
(uint32_t)idx, 0u));
std::u16string barPath = u"res/layout/bar.xml";
idx = resTable.getTableStringBlock(0)->indexOfString(barPath.data(),
barPath.size());
std::u16string bar_path = u"res/layout/bar.xml";
idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(),
bar_path.size());
ASSERT_GE(idx, 0);
EXPECT_TRUE(exists(&resTable, "com.app.test:layout/bar",
EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar",
ResourceId(0x7f050000), {}, Res_value::TYPE_STRING,
(uint32_t)idx, 0u));
}
@@ -190,42 +194,43 @@ TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.setPackageId("com.app.test", 0x7f)
.addSimple("com.app.test:id/one", ResourceId(0x7f020001))
.addSimple("com.app.test:id/three", ResourceId(0x7f020003))
.build();
.SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020001))
.AddSimple("com.app.test:id/three", ResourceId(0x7f020003))
.Build();
ResTable resTable;
ASSERT_TRUE(flatten(table.get(), &resTable));
ResTable res_table;
ASSERT_TRUE(Flatten(table.get(), &res_table));
EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020001),
{}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020003),
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001),
{}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN,
0u, 0u));
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
Attribute attr(false);
attr.typeMask = android::ResTable_map::TYPE_INTEGER;
attr.minInt = 10;
attr.maxInt = 23;
attr.type_mask = android::ResTable_map::TYPE_INTEGER;
attr.min_int = 10;
attr.max_int = 23;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.setPackageId("android", 0x01)
.addValue("android:attr/foo", ResourceId(0x01010000),
.SetPackageId("android", 0x01)
.AddValue("android:attr/foo", ResourceId(0x01010000),
util::make_unique<Attribute>(attr))
.build();
.Build();
ResourceTable result;
ASSERT_TRUE(flatten(table.get(), &result));
ASSERT_TRUE(Flatten(table.get(), &result));
Attribute* actualAttr =
test::getValue<Attribute>(&result, "android:attr/foo");
test::GetValue<Attribute>(&result, "android:attr/foo");
ASSERT_NE(nullptr, actualAttr);
EXPECT_EQ(attr.isWeak(), actualAttr->isWeak());
EXPECT_EQ(attr.typeMask, actualAttr->typeMask);
EXPECT_EQ(attr.minInt, actualAttr->minInt);
EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
EXPECT_EQ(attr.IsWeak(), actualAttr->IsWeak());
EXPECT_EQ(attr.type_mask, actualAttr->type_mask);
EXPECT_EQ(attr.min_int, actualAttr->min_int);
EXPECT_EQ(attr.max_int, actualAttr->max_int);
}
} // namespace aapt

View File

@@ -15,17 +15,21 @@
*/
#include "flatten/XmlFlattener.h"
#include <algorithm>
#include <map>
#include <vector>
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
#include "utils/misc.h"
#include "SdkConstants.h"
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
#include "xml/XmlDom.h"
#include <androidfw/ResourceTypes.h>
#include <utils/misc.h>
#include <algorithm>
#include <map>
#include <vector>
using namespace android;
namespace aapt {
@@ -34,216 +38,215 @@ namespace {
constexpr uint32_t kLowPriority = 0xffffffffu;
struct XmlFlattenerVisitor : public xml::Visitor {
using xml::Visitor::visit;
static bool cmp_xml_attribute_by_id(const xml::Attribute* a,
const xml::Attribute* b) {
if (a->compiled_attribute && a->compiled_attribute.value().id) {
if (b->compiled_attribute && b->compiled_attribute.value().id) {
return a->compiled_attribute.value().id.value() <
b->compiled_attribute.value().id.value();
}
return true;
} else if (!b->compiled_attribute) {
int diff = a->namespace_uri.compare(b->namespace_uri);
if (diff < 0) {
return true;
} else if (diff > 0) {
return false;
}
return a->name < b->name;
}
return false;
}
BigBuffer* mBuffer;
XmlFlattenerOptions mOptions;
StringPool mPool;
std::map<uint8_t, StringPool> mPackagePools;
class XmlFlattenerVisitor : public xml::Visitor {
public:
using xml::Visitor::Visit;
StringPool pool;
std::map<uint8_t, StringPool> package_pools;
struct StringFlattenDest {
StringPool::Ref ref;
ResStringPool_ref* dest;
};
std::vector<StringFlattenDest> mStringRefs;
// Scratch vector to filter attributes. We avoid allocations
// making this a member.
std::vector<xml::Attribute*> mFilteredAttrs;
std::vector<StringFlattenDest> string_refs;
XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
: mBuffer(buffer), mOptions(options) {}
: buffer_(buffer), options_(options) {}
void addString(const StringPiece& str, uint32_t priority,
android::ResStringPool_ref* dest,
bool treatEmptyStringAsNull = false) {
if (str.empty() && treatEmptyStringAsNull) {
// Some parts of the runtime treat null differently than empty string.
dest->index = util::deviceToHost32(-1);
} else {
mStringRefs.push_back(StringFlattenDest{
mPool.makeRef(str, StringPool::Context(priority)), dest});
}
}
void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
mStringRefs.push_back(StringFlattenDest{ref, dest});
}
void writeNamespace(xml::Namespace* node, uint16_t type) {
ChunkWriter writer(mBuffer);
ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(type);
flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
flatNode->comment.index = util::hostToDevice32(-1);
ResXMLTree_namespaceExt* flatNs =
writer.nextBlock<ResXMLTree_namespaceExt>();
addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
addString(node->namespaceUri, kLowPriority, &flatNs->uri);
writer.finish();
}
void visit(xml::Namespace* node) override {
if (node->namespaceUri == xml::kSchemaTools) {
void Visit(xml::Namespace* node) override {
if (node->namespace_uri == xml::kSchemaTools) {
// Skip dedicated tools namespace.
xml::Visitor::visit(node);
xml::Visitor::Visit(node);
} else {
writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
xml::Visitor::visit(node);
writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
WriteNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
xml::Visitor::Visit(node);
WriteNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
}
}
void visit(xml::Text* node) override {
if (util::trimWhitespace(node->text).empty()) {
void Visit(xml::Text* node) override {
if (util::TrimWhitespace(node->text).empty()) {
// Skip whitespace only text nodes.
return;
}
ChunkWriter writer(mBuffer);
ResXMLTree_node* flatNode =
writer.startChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
flatNode->comment.index = util::hostToDevice32(-1);
ChunkWriter writer(buffer_);
ResXMLTree_node* flat_node =
writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
flat_node->lineNumber = util::HostToDevice32(node->line_number);
flat_node->comment.index = util::HostToDevice32(-1);
ResXMLTree_cdataExt* flatText = writer.nextBlock<ResXMLTree_cdataExt>();
addString(node->text, kLowPriority, &flatText->data);
ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
AddString(node->text, kLowPriority, &flat_text->data);
writer.finish();
writer.Finish();
}
void visit(xml::Element* node) override {
void Visit(xml::Element* node) override {
{
ChunkWriter startWriter(mBuffer);
ResXMLTree_node* flatNode =
startWriter.startChunk<ResXMLTree_node>(RES_XML_START_ELEMENT_TYPE);
flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
flatNode->comment.index = util::hostToDevice32(-1);
ChunkWriter start_writer(buffer_);
ResXMLTree_node* flat_node =
start_writer.StartChunk<ResXMLTree_node>(RES_XML_START_ELEMENT_TYPE);
flat_node->lineNumber = util::HostToDevice32(node->line_number);
flat_node->comment.index = util::HostToDevice32(-1);
ResXMLTree_attrExt* flatElem =
startWriter.nextBlock<ResXMLTree_attrExt>();
ResXMLTree_attrExt* flat_elem =
start_writer.NextBlock<ResXMLTree_attrExt>();
// A missing namespace must be null, not an empty string. Otherwise the
// runtime
// complains.
addString(node->namespaceUri, kLowPriority, &flatElem->ns,
true /* treatEmptyStringAsNull */);
addString(node->name, kLowPriority, &flatElem->name,
true /* treatEmptyStringAsNull */);
// runtime complains.
AddString(node->namespace_uri, kLowPriority, &flat_elem->ns,
true /* treat_empty_string_as_null */);
AddString(node->name, kLowPriority, &flat_elem->name,
true /* treat_empty_string_as_null */);
flatElem->attributeStart = util::hostToDevice16(sizeof(*flatElem));
flatElem->attributeSize =
util::hostToDevice16(sizeof(ResXMLTree_attribute));
flat_elem->attributeStart = util::HostToDevice16(sizeof(*flat_elem));
flat_elem->attributeSize =
util::HostToDevice16(sizeof(ResXMLTree_attribute));
writeAttributes(node, flatElem, &startWriter);
WriteAttributes(node, flat_elem, &start_writer);
startWriter.finish();
start_writer.Finish();
}
xml::Visitor::visit(node);
xml::Visitor::Visit(node);
{
ChunkWriter endWriter(mBuffer);
ResXMLTree_node* flatEndNode =
endWriter.startChunk<ResXMLTree_node>(RES_XML_END_ELEMENT_TYPE);
flatEndNode->lineNumber = util::hostToDevice32(node->lineNumber);
flatEndNode->comment.index = util::hostToDevice32(-1);
ChunkWriter end_writer(buffer_);
ResXMLTree_node* flat_end_node =
end_writer.StartChunk<ResXMLTree_node>(RES_XML_END_ELEMENT_TYPE);
flat_end_node->lineNumber = util::HostToDevice32(node->line_number);
flat_end_node->comment.index = util::HostToDevice32(-1);
ResXMLTree_endElementExt* flatEndElem =
endWriter.nextBlock<ResXMLTree_endElementExt>();
addString(node->namespaceUri, kLowPriority, &flatEndElem->ns,
true /* treatEmptyStringAsNull */);
addString(node->name, kLowPriority, &flatEndElem->name);
ResXMLTree_endElementExt* flat_end_elem =
end_writer.NextBlock<ResXMLTree_endElementExt>();
AddString(node->namespace_uri, kLowPriority, &flat_end_elem->ns,
true /* treat_empty_string_as_null */);
AddString(node->name, kLowPriority, &flat_end_elem->name);
endWriter.finish();
end_writer.Finish();
}
}
static bool cmpXmlAttributeById(const xml::Attribute* a,
const xml::Attribute* b) {
if (a->compiledAttribute && a->compiledAttribute.value().id) {
if (b->compiledAttribute && b->compiledAttribute.value().id) {
return a->compiledAttribute.value().id.value() <
b->compiledAttribute.value().id.value();
}
return true;
} else if (!b->compiledAttribute) {
int diff = a->namespaceUri.compare(b->namespaceUri);
if (diff < 0) {
return true;
} else if (diff > 0) {
return false;
}
return a->name < b->name;
private:
DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
void AddString(const StringPiece& str, uint32_t priority,
android::ResStringPool_ref* dest,
bool treat_empty_string_as_null = false) {
if (str.empty() && treat_empty_string_as_null) {
// Some parts of the runtime treat null differently than empty string.
dest->index = util::DeviceToHost32(-1);
} else {
string_refs.push_back(StringFlattenDest{
pool.MakeRef(str, StringPool::Context(priority)), dest});
}
return false;
}
void writeAttributes(xml::Element* node, ResXMLTree_attrExt* flatElem,
void AddString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
string_refs.push_back(StringFlattenDest{ref, dest});
}
void WriteNamespace(xml::Namespace* node, uint16_t type) {
ChunkWriter writer(buffer_);
ResXMLTree_node* flatNode = writer.StartChunk<ResXMLTree_node>(type);
flatNode->lineNumber = util::HostToDevice32(node->line_number);
flatNode->comment.index = util::HostToDevice32(-1);
ResXMLTree_namespaceExt* flat_ns =
writer.NextBlock<ResXMLTree_namespaceExt>();
AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix);
AddString(node->namespace_uri, kLowPriority, &flat_ns->uri);
writer.Finish();
}
void WriteAttributes(xml::Element* node, ResXMLTree_attrExt* flat_elem,
ChunkWriter* writer) {
mFilteredAttrs.clear();
mFilteredAttrs.reserve(node->attributes.size());
filtered_attrs_.clear();
filtered_attrs_.reserve(node->attributes.size());
// Filter the attributes.
for (xml::Attribute& attr : node->attributes) {
if (mOptions.maxSdkLevel && attr.compiledAttribute &&
attr.compiledAttribute.value().id) {
size_t sdkLevel =
findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
if (sdkLevel > mOptions.maxSdkLevel.value()) {
if (options_.max_sdk_level && attr.compiled_attribute &&
attr.compiled_attribute.value().id) {
size_t sdk_level =
FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
if (sdk_level > options_.max_sdk_level.value()) {
continue;
}
}
if (attr.namespaceUri == xml::kSchemaTools) {
if (attr.namespace_uri == xml::kSchemaTools) {
continue;
}
mFilteredAttrs.push_back(&attr);
filtered_attrs_.push_back(&attr);
}
if (mFilteredAttrs.empty()) {
if (filtered_attrs_.empty()) {
return;
}
const ResourceId kIdAttr(0x010100d0);
std::sort(mFilteredAttrs.begin(), mFilteredAttrs.end(),
cmpXmlAttributeById);
std::sort(filtered_attrs_.begin(), filtered_attrs_.end(),
cmp_xml_attribute_by_id);
flatElem->attributeCount = util::hostToDevice16(mFilteredAttrs.size());
flat_elem->attributeCount = util::HostToDevice16(filtered_attrs_.size());
ResXMLTree_attribute* flatAttr =
writer->nextBlock<ResXMLTree_attribute>(mFilteredAttrs.size());
uint16_t attributeIndex = 1;
for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
ResXMLTree_attribute* flat_attr =
writer->NextBlock<ResXMLTree_attribute>(filtered_attrs_.size());
uint16_t attribute_index = 1;
for (const xml::Attribute* xml_attr : filtered_attrs_) {
// Assign the indices for specific attributes.
if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
flatElem->idIndex = util::hostToDevice16(attributeIndex);
} else if (xmlAttr->namespaceUri.empty()) {
if (xmlAttr->name == "class") {
flatElem->classIndex = util::hostToDevice16(attributeIndex);
} else if (xmlAttr->name == "style") {
flatElem->styleIndex = util::hostToDevice16(attributeIndex);
if (xml_attr->compiled_attribute &&
xml_attr->compiled_attribute.value().id &&
xml_attr->compiled_attribute.value().id.value() == kIdAttr) {
flat_elem->idIndex = util::HostToDevice16(attribute_index);
} else if (xml_attr->namespace_uri.empty()) {
if (xml_attr->name == "class") {
flat_elem->classIndex = util::HostToDevice16(attribute_index);
} else if (xml_attr->name == "style") {
flat_elem->styleIndex = util::HostToDevice16(attribute_index);
}
}
attributeIndex++;
attribute_index++;
// Add the namespaceUri to the list of StringRefs to encode. Use null if
// the namespace
// is empty (doesn't exist).
addString(xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns,
true /* treatEmptyStringAsNull */);
AddString(xml_attr->namespace_uri, kLowPriority, &flat_attr->ns,
true /* treat_empty_string_as_null */);
flatAttr->rawValue.index = util::hostToDevice32(-1);
flat_attr->rawValue.index = util::HostToDevice32(-1);
if (!xmlAttr->compiledAttribute ||
!xmlAttr->compiledAttribute.value().id) {
if (!xml_attr->compiled_attribute ||
!xml_attr->compiled_attribute.value().id) {
// The attribute has no associated ResourceID, so the string order
// doesn't matter.
addString(xmlAttr->name, kLowPriority, &flatAttr->name);
AddString(xml_attr->name, kLowPriority, &flat_attr->name);
} else {
// Attribute names are stored without packages, but we use
// their StringPool index to lookup their resource IDs.
@@ -252,99 +255,106 @@ struct XmlFlattenerVisitor : public xml::Visitor {
// pools that we later combine.
//
// Lookup the StringPool for this package and make the reference there.
const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
const xml::AaptAttribute& aapt_attr =
xml_attr->compiled_attribute.value();
StringPool::Ref nameRef =
mPackagePools[aaptAttr.id.value().packageId()].makeRef(
xmlAttr->name, StringPool::Context(aaptAttr.id.value().id));
StringPool::Ref name_ref =
package_pools[aapt_attr.id.value().package_id()].MakeRef(
xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
// Add it to the list of strings to flatten.
addString(nameRef, &flatAttr->name);
AddString(name_ref, &flat_attr->name);
}
if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
if (options_.keep_raw_values || !xml_attr->compiled_value) {
// Keep raw values if the value is not compiled or
// if we're building a static library (need symbols).
addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
}
if (xmlAttr->compiledValue) {
bool result = xmlAttr->compiledValue->flatten(&flatAttr->typedValue);
assert(result);
if (xml_attr->compiled_value) {
CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
} else {
// Flatten as a regular string type.
flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
addString(xmlAttr->value, kLowPriority,
(ResStringPool_ref*)&flatAttr->typedValue.data);
flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
AddString(xml_attr->value, kLowPriority,
(ResStringPool_ref*)&flat_attr->typedValue.data);
}
flatAttr->typedValue.size =
util::hostToDevice16(sizeof(flatAttr->typedValue));
flatAttr++;
flat_attr->typedValue.size =
util::HostToDevice16(sizeof(flat_attr->typedValue));
flat_attr++;
}
}
BigBuffer* buffer_;
XmlFlattenerOptions options_;
// Scratch vector to filter attributes. We avoid allocations
// making this a member.
std::vector<xml::Attribute*> filtered_attrs_;
};
} // namespace
bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
BigBuffer nodeBuffer(1024);
XmlFlattenerVisitor visitor(&nodeBuffer, mOptions);
node->accept(&visitor);
bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
BigBuffer node_buffer(1024);
XmlFlattenerVisitor visitor(&node_buffer, options_);
node->Accept(&visitor);
// Merge the package pools into the main pool.
for (auto& packagePoolEntry : visitor.mPackagePools) {
visitor.mPool.merge(std::move(packagePoolEntry.second));
for (auto& package_pool_entry : visitor.package_pools) {
visitor.pool.Merge(std::move(package_pool_entry.second));
}
// Sort the string pool so that attribute resource IDs show up first.
visitor.mPool.sort(
visitor.pool.Sort(
[](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
return a.context.priority < b.context.priority;
});
// Now we flatten the string pool references into the correct places.
for (const auto& refEntry : visitor.mStringRefs) {
refEntry.dest->index = util::hostToDevice32(refEntry.ref.getIndex());
for (const auto& ref_entry : visitor.string_refs) {
ref_entry.dest->index = util::HostToDevice32(ref_entry.ref.index());
}
// Write the XML header.
ChunkWriter xmlHeaderWriter(mBuffer);
xmlHeaderWriter.startChunk<ResXMLTree_header>(RES_XML_TYPE);
ChunkWriter xml_header_writer(buffer_);
xml_header_writer.StartChunk<ResXMLTree_header>(RES_XML_TYPE);
// Flatten the StringPool.
StringPool::flattenUtf8(mBuffer, visitor.mPool);
StringPool::FlattenUtf8(buffer_, visitor.pool);
{
// Write the array of resource IDs, indexed by StringPool order.
ChunkWriter resIdMapWriter(mBuffer);
resIdMapWriter.startChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
for (const auto& str : visitor.mPool) {
ChunkWriter res_id_map_writer(buffer_);
res_id_map_writer.StartChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
for (const auto& str : visitor.pool) {
ResourceId id = {str->context.priority};
if (id.id == kLowPriority || !id.isValid()) {
if (id.id == kLowPriority || !id.is_valid()) {
// When we see the first non-resource ID,
// we're done.
break;
}
*resIdMapWriter.nextBlock<uint32_t>() = id.id;
*res_id_map_writer.NextBlock<uint32_t>() = id.id;
}
resIdMapWriter.finish();
res_id_map_writer.Finish();
}
// Move the nodeBuffer and append it to the out buffer.
mBuffer->appendBuffer(std::move(nodeBuffer));
buffer_->AppendBuffer(std::move(node_buffer));
// Finish the xml header.
xmlHeaderWriter.finish();
xml_header_writer.Finish();
return true;
}
bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) {
bool XmlFlattener::Consume(IAaptContext* context, xml::XmlResource* resource) {
if (!resource->root) {
return false;
}
return flatten(context, resource->root.get());
return Flatten(context, resource->root.get());
}
} // namespace aapt

View File

@@ -17,6 +17,8 @@
#ifndef AAPT_FLATTEN_XMLFLATTENER_H
#define AAPT_FLATTEN_XMLFLATTENER_H
#include "android-base/macros.h"
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
#include "xml/XmlDom.h"
@@ -27,26 +29,28 @@ struct XmlFlattenerOptions {
/**
* Keep attribute raw string values along with typed values.
*/
bool keepRawValues = false;
bool keep_raw_values = false;
/**
* If set, the max SDK level of attribute to flatten. All others are ignored.
*/
Maybe<size_t> maxSdkLevel;
Maybe<size_t> max_sdk_level;
};
class XmlFlattener : public IXmlResourceConsumer {
public:
XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
: mBuffer(buffer), mOptions(options) {}
: buffer_(buffer), options_(options) {}
bool consume(IAaptContext* context, xml::XmlResource* resource) override;
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
private:
BigBuffer* mBuffer;
XmlFlattenerOptions mOptions;
DISALLOW_COPY_AND_ASSIGN(XmlFlattener);
bool flatten(IAaptContext* context, xml::Node* node);
bool Flatten(IAaptContext* context, xml::Node* node);
BigBuffer* buffer_;
XmlFlattenerOptions options_;
};
} // namespace aapt

View File

@@ -15,61 +15,62 @@
*/
#include "flatten/XmlFlattener.h"
#include "androidfw/ResourceTypes.h"
#include "link/Linkers.h"
#include "test/Test.h"
#include "util/BigBuffer.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
class XmlFlattenerTest : public ::testing::Test {
public:
void SetUp() override {
mContext =
context_ =
test::ContextBuilder()
.setCompilationPackage("com.app.test")
.setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
.addSymbolSource(
.SetCompilationPackage("com.app.test")
.SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
.AddSymbolSource(
test::StaticSymbolSourceBuilder()
.addSymbol("android:attr/id", ResourceId(0x010100d0),
test::AttributeBuilder().build())
.addSymbol("com.app.test:id/id", ResourceId(0x7f020000))
.addSymbol("android:attr/paddingStart",
.AddSymbol("android:attr/id", ResourceId(0x010100d0),
test::AttributeBuilder().Build())
.AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
.AddSymbol("android:attr/paddingStart",
ResourceId(0x010103b3),
test::AttributeBuilder().build())
.addSymbol("android:attr/colorAccent",
test::AttributeBuilder().Build())
.AddSymbol("android:attr/colorAccent",
ResourceId(0x01010435),
test::AttributeBuilder().build())
.build())
.build();
test::AttributeBuilder().Build())
.Build())
.Build();
}
::testing::AssertionResult flatten(xml::XmlResource* doc,
android::ResXMLTree* outTree,
::testing::AssertionResult Flatten(xml::XmlResource* doc,
android::ResXMLTree* out_tree,
const XmlFlattenerOptions& options = {}) {
using namespace android; // For NO_ERROR on windows because it is a macro.
BigBuffer buffer(1024);
XmlFlattener flattener(&buffer, options);
if (!flattener.consume(mContext.get(), doc)) {
if (!flattener.Consume(context_.get(), doc)) {
return ::testing::AssertionFailure() << "failed to flatten XML Tree";
}
std::unique_ptr<uint8_t[]> data = util::copy(buffer);
if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
return ::testing::AssertionFailure() << "flattened XML is corrupt";
}
return ::testing::AssertionSuccess();
}
protected:
std::unique_ptr<IAaptContext> mContext;
std::unique_ptr<IAaptContext> context_;
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:test="http://com.test"
attr="hey">
<Layout test:hello="hi" />
@@ -77,27 +78,27 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
</View>)EOF");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
ASSERT_TRUE(Flatten(doc.get(), &tree));
ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
size_t len;
const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
const char16_t* namespaceUri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
const char16_t* namespace_uri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
const char16_t* tagName = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tagName, len), u"View");
const char16_t* tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"View");
ASSERT_EQ(1u, tree.getAttributeCount());
ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
const char16_t* attrName = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attrName, len), u"attr");
const char16_t* attr_name = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attr_name, len), u"attr");
EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr",
StringPiece16(u"attr").size()));
@@ -105,22 +106,22 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
tagName = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
ASSERT_EQ(1u, tree.getAttributeCount());
const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len);
EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test");
const char16_t* attr_namespace = tree.getAttributeNamespace(0, &len);
EXPECT_EQ(StringPiece16(attr_namespace, len), u"http://com.test");
attrName = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attrName, len), u"hello");
attr_name = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attr_name, len), u"hello");
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
tagName = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
ASSERT_EQ(0u, tree.getAttributeCount());
ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
@@ -129,39 +130,39 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
tagName = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
tagName = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tagName, len), u"View");
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"View");
ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
namespacePrefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
namespace_prefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
namespaceUri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
namespace_uri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="1dp"
android:colorAccent="#ffffff"/>)EOF");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
ASSERT_TRUE(linker.sdk_levels().count(17) == 1);
ASSERT_TRUE(linker.sdk_levels().count(21) == 1);
android::ResXMLTree tree;
XmlFlattenerOptions options;
options.maxSdkLevel = 17;
ASSERT_TRUE(flatten(doc.get(), &tree, options));
options.max_sdk_level = 17;
ASSERT_TRUE(Flatten(doc.get(), &tree, options));
while (tree.next() != android::ResXMLTree::START_TAG) {
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
@@ -173,23 +174,23 @@ TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:tools="http://schemas.android.com/tools"
xmlns:foo="http://schemas.android.com/foo"
foo:bar="Foo"
tools:ignore="MissingTranslation"/>)EOF");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
ASSERT_TRUE(Flatten(doc.get(), &tree));
ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
size_t len;
const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespacePrefix, len), u"foo");
const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespace_prefix, len), u"foo");
const char16_t* namespaceUri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespaceUri, len),
const char16_t* namespace_uri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespace_uri, len),
u"http://schemas.android.com/foo");
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
@@ -200,14 +201,14 @@ TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
}
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/id"
class="str"
style="@id/id"/>)EOF");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
@@ -225,10 +226,10 @@ TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
*/
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
std::unique_ptr<xml::XmlResource> doc =
test::buildXmlDom("<View package=\"android\"/>");
test::BuildXmlDom("<View package=\"android\"/>");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
@@ -242,10 +243,10 @@ TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
std::unique_ptr<xml::XmlResource> doc =
test::buildXmlDom("<View package=\"\"/>");
test::BuildXmlDom("<View package=\"\"/>");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);

View File

@@ -17,10 +17,11 @@
#ifndef AAPT_IO_DATA_H
#define AAPT_IO_DATA_H
#include <android-base/macros.h>
#include <utils/FileMap.h>
#include <memory>
#include "android-base/macros.h"
#include "utils/FileMap.h"
namespace aapt {
namespace io {
@@ -39,20 +40,20 @@ class IData {
class DataSegment : public IData {
public:
explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len)
: mData(std::move(data)), mOffset(offset), mLen(len) {}
: data_(std::move(data)), offset_(offset), len_(len) {}
const void* data() const override {
return static_cast<const uint8_t*>(mData->data()) + mOffset;
return static_cast<const uint8_t*>(data_->data()) + offset_;
}
size_t size() const override { return mLen; }
size_t size() const override { return len_; }
private:
DISALLOW_COPY_AND_ASSIGN(DataSegment);
std::unique_ptr<IData> mData;
size_t mOffset;
size_t mLen;
std::unique_ptr<IData> data_;
size_t offset_;
size_t len_;
};
/**
@@ -63,14 +64,14 @@ class DataSegment : public IData {
class MmappedData : public IData {
public:
explicit MmappedData(android::FileMap&& map)
: mMap(std::forward<android::FileMap>(map)) {}
: map_(std::forward<android::FileMap>(map)) {}
const void* data() const override { return mMap.getDataPtr(); }
const void* data() const override { return map_.getDataPtr(); }
size_t size() const override { return mMap.getDataLength(); }
size_t size() const override { return map_.getDataLength(); }
private:
android::FileMap mMap;
android::FileMap map_;
};
/**
@@ -81,15 +82,15 @@ class MmappedData : public IData {
class MallocData : public IData {
public:
MallocData(std::unique_ptr<const uint8_t[]> data, size_t size)
: mData(std::move(data)), mSize(size) {}
: data_(std::move(data)), size_(size) {}
const void* data() const override { return mData.get(); }
const void* data() const override { return data_.get(); }
size_t size() const override { return mSize; }
size_t size() const override { return size_; }
private:
std::unique_ptr<const uint8_t[]> mData;
size_t mSize;
std::unique_ptr<const uint8_t[]> data_;
size_t size_;
};
/**

View File

@@ -21,23 +21,23 @@
namespace aapt {
namespace io {
IFile* IFile::createFileSegment(size_t offset, size_t len) {
FileSegment* fileSegment = new FileSegment(this, offset, len);
mSegments.push_back(std::unique_ptr<IFile>(fileSegment));
return fileSegment;
IFile* IFile::CreateFileSegment(size_t offset, size_t len) {
FileSegment* file_segment = new FileSegment(this, offset, len);
segments_.push_back(std::unique_ptr<IFile>(file_segment));
return file_segment;
}
std::unique_ptr<IData> FileSegment::openAsData() {
std::unique_ptr<IData> data = mFile->openAsData();
if (!data) {
return {};
}
if (mOffset <= data->size() - mLen) {
return util::make_unique<DataSegment>(std::move(data), mOffset, mLen);
}
std::unique_ptr<IData> FileSegment::OpenAsData() {
std::unique_ptr<IData> data = file_->OpenAsData();
if (!data) {
return {};
}
if (offset_ <= data->size() - len_) {
return util::make_unique<DataSegment>(std::move(data), offset_, len_);
}
return {};
}
} // namespace io
} // namespace aapt
} // namespace io
} // namespace aapt

View File

@@ -17,15 +17,16 @@
#ifndef AAPT_IO_FILE_H
#define AAPT_IO_FILE_H
#include "Source.h"
#include "io/Data.h"
#include "util/Util.h"
#include <android-base/macros.h>
#include <list>
#include <memory>
#include <vector>
#include "android-base/macros.h"
#include "Source.h"
#include "io/Data.h"
#include "util/Util.h"
namespace aapt {
namespace io {
@@ -49,7 +50,7 @@ class IFile {
*
* Returns nullptr on failure.
*/
virtual std::unique_ptr<IData> openAsData() = 0;
virtual std::unique_ptr<IData> OpenAsData() = 0;
/**
* Returns the source of this file. This is for presentation to the user and
@@ -58,9 +59,9 @@ class IFile {
* the files within
* a ZIP archive from the path to the containing ZIP archive.
*/
virtual const Source& getSource() const = 0;
virtual const Source& GetSource() const = 0;
IFile* createFileSegment(size_t offset, size_t len);
IFile* CreateFileSegment(size_t offset, size_t len);
private:
// Any segments created from this IFile need to be owned by this IFile, so
@@ -68,7 +69,7 @@ class IFile {
// in a list. This will never be read, so we prefer better insertion
// performance
// than cache locality, hence the list.
std::list<std::unique_ptr<IFile>> mSegments;
std::list<std::unique_ptr<IFile>> segments_;
};
/**
@@ -78,26 +79,26 @@ class IFile {
class FileSegment : public IFile {
public:
explicit FileSegment(IFile* file, size_t offset, size_t len)
: mFile(file), mOffset(offset), mLen(len) {}
: file_(file), offset_(offset), len_(len) {}
std::unique_ptr<IData> openAsData() override;
std::unique_ptr<IData> OpenAsData() override;
const Source& getSource() const override { return mFile->getSource(); }
const Source& GetSource() const override { return file_->GetSource(); }
private:
DISALLOW_COPY_AND_ASSIGN(FileSegment);
IFile* mFile;
size_t mOffset;
size_t mLen;
IFile* file_;
size_t offset_;
size_t len_;
};
class IFileCollectionIterator {
public:
virtual ~IFileCollectionIterator() = default;
virtual bool hasNext() = 0;
virtual IFile* next() = 0;
virtual bool HasNext() = 0;
virtual IFile* Next() = 0;
};
/**
@@ -109,8 +110,8 @@ class IFileCollection {
public:
virtual ~IFileCollection() = default;
virtual IFile* findFile(const StringPiece& path) = 0;
virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
virtual IFile* FindFile(const StringPiece& path) = 0;
virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
};
} // namespace io

View File

@@ -14,65 +14,62 @@
* limitations under the License.
*/
#include "Source.h"
#include "io/FileSystem.h"
#include "utils/FileMap.h"
#include "Source.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <utils/FileMap.h>
namespace aapt {
namespace io {
RegularFile::RegularFile(const Source& source) : mSource(source) {
}
RegularFile::RegularFile(const Source& source) : source_(source) {}
std::unique_ptr<IData> RegularFile::openAsData() {
android::FileMap map;
if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
return util::make_unique<MmappedData>(std::move(map.value()));
}
return util::make_unique<EmptyData>();
std::unique_ptr<IData> RegularFile::OpenAsData() {
android::FileMap map;
if (Maybe<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
return util::make_unique<MmappedData>(std::move(map.value()));
}
return {};
return util::make_unique<EmptyData>();
}
return {};
}
const Source& RegularFile::getSource() const {
return mSource;
const Source& RegularFile::GetSource() const { return source_; }
FileCollectionIterator::FileCollectionIterator(FileCollection* collection)
: current_(collection->files_.begin()), end_(collection->files_.end()) {}
bool FileCollectionIterator::HasNext() { return current_ != end_; }
IFile* FileCollectionIterator::Next() {
IFile* result = current_->second.get();
++current_;
return result;
}
FileCollectionIterator::FileCollectionIterator(FileCollection* collection) :
mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
IFile* FileCollection::InsertFile(const StringPiece& path) {
return (files_[path.ToString()] =
util::make_unique<RegularFile>(Source(path)))
.get();
}
bool FileCollectionIterator::hasNext() {
return mCurrent != mEnd;
IFile* FileCollection::FindFile(const StringPiece& path) {
auto iter = files_.find(path.ToString());
if (iter != files_.end()) {
return iter->second.get();
}
return nullptr;
}
IFile* FileCollectionIterator::next() {
IFile* result = mCurrent->second.get();
++mCurrent;
return result;
std::unique_ptr<IFileCollectionIterator> FileCollection::Iterator() {
return util::make_unique<FileCollectionIterator>(this);
}
IFile* FileCollection::insertFile(const StringPiece& path) {
return (mFiles[path.toString()] = util::make_unique<RegularFile>(Source(path))).get();
}
IFile* FileCollection::findFile(const StringPiece& path) {
auto iter = mFiles.find(path.toString());
if (iter != mFiles.end()) {
return iter->second.get();
}
return nullptr;
}
std::unique_ptr<IFileCollectionIterator> FileCollection::iterator() {
return util::make_unique<FileCollectionIterator>(this);
}
} // namespace io
} // namespace aapt
} // namespace io
} // namespace aapt

View File

@@ -17,10 +17,10 @@
#ifndef AAPT_IO_FILESYSTEM_H
#define AAPT_IO_FILESYSTEM_H
#include "io/File.h"
#include <map>
#include "io/File.h"
namespace aapt {
namespace io {
@@ -31,11 +31,11 @@ class RegularFile : public IFile {
public:
explicit RegularFile(const Source& source);
std::unique_ptr<IData> openAsData() override;
const Source& getSource() const override;
std::unique_ptr<IData> OpenAsData() override;
const Source& GetSource() const override;
private:
Source mSource;
Source source_;
};
class FileCollection;
@@ -44,11 +44,11 @@ class FileCollectionIterator : public IFileCollectionIterator {
public:
explicit FileCollectionIterator(FileCollection* collection);
bool hasNext() override;
io::IFile* next() override;
bool HasNext() override;
io::IFile* Next() override;
private:
std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
std::map<std::string, std::unique_ptr<IFile>>::const_iterator current_, end_;
};
/**
@@ -59,13 +59,13 @@ class FileCollection : public IFileCollection {
/**
* Adds a file located at path. Returns the IFile representation of that file.
*/
IFile* insertFile(const StringPiece& path);
IFile* findFile(const StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> iterator() override;
IFile* InsertFile(const StringPiece& path);
IFile* FindFile(const StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
private:
friend class FileCollectionIterator;
std::map<std::string, std::unique_ptr<IFile>> mFiles;
std::map<std::string, std::unique_ptr<IFile>> files_;
};
} // namespace io

View File

@@ -22,23 +22,23 @@
namespace aapt {
namespace io {
bool copy(OutputStream* out, InputStream* in) {
const void* inBuffer;
int inLen;
while (in->Next(&inBuffer, &inLen)) {
void* outBuffer;
int outLen;
if (!out->Next(&outBuffer, &outLen)) {
return !out->HadError();
}
const int bytesToCopy = std::min(inLen, outLen);
memcpy(outBuffer, inBuffer, bytesToCopy);
out->BackUp(outLen - bytesToCopy);
in->BackUp(inLen - bytesToCopy);
bool Copy(OutputStream* out, InputStream* in) {
const void* in_buffer;
int in_len;
while (in->Next(&in_buffer, &in_len)) {
void* out_buffer;
int out_len;
if (!out->Next(&out_buffer, &out_len)) {
return !out->HadError();
}
return !in->HadError();
const int bytes_to_copy = std::min(in_len, out_len);
memcpy(out_buffer, in_buffer, bytes_to_copy);
out->BackUp(out_len - bytes_to_copy);
in->BackUp(in_len - bytes_to_copy);
}
return !in->HadError();
}
} // namespace io
} // namespace aapt
} // namespace io
} // namespace aapt

View File

@@ -17,9 +17,10 @@
#ifndef AAPT_IO_IO_H
#define AAPT_IO_IO_H
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <string>
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace aapt {
namespace io {
@@ -29,7 +30,7 @@ namespace io {
*
* The code style here matches the protobuf style.
*/
class InputStream : public google::protobuf::io::ZeroCopyInputStream {
class InputStream : public ::google::protobuf::io::ZeroCopyInputStream {
public:
virtual std::string GetError() const { return {}; }
@@ -42,7 +43,7 @@ class InputStream : public google::protobuf::io::ZeroCopyInputStream {
*
* The code style here matches the protobuf style.
*/
class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
class OutputStream : public ::google::protobuf::io::ZeroCopyOutputStream {
public:
virtual std::string GetError() const { return {}; }
@@ -54,7 +55,7 @@ class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
* If there was an error, check the individual streams' HadError/GetError
* methods.
*/
bool copy(OutputStream* out, InputStream* in);
bool Copy(OutputStream* out, InputStream* in);
} // namespace io
} // namespace aapt

View File

@@ -14,129 +14,128 @@
* limitations under the License.
*/
#include "Source.h"
#include "io/ZipArchive.h"
#include "util/Util.h"
#include <utils/FileMap.h>
#include <ziparchive/zip_archive.h>
#include "utils/FileMap.h"
#include "ziparchive/zip_archive.h"
#include "Source.h"
#include "util/Util.h"
namespace aapt {
namespace io {
ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
mZipHandle(handle), mZipEntry(entry), mSource(source) {
}
ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry,
const Source& source)
: zip_handle_(handle), zip_entry_(entry), source_(source) {}
std::unique_ptr<IData> ZipFile::openAsData() {
if (mZipEntry.method == kCompressStored) {
int fd = GetFileDescriptor(mZipHandle);
std::unique_ptr<IData> ZipFile::OpenAsData() {
if (zip_entry_.method == kCompressStored) {
int fd = GetFileDescriptor(zip_handle_);
android::FileMap fileMap;
bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
mZipEntry.uncompressed_length, true);
if (!result) {
return {};
}
return util::make_unique<MmappedData>(std::move(fileMap));
} else {
std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
new uint8_t[mZipEntry.uncompressed_length]);
int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
static_cast<uint32_t>(mZipEntry.uncompressed_length));
if (result != 0) {
return {};
}
return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
android::FileMap file_map;
bool result = file_map.create(nullptr, fd, zip_entry_.offset,
zip_entry_.uncompressed_length, true);
if (!result) {
return {};
}
}
return util::make_unique<MmappedData>(std::move(file_map));
const Source& ZipFile::getSource() const {
return mSource;
}
ZipFileCollectionIterator::ZipFileCollectionIterator(ZipFileCollection* collection) :
mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
}
bool ZipFileCollectionIterator::hasNext() {
return mCurrent != mEnd;
}
IFile* ZipFileCollectionIterator::next() {
IFile* result = mCurrent->second.get();
++mCurrent;
return result;
}
ZipFileCollection::ZipFileCollection() : mHandle(nullptr) {
}
std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece& path,
std::string* outError) {
constexpr static const int32_t kEmptyArchive = -6;
std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
new ZipFileCollection());
int32_t result = OpenArchive(path.data(), &collection->mHandle);
} else {
std::unique_ptr<uint8_t[]> data =
std::unique_ptr<uint8_t[]>(new uint8_t[zip_entry_.uncompressed_length]);
int32_t result =
ExtractToMemory(zip_handle_, &zip_entry_, data.get(),
static_cast<uint32_t>(zip_entry_.uncompressed_length));
if (result != 0) {
// If a zip is empty, result will be an error code. This is fine and we should
// return an empty ZipFileCollection.
if (result == kEmptyArchive) {
return collection;
}
if (outError) *outError = ErrorCodeString(result);
return {};
return {};
}
void* cookie = nullptr;
result = StartIteration(collection->mHandle, &cookie, nullptr, nullptr);
if (result != 0) {
if (outError) *outError = ErrorCodeString(result);
return {};
}
using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
IterationEnder iterationEnder(cookie, EndIteration);
ZipString zipEntryName;
ZipEntry zipData;
while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
std::string zipEntryPath = std::string(reinterpret_cast<const char*>(zipEntryName.name),
zipEntryName.name_length);
std::string nestedPath = path.toString() + "@" + zipEntryPath;
collection->mFiles[zipEntryPath] = util::make_unique<ZipFile>(collection->mHandle,
zipData,
Source(nestedPath));
}
if (result != -1) {
if (outError) *outError = ErrorCodeString(result);
return {};
}
return collection;
return util::make_unique<MallocData>(std::move(data),
zip_entry_.uncompressed_length);
}
}
IFile* ZipFileCollection::findFile(const StringPiece& path) {
auto iter = mFiles.find(path.toString());
if (iter != mFiles.end()) {
return iter->second.get();
}
return nullptr;
const Source& ZipFile::GetSource() const { return source_; }
ZipFileCollectionIterator::ZipFileCollectionIterator(
ZipFileCollection* collection)
: current_(collection->files_.begin()), end_(collection->files_.end()) {}
bool ZipFileCollectionIterator::HasNext() { return current_ != end_; }
IFile* ZipFileCollectionIterator::Next() {
IFile* result = current_->second.get();
++current_;
return result;
}
std::unique_ptr<IFileCollectionIterator> ZipFileCollection::iterator() {
return util::make_unique<ZipFileCollectionIterator>(this);
ZipFileCollection::ZipFileCollection() : handle_(nullptr) {}
std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
const StringPiece& path, std::string* out_error) {
constexpr static const int32_t kEmptyArchive = -6;
std::unique_ptr<ZipFileCollection> collection =
std::unique_ptr<ZipFileCollection>(new ZipFileCollection());
int32_t result = OpenArchive(path.data(), &collection->handle_);
if (result != 0) {
// If a zip is empty, result will be an error code. This is fine and we
// should
// return an empty ZipFileCollection.
if (result == kEmptyArchive) {
return collection;
}
if (out_error) *out_error = ErrorCodeString(result);
return {};
}
void* cookie = nullptr;
result = StartIteration(collection->handle_, &cookie, nullptr, nullptr);
if (result != 0) {
if (out_error) *out_error = ErrorCodeString(result);
return {};
}
using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
IterationEnder iteration_ender(cookie, EndIteration);
ZipString zip_entry_name;
ZipEntry zip_data;
while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) {
std::string zip_entry_path =
std::string(reinterpret_cast<const char*>(zip_entry_name.name),
zip_entry_name.name_length);
std::string nested_path = path.ToString() + "@" + zip_entry_path;
collection->files_[zip_entry_path] = util::make_unique<ZipFile>(
collection->handle_, zip_data, Source(nested_path));
}
if (result != -1) {
if (out_error) *out_error = ErrorCodeString(result);
return {};
}
return collection;
}
IFile* ZipFileCollection::FindFile(const StringPiece& path) {
auto iter = files_.find(path.ToString());
if (iter != files_.end()) {
return iter->second.get();
}
return nullptr;
}
std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() {
return util::make_unique<ZipFileCollectionIterator>(this);
}
ZipFileCollection::~ZipFileCollection() {
if (mHandle) {
CloseArchive(mHandle);
}
if (handle_) {
CloseArchive(handle_);
}
}
} // namespace io
} // namespace aapt
} // namespace io
} // namespace aapt

View File

@@ -17,12 +17,13 @@
#ifndef AAPT_IO_ZIPARCHIVE_H
#define AAPT_IO_ZIPARCHIVE_H
#include "ziparchive/zip_archive.h"
#include <map>
#include "io/File.h"
#include "util/StringPiece.h"
#include <ziparchive/zip_archive.h>
#include <map>
namespace aapt {
namespace io {
@@ -36,13 +37,13 @@ class ZipFile : public IFile {
public:
ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
std::unique_ptr<IData> openAsData() override;
const Source& getSource() const override;
std::unique_ptr<IData> OpenAsData() override;
const Source& GetSource() const override;
private:
ZipArchiveHandle mZipHandle;
ZipEntry mZipEntry;
Source mSource;
ZipArchiveHandle zip_handle_;
ZipEntry zip_entry_;
Source source_;
};
class ZipFileCollection;
@@ -51,11 +52,11 @@ class ZipFileCollectionIterator : public IFileCollectionIterator {
public:
explicit ZipFileCollectionIterator(ZipFileCollection* collection);
bool hasNext() override;
io::IFile* next() override;
bool HasNext() override;
io::IFile* Next() override;
private:
std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
std::map<std::string, std::unique_ptr<IFile>>::const_iterator current_, end_;
};
/**
@@ -63,11 +64,11 @@ class ZipFileCollectionIterator : public IFileCollectionIterator {
*/
class ZipFileCollection : public IFileCollection {
public:
static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
static std::unique_ptr<ZipFileCollection> Create(const StringPiece& path,
std::string* outError);
io::IFile* findFile(const StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> iterator() override;
io::IFile* FindFile(const StringPiece& path) override;
std::unique_ptr<IFileCollectionIterator> Iterator() override;
~ZipFileCollection() override;
@@ -75,8 +76,8 @@ class ZipFileCollection : public IFileCollection {
friend class ZipFileCollectionIterator;
ZipFileCollection();
ZipArchiveHandle mHandle;
std::map<std::string, std::unique_ptr<IFile>> mFiles;
ZipArchiveHandle handle_;
std::map<std::string, std::unique_ptr<IFile>> files_;
};
} // namespace io

View File

@@ -15,69 +15,71 @@
*/
#include "java/AnnotationProcessor.h"
#include "util/Util.h"
#include <algorithm>
#include "util/Util.h"
namespace aapt {
void AnnotationProcessor::appendCommentLine(std::string& comment) {
static const std::string sDeprecated = "@deprecated";
static const std::string sSystemApi = "@SystemApi";
void AnnotationProcessor::AppendCommentLine(std::string& comment) {
static const std::string sDeprecated = "@deprecated";
static const std::string sSystemApi = "@SystemApi";
if (comment.find(sDeprecated) != std::string::npos) {
mAnnotationBitMask |= kDeprecated;
}
if (comment.find(sDeprecated) != std::string::npos) {
annotation_bit_mask_ |= kDeprecated;
}
std::string::size_type idx = comment.find(sSystemApi);
if (idx != std::string::npos) {
mAnnotationBitMask |= kSystemApi;
comment.erase(comment.begin() + idx, comment.begin() + idx + sSystemApi.size());
}
std::string::size_type idx = comment.find(sSystemApi);
if (idx != std::string::npos) {
annotation_bit_mask_ |= kSystemApi;
comment.erase(comment.begin() + idx,
comment.begin() + idx + sSystemApi.size());
}
if (util::trimWhitespace(comment).empty()) {
return;
}
if (util::TrimWhitespace(comment).empty()) {
return;
}
if (!mHasComments) {
mHasComments = true;
mComment << "/**";
}
if (!has_comments_) {
has_comments_ = true;
comment_ << "/**";
}
mComment << "\n * " << std::move(comment);
comment_ << "\n * " << std::move(comment);
}
void AnnotationProcessor::appendComment(const StringPiece& comment) {
// We need to process line by line to clean-up whitespace and append prefixes.
for (StringPiece line : util::tokenize(comment, '\n')) {
line = util::trimWhitespace(line);
if (!line.empty()) {
std::string lineCopy = line.toString();
appendCommentLine(lineCopy);
}
void AnnotationProcessor::AppendComment(const StringPiece& comment) {
// We need to process line by line to clean-up whitespace and append prefixes.
for (StringPiece line : util::Tokenize(comment, '\n')) {
line = util::TrimWhitespace(line);
if (!line.empty()) {
std::string lineCopy = line.ToString();
AppendCommentLine(lineCopy);
}
}
}
void AnnotationProcessor::appendNewLine() {
mComment << "\n *";
void AnnotationProcessor::AppendNewLine() { comment_ << "\n *"; }
void AnnotationProcessor::WriteToStream(std::ostream* out,
const StringPiece& prefix) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
*out << prefix << line << "\n";
}
*out << prefix << " */"
<< "\n";
}
if (annotation_bit_mask_ & kDeprecated) {
*out << prefix << "@Deprecated\n";
}
if (annotation_bit_mask_ & kSystemApi) {
*out << prefix << "@android.annotation.SystemApi\n";
}
}
void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const {
if (mHasComments) {
std::string result = mComment.str();
for (StringPiece line : util::tokenize(result, '\n')) {
*out << prefix << line << "\n";
}
*out << prefix << " */" << "\n";
}
if (mAnnotationBitMask & kDeprecated) {
*out << prefix << "@Deprecated\n";
}
if (mAnnotationBitMask & kSystemApi) {
*out << prefix << "@android.annotation.SystemApi\n";
}
}
} // namespace aapt
} // namespace aapt

View File

@@ -17,11 +17,11 @@
#ifndef AAPT_JAVA_ANNOTATIONPROCESSOR_H
#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
#include "util/StringPiece.h"
#include <sstream>
#include <string>
#include "util/StringPiece.h"
namespace aapt {
/**
@@ -58,15 +58,15 @@ class AnnotationProcessor {
* configurations,
* we need to collect all the comments.
*/
void appendComment(const StringPiece& comment);
void AppendComment(const StringPiece& comment);
void appendNewLine();
void AppendNewLine();
/**
* Writes the comments and annotations to the stream, with the given prefix
* before each line.
*/
void writeToStream(std::ostream* out, const StringPiece& prefix) const;
void WriteToStream(std::ostream* out, const StringPiece& prefix) const;
private:
enum : uint32_t {
@@ -74,12 +74,12 @@ class AnnotationProcessor {
kSystemApi = 0x02,
};
std::stringstream mComment;
std::stringstream comment_;
std::stringstream mAnnotations;
bool mHasComments = false;
uint32_t mAnnotationBitMask = 0;
bool has_comments_ = false;
uint32_t annotation_bit_mask_ = 0;
void appendCommentLine(std::string& line);
void AppendCommentLine(std::string& line);
};
} // namespace aapt

View File

@@ -15,38 +15,39 @@
*/
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
namespace aapt {
TEST(AnnotationProcessorTest, EmitsDeprecated) {
const char* comment = "Some comment, and it should contain a marker word, "
"something that marks this resource as nor needed. "
"{@deprecated That's the marker! }";
const char* comment =
"Some comment, and it should contain a marker word, "
"something that marks this resource as nor needed. "
"{@deprecated That's the marker! }";
AnnotationProcessor processor;
processor.appendComment(comment);
AnnotationProcessor processor;
processor.AppendComment(comment);
std::stringstream result;
processor.writeToStream(&result, "");
std::string annotations = result.str();
std::stringstream result;
processor.WriteToStream(&result, "");
std::string annotations = result.str();
EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
}
TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.appendComment("@SystemApi This is a system API");
AnnotationProcessor processor;
processor.AppendComment("@SystemApi This is a system API");
std::stringstream result;
processor.writeToStream(&result, "");
std::string annotations = result.str();
std::stringstream result;
processor.WriteToStream(&result, "");
std::string annotations = result.str();
EXPECT_NE(std::string::npos, annotations.find("@android.annotation.SystemApi"));
EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
EXPECT_NE(std::string::npos,
annotations.find("@android.annotation.SystemApi"));
EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
}
} // namespace aapt
} // namespace aapt

View File

@@ -15,61 +15,59 @@
*/
#include "java/ClassDefinition.h"
#include "util/StringPiece.h"
#include <ostream>
#include "util/StringPiece.h"
namespace aapt {
bool ClassDefinition::empty() const {
for (const std::unique_ptr<ClassMember>& member : mMembers) {
if (!member->empty()) {
return false;
}
for (const std::unique_ptr<ClassMember>& member : members_) {
if (!member->empty()) {
return false;
}
return true;
}
return true;
}
void ClassDefinition::writeToStream(const StringPiece& prefix, bool final,
void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const {
if (mMembers.empty() && !mCreateIfEmpty) {
return;
}
if (members_.empty() && !create_if_empty_) {
return;
}
ClassMember::writeToStream(prefix, final, out);
ClassMember::WriteToStream(prefix, final, out);
*out << prefix << "public ";
if (mQualifier == ClassQualifier::Static) {
*out << "static ";
}
*out << "final class " << mName << " {\n";
*out << prefix << "public ";
if (qualifier_ == ClassQualifier::Static) {
*out << "static ";
}
*out << "final class " << name_ << " {\n";
std::string newPrefix = prefix.toString();
newPrefix.append(kIndent);
std::string new_prefix = prefix.ToString();
new_prefix.append(kIndent);
for (const std::unique_ptr<ClassMember>& member : mMembers) {
member->writeToStream(newPrefix, final, out);
*out << "\n";
}
for (const std::unique_ptr<ClassMember>& member : members_) {
member->WriteToStream(new_prefix, final, out);
*out << "\n";
}
*out << prefix << "}";
*out << prefix << "}";
}
constexpr static const char* sWarningHeader =
"/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
" *\n"
" * This class was automatically generated by the\n"
" * aapt tool from the resource data it found. It\n"
" * should not be modified by hand.\n"
" */\n\n";
"/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
" *\n"
" * This class was automatically generated by the\n"
" * aapt tool from the resource data it found. It\n"
" * should not be modified by hand.\n"
" */\n\n";
bool ClassDefinition::writeJavaFile(const ClassDefinition* def,
const StringPiece& package,
bool final,
bool ClassDefinition::WriteJavaFile(const ClassDefinition* def,
const StringPiece& package, bool final,
std::ostream* out) {
*out << sWarningHeader << "package " << package << ";\n\n";
def->writeToStream("", final, out);
return bool(*out);
*out << sWarningHeader << "package " << package << ";\n\n";
def->WriteToStream("", final, out);
return bool(*out);
}
} // namespace aapt
} // namespace aapt

View File

@@ -17,15 +17,16 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
#include <ostream>
#include <string>
#include "android-base/macros.h"
#include "Resource.h"
#include "java/AnnotationProcessor.h"
#include "util/StringPiece.h"
#include "util/Util.h"
#include <android-base/macros.h>
#include <sstream>
#include <string>
namespace aapt {
// The number of attributes to emit per line in a Styleable array.
@@ -36,38 +37,38 @@ class ClassMember {
public:
virtual ~ClassMember() = default;
AnnotationProcessor* getCommentBuilder() { return &mProcessor; }
AnnotationProcessor* GetCommentBuilder() { return &processor_; }
virtual bool empty() const = 0;
virtual void writeToStream(const StringPiece& prefix, bool final,
virtual void WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const {
mProcessor.writeToStream(out, prefix);
processor_.WriteToStream(out, prefix);
}
private:
AnnotationProcessor mProcessor;
AnnotationProcessor processor_;
};
template <typename T>
class PrimitiveMember : public ClassMember {
public:
PrimitiveMember(const StringPiece& name, const T& val)
: mName(name.toString()), mVal(val) {}
: name_(name.ToString()), val_(val) {}
bool empty() const override { return false; }
void writeToStream(const StringPiece& prefix, bool final,
void WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const override {
ClassMember::writeToStream(prefix, final, out);
ClassMember::WriteToStream(prefix, final, out);
*out << prefix << "public static " << (final ? "final " : "") << "int "
<< mName << "=" << mVal << ";";
<< name_ << "=" << val_ << ";";
}
private:
std::string mName;
T mVal;
std::string name_;
T val_;
DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
@@ -79,21 +80,21 @@ template <>
class PrimitiveMember<std::string> : public ClassMember {
public:
PrimitiveMember(const StringPiece& name, const std::string& val)
: mName(name.toString()), mVal(val) {}
: name_(name.ToString()), val_(val) {}
bool empty() const override { return false; }
void writeToStream(const StringPiece& prefix, bool final,
void WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const override {
ClassMember::writeToStream(prefix, final, out);
ClassMember::WriteToStream(prefix, final, out);
*out << prefix << "public static " << (final ? "final " : "") << "String "
<< mName << "=\"" << mVal << "\";";
<< name_ << "=\"" << val_ << "\";";
}
private:
std::string mName;
std::string mVal;
std::string name_;
std::string val_;
DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
@@ -106,20 +107,20 @@ template <typename T>
class PrimitiveArrayMember : public ClassMember {
public:
explicit PrimitiveArrayMember(const StringPiece& name)
: mName(name.toString()) {}
: name_(name.ToString()) {}
void addElement(const T& val) { mElements.push_back(val); }
void AddElement(const T& val) { elements_.push_back(val); }
bool empty() const override { return false; }
void writeToStream(const StringPiece& prefix, bool final,
void WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const override {
ClassMember::writeToStream(prefix, final, out);
ClassMember::WriteToStream(prefix, final, out);
*out << prefix << "public static final int[] " << mName << "={";
*out << prefix << "public static final int[] " << name_ << "={";
const auto begin = mElements.begin();
const auto end = mElements.end();
const auto begin = elements_.begin();
const auto end = elements_.end();
for (auto current = begin; current != end; ++current) {
if (std::distance(begin, current) % kAttribsPerLine == 0) {
*out << "\n" << prefix << kIndent << kIndent;
@@ -134,8 +135,8 @@ class PrimitiveArrayMember : public ClassMember {
}
private:
std::string mName;
std::vector<T> mElements;
std::string name_;
std::vector<T> elements_;
DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
};
@@ -146,29 +147,29 @@ enum class ClassQualifier { None, Static };
class ClassDefinition : public ClassMember {
public:
static bool writeJavaFile(const ClassDefinition* def,
static bool WriteJavaFile(const ClassDefinition* def,
const StringPiece& package, bool final,
std::ostream* out);
ClassDefinition(const StringPiece& name, ClassQualifier qualifier,
bool createIfEmpty)
: mName(name.toString()),
mQualifier(qualifier),
mCreateIfEmpty(createIfEmpty) {}
: name_(name.ToString()),
qualifier_(qualifier),
create_if_empty_(createIfEmpty) {}
void addMember(std::unique_ptr<ClassMember> member) {
mMembers.push_back(std::move(member));
void AddMember(std::unique_ptr<ClassMember> member) {
members_.push_back(std::move(member));
}
bool empty() const override;
void writeToStream(const StringPiece& prefix, bool final,
void WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const override;
private:
std::string mName;
ClassQualifier mQualifier;
bool mCreateIfEmpty;
std::vector<std::unique_ptr<ClassMember>> mMembers;
std::string name_;
ClassQualifier qualifier_;
bool create_if_empty_;
std::vector<std::unique_ptr<ClassMember>> members_;
DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
};

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More