AAPT2: Better debugging output
Test: make aapt2_tests
Change-Id: I7778b773201381538dc1f2e376abee4eb33e44c0
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index ae67f61..eb3a99a 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -113,6 +113,7 @@
"optimize/VersionCollapser.cpp",
"process/SymbolTable.cpp",
"split/TableSplitter.cpp",
+ "text/Printer.cpp",
"text/Unicode.cpp",
"text/Utf8Iterator.cpp",
"util/BigBuffer.cpp",
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 59a6e12..f621660 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -882,6 +882,11 @@
return std::string(locale);
}
+std::string ConfigDescription::to_string() const {
+ const android::String8 str = toString();
+ return std::string(str.string(), str.size());
+}
+
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
if (*this == o) {
return true;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index c1d0e10..f719552 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -64,6 +64,8 @@
// Returns the BCP-47 language tag of this configuration's locale.
std::string GetBcp47LanguageTag(bool canonicalize = false) const;
+ std::string to_string() const;
+
/**
* A configuration X dominates another configuration Y, if X has at least the
* precedence of Y and X is strictly more general than Y: for any type defined
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 61c304b..08efc27 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -17,7 +17,6 @@
#include "Debug.h"
#include <algorithm>
-#include <iostream>
#include <map>
#include <memory>
#include <queue>
@@ -25,120 +24,244 @@
#include <vector>
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
+#include "text/Printer.h"
#include "util/Util.h"
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
+
namespace aapt {
namespace {
-class PrintVisitor : public ConstValueVisitor {
+class ValueHeadlinePrinter : public ConstValueVisitor {
public:
using ConstValueVisitor::Visit;
+ explicit ValueHeadlinePrinter(const std::string& package, Printer* printer)
+ : package_(package), printer_(printer) {
+ }
+
void Visit(const Attribute* attr) override {
- std::cout << "(attr) type=";
- attr->PrintMask(&std::cout);
- static constexpr uint32_t kMask =
- android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
+ printer_->Print("(attr) type=");
+ printer_->Print(attr->MaskString());
+ if (!attr->symbols.empty()) {
+ printer_->Print(StringPrintf(" size=%zd", attr->symbols.size()));
+ }
+ }
+
+ void Visit(const Style* style) override {
+ printer_->Print(StringPrintf("(style) size=%zd", style->entries.size()));
+ if (style->parent) {
+ printer_->Print(" parent=");
+
+ const Reference& parent_ref = style->parent.value();
+ if (parent_ref.name) {
+ if (parent_ref.private_reference) {
+ printer_->Print("*");
+ }
+
+ const ResourceName& parent_name = parent_ref.name.value();
+ if (package_ != parent_name.package) {
+ printer_->Print(parent_name.package);
+ printer_->Print(":");
+ }
+ printer_->Print(to_string(parent_name.type));
+ printer_->Print("/");
+ printer_->Print(parent_name.entry);
+ if (parent_ref.id) {
+ printer_->Print(" (");
+ printer_->Print(parent_ref.id.value().to_string());
+ printer_->Print(")");
+ }
+ } else if (parent_ref.id) {
+ printer_->Print(parent_ref.id.value().to_string());
+ } else {
+ printer_->Print("???");
+ }
+ }
+ }
+
+ void Visit(const Array* array) override {
+ printer_->Print(StringPrintf("(array) size=%zd", array->elements.size()));
+ }
+
+ void Visit(const Plural* plural) override {
+ size_t count = std::count_if(plural->values.begin(), plural->values.end(),
+ [](const std::unique_ptr<Item>& v) { return v != nullptr; });
+ printer_->Print(StringPrintf("(plurals) size=%zd", count));
+ }
+
+ void Visit(const Styleable* styleable) override {
+ printer_->Println(StringPrintf("(styleable) size=%zd", styleable->entries.size()));
+ }
+
+ void VisitItem(const Item* item) override {
+ // Pretty much guaranteed to be one line.
+ if (const Reference* ref = ValueCast<Reference>(item)) {
+ // Special case Reference so that we can print local resources without a package name.
+ ref->PrettyPrint(package_, printer_);
+ } else {
+ item->PrettyPrint(printer_);
+ }
+ }
+
+ private:
+ std::string package_;
+ Printer* printer_;
+};
+
+class ValueBodyPrinter : public ConstValueVisitor {
+ public:
+ using ConstValueVisitor::Visit;
+
+ explicit ValueBodyPrinter(const std::string& package, Printer* printer)
+ : package_(package), printer_(printer) {
+ }
+
+ void Visit(const Attribute* attr) override {
+ constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
if (attr->type_mask & kMask) {
for (const auto& symbol : attr->symbols) {
- std::cout << "\n " << symbol.symbol.name.value().entry;
+ printer_->Print(symbol.symbol.name.value().entry);
if (symbol.symbol.id) {
- std::cout << " (" << symbol.symbol.id.value() << ")";
+ printer_->Print("(");
+ printer_->Print(symbol.symbol.id.value().to_string());
+ printer_->Print(")");
}
- std::cout << " = " << symbol.value;
+ printer_->Println(StringPrintf("=0x%08x", symbol.value));
}
}
}
void Visit(const Style* style) override {
- std::cout << "(style)";
- if (style->parent) {
- const Reference& parent_ref = style->parent.value();
- std::cout << " parent=";
- if (parent_ref.name) {
- if (parent_ref.private_reference) {
- std::cout << "*";
- }
- std::cout << parent_ref.name.value() << " ";
- }
-
- if (parent_ref.id) {
- std::cout << parent_ref.id.value();
- }
- }
-
for (const auto& entry : style->entries) {
- std::cout << "\n ";
if (entry.key.name) {
const ResourceName& name = entry.key.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
+ if (!name.package.empty() && name.package != package_) {
+ printer_->Print(name.package);
+ printer_->Print(":");
}
- std::cout << name.entry;
+ printer_->Print(name.entry);
+
+ if (entry.key.id) {
+ printer_->Print("(");
+ printer_->Print(entry.key.id.value().to_string());
+ printer_->Print(")");
+ }
+ } else if (entry.key.id) {
+ printer_->Print(entry.key.id.value().to_string());
+ } else {
+ printer_->Print("???");
}
- if (entry.key.id) {
- std::cout << "(" << entry.key.id.value() << ")";
- }
-
- std::cout << "=" << *entry.value;
+ printer_->Print("=");
+ PrintItem(*entry.value);
+ printer_->Println();
}
}
void Visit(const Array* array) override {
- array->Print(&std::cout);
+ const size_t count = array->elements.size();
+ printer_->Print("[");
+ if (count > 0) {
+ for (size_t i = 0u; i < count; i++) {
+ if (i != 0u && i % 4u == 0u) {
+ printer_->Println();
+ printer_->Print(" ");
+ }
+ PrintItem(*array->elements[i]);
+ if (i != count - 1) {
+ printer_->Print(", ");
+ }
+ }
+ printer_->Println("]");
+ }
}
void Visit(const Plural* plural) override {
- plural->Print(&std::cout);
+ constexpr std::array<const char*, Plural::Count> kPluralNames = {
+ {"zero", "one", "two", "few", "many", "other"}};
+
+ for (size_t i = 0; i < Plural::Count; i++) {
+ if (plural->values[i] != nullptr) {
+ printer_->Print(StringPrintf("%s=", kPluralNames[i]));
+ PrintItem(*plural->values[i]);
+ printer_->Println();
+ }
+ }
}
void Visit(const Styleable* styleable) override {
- std::cout << "(styleable)";
for (const auto& attr : styleable->entries) {
- std::cout << "\n ";
if (attr.name) {
const ResourceName& name = attr.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
+ if (!name.package.empty() && name.package != package_) {
+ printer_->Print(name.package);
+ printer_->Print(":");
}
- std::cout << name.entry;
+ printer_->Print(name.entry);
+
+ if (attr.id) {
+ printer_->Print("(");
+ printer_->Print(attr.id.value().to_string());
+ printer_->Print(")");
+ }
}
if (attr.id) {
- std::cout << "(" << attr.id.value() << ")";
+ printer_->Print(attr.id.value().to_string());
}
}
}
void VisitItem(const Item* item) override {
- item->Print(&std::cout);
+ // Intentionally left empty, we already printed the Items.
}
+
+ private:
+ void PrintItem(const Item& item) {
+ if (const Reference* ref = ValueCast<Reference>(&item)) {
+ // Special case Reference so that we can print local resources without a package name.
+ ref->PrettyPrint(package_, printer_);
+ } else {
+ item.PrettyPrint(printer_);
+ }
+ }
+
+ std::string package_;
+ Printer* printer_;
};
} // namespace
-void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options) {
- PrintVisitor visitor;
-
+void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
+ Printer* printer) {
for (const auto& package : table.packages) {
- std::cout << "Package name=" << package->name;
- if (package->id) {
- std::cout << " id=" << std::hex << (int)package->id.value() << std::dec;
- }
- std::cout << std::endl;
+ ValueHeadlinePrinter headline_printer(package->name, printer);
+ ValueBodyPrinter body_printer(package->name, printer);
+ printer->Print("Package name=");
+ printer->Print(package->name);
+ if (package->id) {
+ printer->Print(StringPrintf(" id=%02x", package->id.value()));
+ }
+ printer->Println();
+
+ printer->Indent();
for (const auto& type : package->types) {
- std::cout << "\n type " << type->type;
+ printer->Print("type ");
+ printer->Print(to_string(type->type));
if (type->id) {
- std::cout << " id=" << std::hex << (int)type->id.value() << std::dec;
+ printer->Print(StringPrintf(" id=%02x", type->id.value()));
}
- std::cout << " entryCount=" << type->entries.size() << std::endl;
+ printer->Println(StringPrintf(" entryCount=%zd", type->entries.size()));
std::vector<const ResourceEntry*> sorted_entries;
for (const auto& entry : type->entries) {
@@ -156,35 +279,54 @@
sorted_entries.insert(iter, entry.get());
}
+ printer->Indent();
for (const ResourceEntry* entry : sorted_entries) {
const ResourceId id(package->id.value_or_default(0), type->id.value_or_default(0),
entry->id.value_or_default(0));
- const ResourceName name(package->name, type->type, entry->name);
- std::cout << " spec resource " << id << " " << name;
+ printer->Print("resource ");
+ printer->Print(id.to_string());
+ printer->Print(" ");
+
+ // Write the name without the package (this is obvious and too verbose).
+ printer->Print(to_string(type->type));
+ printer->Print("/");
+ printer->Print(entry->name);
+
switch (entry->symbol_status.state) {
case SymbolState::kPublic:
- std::cout << " PUBLIC";
+ printer->Print(" PUBLIC");
break;
case SymbolState::kPrivate:
- std::cout << " _PRIVATE_";
+ printer->Print(" _PRIVATE_");
break;
- default:
+ case SymbolState::kUndefined:
+ // Print nothing.
break;
}
- std::cout << std::endl;
+ printer->Println();
+ printer->Indent();
for (const auto& value : entry->values) {
- std::cout << " (" << value->config << ") ";
- value->value->Accept(&visitor);
+ printer->Print("(");
+ printer->Print(value->config.to_string());
+ printer->Print(") ");
+ value->value->Accept(&headline_printer);
if (options.show_sources && !value->value->GetSource().path.empty()) {
- std::cout << " src=" << value->value->GetSource();
+ printer->Print(" src=");
+ printer->Print(value->value->GetSource().to_string());
}
- std::cout << std::endl;
+ printer->Println();
+ printer->Indent();
+ value->value->Accept(&body_printer);
+ printer->Undent();
}
+ printer->Undent();
}
+ printer->Undent();
}
+ printer->Undent();
}
}
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 296d04b..3c1ee4c 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -22,6 +22,7 @@
#include "Resource.h"
#include "ResourceTable.h"
+#include "text/Printer.h"
#include "xml/XmlDom.h"
namespace aapt {
@@ -31,9 +32,9 @@
};
struct Debug {
- static void PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options = {});
- static void PrintStyleGraph(ResourceTable* table,
- const ResourceName& target_style);
+ static void PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
+ text::Printer* printer);
+ static void PrintStyleGraph(ResourceTable* table, const ResourceName& target_style);
static void DumpHex(const void* data, size_t len);
static void DumpXml(const xml::XmlResource& doc);
};
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index a9f5f29..b78f48c 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -17,13 +17,34 @@
#include "Resource.h"
#include <map>
+#include <sstream>
#include <string>
-using android::StringPiece;
+#include "android-base/stringprintf.h"
+
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
-StringPiece ToString(ResourceType type) {
+std::string ResourceId::to_string() const {
+ return StringPrintf("0x%08x", id);
+}
+
+std::string ResourceName::to_string() const {
+ return ResourceNameRef(*this).to_string();
+}
+
+std::string ResourceNameRef::to_string() const {
+ std::ostringstream str_stream;
+ if (!package.empty()) {
+ str_stream << package << ":";
+ }
+ str_stream << type << "/" << entry;
+ return str_stream.str();
+}
+
+StringPiece to_string(ResourceType type) {
switch (type) {
case ResourceType::kAnim:
return "anim";
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 87b9867..96a0203 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -69,11 +69,10 @@
kXml,
};
-android::StringPiece ToString(ResourceType type);
+android::StringPiece to_string(ResourceType type);
/**
- * Returns a pointer to a valid ResourceType, or nullptr if
- * the string was invalid.
+ * Returns a pointer to a valid ResourceType, or nullptr if the string was invalid.
*/
const ResourceType* ParseResourceType(const android::StringPiece& str);
@@ -92,7 +91,7 @@
int compare(const ResourceName& other) const;
bool is_valid() const;
- std::string ToString() const;
+ std::string to_string() const;
};
/**
@@ -115,8 +114,10 @@
ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
ResourceNameRef& operator=(const ResourceName& rhs);
- ResourceName ToResourceName() const;
bool is_valid() const;
+
+ ResourceName ToResourceName() const;
+ std::string to_string() const;
};
constexpr const uint8_t kAppPackageId = 0x7fu;
@@ -149,6 +150,8 @@
uint8_t package_id() const;
uint8_t type_id() const;
uint16_t entry_id() const;
+
+ std::string to_string() const;
};
struct SourcedResourceName {
@@ -229,7 +232,9 @@
return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
-inline bool ResourceId::is_valid_dynamic() const { return (id & 0x00ff0000u) != 0; }
+inline bool ResourceId::is_valid_dynamic() const {
+ return (id & 0x00ff0000u) != 0;
+}
inline uint8_t ResourceId::package_id() const {
return static_cast<uint8_t>(id >> 24);
@@ -259,24 +264,16 @@
return lhs.id != rhs.id;
}
-inline ::std::ostream& operator<<(::std::ostream& out,
- 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
- << res_id.id;
- out.flags(old_flags);
- out.fill(old_fill);
- return out;
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceId& res_id) {
+ return out << res_id.to_string();
}
//
// ResourceType implementation.
//
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ResourceType& val) {
- return out << ToString(val);
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) {
+ return out << to_string(val);
}
//
@@ -315,18 +312,8 @@
std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ResourceName& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
-}
-
-inline std::string ResourceName::ToString() const {
- std::stringstream stream;
- stream << *this;
- return stream.str();
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) {
+ return out << name.to_string();
}
//
@@ -370,12 +357,8 @@
std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ResourceNameRef& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNameRef& name) {
+ return out << name.to_string();
}
inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
@@ -386,8 +369,7 @@
return ResourceNameRef(lhs) != rhs;
}
-inline bool operator==(const SourcedResourceName& lhs,
- const SourcedResourceName& rhs) {
+inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName& rhs) {
return lhs.name == rhs.name && lhs.line == rhs.line;
}
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 082fd86..b38d259 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -17,9 +17,12 @@
#include "ResourceValues.h"
#include <algorithm>
+#include <cinttypes>
#include <limits>
#include <set>
+#include <sstream>
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "Resource.h"
@@ -27,8 +30,18 @@
#include "ValueVisitor.h"
#include "util/Util.h"
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
+
namespace aapt {
+void Value::PrettyPrint(Printer* printer) const {
+ std::ostringstream str_stream;
+ Print(&str_stream);
+ printer->Print(str_stream.str());
+}
+
std::ostream& operator<<(std::ostream& out, const Value& value) {
value.Print(&out);
return out;
@@ -155,6 +168,49 @@
}
}
+static void PrettyPrintReferenceImpl(const Reference& ref, bool print_package, Printer* printer) {
+ switch (ref.reference_type) {
+ case Reference::Type::kResource:
+ printer->Print("@");
+ break;
+
+ case Reference::Type::kAttribute:
+ printer->Print("?");
+ break;
+ }
+
+ if (!ref.name && !ref.id) {
+ printer->Print("null");
+ return;
+ }
+
+ if (ref.private_reference) {
+ printer->Print("*");
+ }
+
+ if (ref.name) {
+ const ResourceName& name = ref.name.value();
+ if (print_package) {
+ printer->Print(name.to_string());
+ } else {
+ printer->Print(to_string(name.type));
+ printer->Print("/");
+ printer->Print(name.entry);
+ }
+ } else if (ref.id && ref.id.value().is_valid_dynamic()) {
+ printer->Print(ref.id.value().to_string());
+ }
+}
+
+void Reference::PrettyPrint(Printer* printer) const {
+ PrettyPrintReferenceImpl(*this, true /*print_package*/, printer);
+}
+
+void Reference::PrettyPrint(const StringPiece& package, Printer* printer) const {
+ const bool print_package = name ? package != name.value().package : true;
+ PrettyPrintReferenceImpl(*this, print_package, printer);
+}
+
bool Id::Equals(const Value* value) const {
return ValueCast<Id>(value) != nullptr;
}
@@ -165,11 +221,16 @@
return true;
}
-Id* Id::Clone(StringPool* /*new_pool*/) 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) {}
+String::String(const StringPool::Ref& ref) : value(ref) {
+}
bool String::Equals(const Value* value) const {
const String* other = ValueCast<String>(value);
@@ -218,7 +279,14 @@
*out << "(string) \"" << *value << "\"";
}
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {}
+void String::PrettyPrint(Printer* printer) const {
+ printer->Print("\"");
+ printer->Print(*value);
+ printer->Print("\"");
+}
+
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
+}
bool StyledString::Equals(const Value* value) const {
const StyledString* other = ValueCast<StyledString>(value);
@@ -269,7 +337,8 @@
}
}
-FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {}
+FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
+}
bool FileReference::Equals(const Value* value) const {
const FileReference* other = ValueCast<FileReference>(value);
@@ -302,7 +371,8 @@
*out << "(file) " << *path;
}
-BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {}
+BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
+}
BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
value.dataType = dataType;
@@ -318,7 +388,7 @@
this->value.data == other->value.data;
}
-bool BinaryPrimitive::Flatten(android::Res_value* out_value) const {
+bool BinaryPrimitive::Flatten(::android::Res_value* out_value) const {
out_value->dataType = value.dataType;
out_value->data = util::HostToDevice32(value.data);
return true;
@@ -329,32 +399,110 @@
}
void BinaryPrimitive::Print(std::ostream* out) const {
+ *out << StringPrintf("(primitive) type=0x%02x data=0x%08x", value.dataType, value.data);
+}
+
+static std::string ComplexToString(uint32_t complex_value, bool fraction) {
+ using ::android::Res_value;
+
+ constexpr std::array<int, 4> kRadixShifts = {{23, 16, 8, 0}};
+
+ // Determine the radix that was used.
+ const uint32_t radix =
+ (complex_value >> Res_value::COMPLEX_RADIX_SHIFT) & Res_value::COMPLEX_RADIX_MASK;
+ const uint64_t mantissa = uint64_t{(complex_value >> Res_value::COMPLEX_MANTISSA_SHIFT) &
+ Res_value::COMPLEX_MANTISSA_MASK}
+ << kRadixShifts[radix];
+ const float value = mantissa * (1.0f / (1 << 23));
+
+ std::string str = StringPrintf("%f", value);
+
+ const int unit_type =
+ (complex_value >> Res_value::COMPLEX_UNIT_SHIFT) & Res_value::COMPLEX_UNIT_MASK;
+ if (fraction) {
+ switch (unit_type) {
+ case Res_value::COMPLEX_UNIT_FRACTION:
+ str += "%";
+ break;
+ case Res_value::COMPLEX_UNIT_FRACTION_PARENT:
+ str += "%p";
+ break;
+ default:
+ str += "???";
+ break;
+ }
+ } else {
+ switch (unit_type) {
+ case Res_value::COMPLEX_UNIT_PX:
+ str += "px";
+ break;
+ case Res_value::COMPLEX_UNIT_DIP:
+ str += "dp";
+ break;
+ case Res_value::COMPLEX_UNIT_SP:
+ str += "sp";
+ break;
+ case Res_value::COMPLEX_UNIT_PT:
+ str += "pt";
+ break;
+ case Res_value::COMPLEX_UNIT_IN:
+ str += "in";
+ break;
+ case Res_value::COMPLEX_UNIT_MM:
+ str += "mm";
+ break;
+ default:
+ str += "???";
+ break;
+ }
+ }
+ return str;
+}
+
+void BinaryPrimitive::PrettyPrint(Printer* printer) const {
+ using ::android::Res_value;
switch (value.dataType) {
- case android::Res_value::TYPE_NULL:
- if (value.data == android::Res_value::DATA_NULL_EMPTY) {
- *out << "(empty)";
+ case Res_value::TYPE_NULL:
+ if (value.data == Res_value::DATA_NULL_EMPTY) {
+ printer->Print("@empty");
} else {
- *out << "(null)";
+ printer->Print("@null");
}
break;
- case android::Res_value::TYPE_INT_DEC:
- *out << "(integer) " << static_cast<int32_t>(value.data);
+
+ case Res_value::TYPE_INT_DEC:
+ printer->Print(StringPrintf("%" PRIi32, static_cast<int32_t>(value.data)));
break;
- case android::Res_value::TYPE_INT_HEX:
- *out << "(integer) 0x" << std::hex << value.data << std::dec;
+
+ case Res_value::TYPE_INT_HEX:
+ printer->Print(StringPrintf("0x%08x", value.data));
break;
- case android::Res_value::TYPE_INT_BOOLEAN:
- *out << "(boolean) " << (value.data != 0 ? "true" : "false");
+
+ case Res_value::TYPE_INT_BOOLEAN:
+ printer->Print(value.data != 0 ? "true" : "false");
break;
- case android::Res_value::TYPE_INT_COLOR_ARGB8:
- case android::Res_value::TYPE_INT_COLOR_RGB8:
- case android::Res_value::TYPE_INT_COLOR_ARGB4:
- case android::Res_value::TYPE_INT_COLOR_RGB4:
- *out << "(color) #" << std::hex << value.data << std::dec;
+
+ case Res_value::TYPE_INT_COLOR_ARGB8:
+ case Res_value::TYPE_INT_COLOR_RGB8:
+ case Res_value::TYPE_INT_COLOR_ARGB4:
+ case Res_value::TYPE_INT_COLOR_RGB4:
+ printer->Print(StringPrintf("#%08x", value.data));
break;
+
+ case Res_value::TYPE_FLOAT:
+ printer->Print(StringPrintf("%g", *reinterpret_cast<const float*>(&value.data)));
+ break;
+
+ case Res_value::TYPE_DIMENSION:
+ printer->Print(ComplexToString(value.data, false /*fraction*/));
+ break;
+
+ case Res_value::TYPE_FRACTION:
+ printer->Print(ComplexToString(value.data, true /*fraction*/));
+ break;
+
default:
- *out << "(unknown 0x" << std::hex << (int)value.dataType << ") 0x"
- << std::hex << value.data << std::dec;
+ printer->Print(StringPrintf("(unknown 0x%02x) 0x%08x", value.dataType, value.data));
break;
}
}
@@ -424,107 +572,107 @@
return new Attribute(*this);
}
-void Attribute::PrintMask(std::ostream* out) const {
+std::string Attribute::MaskString() const {
if (type_mask == android::ResTable_map::TYPE_ANY) {
- *out << "any";
- return;
+ return "any";
}
+ std::ostringstream out;
bool set = false;
if ((type_mask & android::ResTable_map::TYPE_REFERENCE) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "reference";
+ out << "reference";
}
if ((type_mask & android::ResTable_map::TYPE_STRING) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "string";
+ out << "string";
}
if ((type_mask & android::ResTable_map::TYPE_INTEGER) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "integer";
+ out << "integer";
}
if ((type_mask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "boolean";
+ out << "boolean";
}
if ((type_mask & android::ResTable_map::TYPE_COLOR) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "color";
+ out << "color";
}
if ((type_mask & android::ResTable_map::TYPE_FLOAT) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "float";
+ out << "float";
}
if ((type_mask & android::ResTable_map::TYPE_DIMENSION) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "dimension";
+ out << "dimension";
}
if ((type_mask & android::ResTable_map::TYPE_FRACTION) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "fraction";
+ out << "fraction";
}
if ((type_mask & android::ResTable_map::TYPE_ENUM) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "enum";
+ out << "enum";
}
if ((type_mask & android::ResTable_map::TYPE_FLAGS) != 0) {
if (!set) {
set = true;
} else {
- *out << "|";
+ out << "|";
}
- *out << "flags";
+ out << "flags";
}
+ return out.str();
}
void Attribute::Print(std::ostream* out) const {
- *out << "(attr) ";
- PrintMask(out);
+ *out << "(attr) " << MaskString();
if (!symbols.empty()) {
*out << " [" << util::Joiner(symbols, ", ") << "]";
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index fd242a1..b2ec8bdd 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -29,6 +29,7 @@
#include "Resource.h"
#include "StringPool.h"
#include "io/File.h"
+#include "text/Printer.h"
#include "util/Maybe.h"
namespace aapt {
@@ -106,6 +107,10 @@
// Human readable printout of this value.
virtual void Print(std::ostream* out) const = 0;
+ // Human readable printout of this value that may omit some information for the sake
+ // of brevity and readability. Default implementation just calls Print().
+ virtual void PrettyPrint(text::Printer* printer) const;
+
friend std::ostream& operator<<(std::ostream& out, const Value& value);
protected:
@@ -162,6 +167,10 @@
bool Flatten(android::Res_value* out_value) const override;
Reference* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
+ void PrettyPrint(text::Printer* printer) const override;
+
+ // Prints the reference without a package name if the package name matches the one given.
+ void PrettyPrint(const android::StringPiece& package, text::Printer* printer) const;
};
bool operator<(const Reference&, const Reference&);
@@ -224,6 +233,7 @@
bool Flatten(android::Res_value* out_value) const override;
String* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
+ void PrettyPrint(text::Printer* printer) const override;
};
struct StyledString : public BaseItem<StyledString> {
@@ -274,6 +284,7 @@
bool Flatten(android::Res_value* out_value) const override;
BinaryPrimitive* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
+ void PrettyPrint(text::Printer* printer) const override;
};
struct Attribute : public BaseValue<Attribute> {
@@ -294,7 +305,7 @@
bool Equals(const Value* value) const override;
Attribute* Clone(StringPool* new_pool) const override;
- void PrintMask(std::ostream* out) const;
+ std::string MaskString() const;
void Print(std::ostream* out) const override;
bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
};
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index d7f2a66..0f312d6 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -20,16 +20,14 @@
#include <ostream>
#include <string>
+#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
#include "util/Maybe.h"
namespace aapt {
-/**
- * Represents a file on disk. Used for logging and
- * showing errors.
- */
+// Represents a file on disk. Used for logging and showing errors.
struct Source {
std::string path;
Maybe<size_t> line;
@@ -42,7 +40,16 @@
inline Source(const android::StringPiece& path, size_t line)
: path(path.to_string()), line(line) {}
- inline Source WithLine(size_t line) const { return Source(path, line); }
+ inline Source WithLine(size_t line) const {
+ return Source(path, line);
+ }
+
+ std::string to_string() const {
+ if (line) {
+ return ::android::base::StringPrintf("%s:%zd", path.c_str(), line.value());
+ }
+ return path;
+ }
};
//
@@ -50,11 +57,7 @@
//
inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
- out << source.path;
- if (source.line) {
- out << ":" << source.line.value();
- }
- return out;
+ return out << source.to_string();
}
inline bool operator==(const Source& lhs, const Source& rhs) {
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index bc8f1dc..bc7f5a8 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+#include <cinttypes>
#include <vector>
+#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
#include "Debug.h"
@@ -27,9 +29,12 @@
#include "io/FileStream.h"
#include "io/ZipArchive.h"
#include "process/IResourceTableConsumer.h"
+#include "text/Printer.h"
#include "util/Files.h"
+using ::aapt::text::Printer;
using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -48,16 +53,28 @@
}
static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
- size_t len) {
- std::cout << "Resource: " << file.name << "\n"
- << "Config: " << file.config << "\n"
- << "Source: " << file.source << "\n"
- << "Type: " << ResourceFileTypeToString(file.type) << "\n"
- << "DataOff: " << offset << "\n"
- << "DataLen: " << len << "\n";
+ size_t len, Printer* printer) {
+ printer->Print("Resource: ");
+ printer->Println(file.name.to_string());
+
+ printer->Print("Config: ");
+ printer->Println(file.config.to_string());
+
+ printer->Print("Source: ");
+ printer->Println(file.source.to_string());
+
+ printer->Print("Type: ");
+ printer->Println(ResourceFileTypeToString(file.type));
+
+ printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
}
static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
+ // Use a smaller buffer so that there is less latency for dumping to stdout.
+ constexpr size_t kStdOutBufferSize = 1024u;
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ Printer printer(&fout);
+
DebugPrintTableOptions print_options;
print_options.show_sources = true;
@@ -83,6 +100,8 @@
<< "failed to parse table: " << err);
return false;
}
+
+ printer.Println("Proto APK");
} else if (io::IFile* file = zip->FindFile("resources.arsc")) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
@@ -95,9 +114,11 @@
if (!parser.Parse()) {
return false;
}
+
+ printer.Println("Binary APK");
}
- Debug::PrintTable(table, print_options);
+ Debug::PrintTable(table, print_options, &printer);
return true;
}
@@ -118,9 +139,12 @@
return false;
}
+ printer.Println("AAPT2 Container (APC)");
ContainerReaderEntry* entry;
while ((entry = reader.Next()) != nullptr) {
if (entry->Type() == ContainerEntryType::kResTable) {
+ printer.Println("kResTable");
+
pb::ResourceTable pb_table;
if (!entry->GetResTable(&pb_table)) {
context->GetDiagnostics()->Error(DiagMessage(file_path)
@@ -136,8 +160,11 @@
continue;
}
- Debug::PrintTable(table, print_options);
+ printer.Indent();
+ Debug::PrintTable(table, print_options, &printer);
+ printer.Undent();
} else if (entry->Type() == ContainerEntryType::kResFile) {
+ printer.Println("kResFile");
pb::internal::CompiledFile pb_compiled_file;
off64_t offset;
size_t length;
@@ -155,7 +182,9 @@
continue;
}
- DumpCompiledFile(file, Source(file_path), offset, length);
+ printer.Indent();
+ DumpCompiledFile(file, Source(file_path), offset, length, &printer);
+ printer.Undent();
}
}
return true;
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 8b6c524..238e339 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -164,7 +164,7 @@
// Add the inline attribute to the parent.
parent_el->attributes.push_back(xml::Attribute{decl.attr_namespace_uri, decl.attr_name,
- "@" + new_doc->file.name.ToString()});
+ "@" + new_doc->file.name.to_string()});
// Delete the subtree.
for (auto iter = parent_el->children.begin(); iter != parent_el->children.end(); ++iter) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 66510b0..5078678 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -424,7 +424,7 @@
// We can ignore the value here.
return util::make_unique<Id>();
default:
- diag_->Error(DiagMessage() << "illegal map type '" << ToString(name.type) << "' ("
+ diag_->Error(DiagMessage() << "illegal map type '" << to_string(name.type) << "' ("
<< (int)name.type << ")");
break;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 259f2e9..4a1b46c 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -472,7 +472,7 @@
expected_type_id++;
}
expected_type_id++;
- type_pool_.MakeRef(ToString(type->type));
+ type_pool_.MakeRef(to_string(type->type));
std::vector<ResourceEntry*> sorted_entries = CollectAndSortEntries(type);
if (sorted_entries.empty()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 1d184fe..97ce01a 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -284,7 +284,7 @@
if (type->id) {
pb_type->mutable_type_id()->set_id(type->id.value());
}
- pb_type->set_name(ToString(type->type).to_string());
+ pb_type->set_name(to_string(type->type).to_string());
for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
pb::Entry* pb_entry = pb_type->add_entry();
@@ -328,7 +328,7 @@
pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
if (ref.name) {
- pb_ref->set_name(ref.name.value().ToString());
+ pb_ref->set_name(ref.name.value().to_string());
}
pb_ref->set_private_(ref.private_reference);
@@ -523,14 +523,14 @@
}
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
- out_file->set_resource_name(file.name.ToString());
+ out_file->set_resource_name(file.name.to_string());
out_file->set_source_path(file.source.path);
out_file->set_type(SerializeFileReferenceTypeToPb(file.type));
SerializeConfig(file.config, out_file->mutable_config());
for (const SourcedResourceName& exported : file.exported_symbols) {
pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
- pb_symbol->set_resource_name(exported.name.ToString());
+ pb_symbol->set_resource_name(exported.name.to_string());
pb_symbol->mutable_source()->set_line_number(exported.line);
}
}
diff --git a/tools/aapt2/io/FileStream.cpp b/tools/aapt2/io/FileStream.cpp
index 2f7a4b3..4ff6d78 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -26,6 +26,7 @@
#include "android-base/utf8.h"
using ::android::base::SystemErrorCodeToString;
+using ::android::base::unique_fd;
namespace aapt {
namespace io {
@@ -100,7 +101,13 @@
}
FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
- : FileOutputStream(::android::base::utf8::open(path.c_str(), mode), buffer_capacity) {
+ : FileOutputStream(unique_fd(::android::base::utf8::open(path.c_str(), mode)),
+ buffer_capacity) {
+}
+
+FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
+ : FileOutputStream(fd.get(), buffer_capacity) {
+ owned_fd_ = std::move(fd);
}
FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
@@ -118,7 +125,7 @@
}
bool FileOutputStream::Next(void** data, size_t* size) {
- if (fd_ == -1 || HadError()) {
+ if (HadError()) {
return false;
}
@@ -159,7 +166,8 @@
ssize_t n = TEMP_FAILURE_RETRY(write(fd_, buffer_.get(), buffer_offset_));
if (n < 0) {
error_ = SystemErrorCodeToString(errno);
- fd_.reset();
+ owned_fd_.reset();
+ fd_ = -1;
buffer_.reset();
return false;
}
diff --git a/tools/aapt2/io/FileStream.h b/tools/aapt2/io/FileStream.h
index 3b07667..4ed1ad5 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -29,12 +29,15 @@
namespace aapt {
namespace io {
+constexpr size_t kDefaultBufferCapacity = 4096u;
+
class FileInputStream : public InputStream {
public:
- explicit FileInputStream(const std::string& path, size_t buffer_capacity = 4096);
+ explicit FileInputStream(const std::string& path,
+ size_t buffer_capacity = kDefaultBufferCapacity);
- // Takes ownership of `fd`.
- explicit FileInputStream(int fd, size_t buffer_capacity = 4096);
+ // Take ownership of `fd`.
+ explicit FileInputStream(int fd, size_t buffer_capacity = kDefaultBufferCapacity);
bool Next(const void** data, size_t* size) override;
@@ -61,10 +64,14 @@
class FileOutputStream : public OutputStream {
public:
explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
- size_t buffer_capacity = 4096);
+ size_t buffer_capacity = kDefaultBufferCapacity);
+
+ // Does not take ownership of `fd`.
+ explicit FileOutputStream(int fd, size_t buffer_capacity = kDefaultBufferCapacity);
// Takes ownership of `fd`.
- explicit FileOutputStream(int fd, size_t buffer_capacity = 4096);
+ explicit FileOutputStream(android::base::unique_fd fd,
+ size_t buffer_capacity = kDefaultBufferCapacity);
~FileOutputStream();
@@ -86,7 +93,8 @@
bool FlushImpl();
- android::base::unique_fd fd_;
+ android::base::unique_fd owned_fd_;
+ int fd_;
std::string error_;
std::unique_ptr<uint8_t[]> buffer_;
size_t buffer_capacity_;
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index 68c3cb1..a6d58ca 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -87,10 +87,8 @@
const std::string input = "this is a cool string";
TemporaryFile file;
- int fd = file.release();
- // FileOutputStream takes ownership.
- FileOutputStream out(fd, 10u);
+ FileOutputStream out(file.fd, 10u);
ASSERT_FALSE(out.HadError());
EXPECT_THAT(out.ByteCount(), Eq(0u));
@@ -118,10 +116,10 @@
ASSERT_TRUE(out.Flush());
- lseek64(fd, 0, SEEK_SET);
+ lseek64(file.fd, 0, SEEK_SET);
std::string actual;
- ASSERT_TRUE(android::base::ReadFdToString(fd, &actual));
+ ASSERT_TRUE(android::base::ReadFdToString(file.fd, &actual));
EXPECT_THAT(actual, StrEq(input));
}
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index d270340..7ee1016 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -18,6 +18,7 @@
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+using ::android::StringPiece;
using ::google::protobuf::io::ZeroCopyOutputStream;
namespace aapt {
@@ -93,6 +94,25 @@
return !in->HadError();
}
+bool Copy(OutputStream* out, const StringPiece& in) {
+ const char* in_buffer = in.data();
+ size_t in_len = in.size();
+ while (in_len != 0) {
+ void* out_buffer;
+ size_t out_len;
+ if (!out->Next(&out_buffer, &out_len)) {
+ return false;
+ }
+
+ const size_t bytes_to_copy = in_len < out_len ? in_len : out_len;
+ memcpy(out_buffer, in_buffer, bytes_to_copy);
+ out->BackUp(out_len - bytes_to_copy);
+ in_buffer += bytes_to_copy;
+ in_len -= bytes_to_copy;
+ }
+ return true;
+}
+
bool Copy(ZeroCopyOutputStream* out, InputStream* in) {
OutputStreamAdaptor adaptor(out);
return Copy(&adaptor, in);
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index 1e48508..de2ab39 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -42,6 +42,7 @@
// Copies the data from in to out. Returns false if there was an error.
// If there was an error, check the individual streams' HadError/GetError methods.
bool Copy(OutputStream* out, InputStream* in);
+bool Copy(OutputStream* out, const ::android::StringPiece& in);
bool Copy(::google::protobuf::io::ZeroCopyOutputStream* out, InputStream* in);
class OutputStreamAdaptor : public io::OutputStream {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 3ba4dd8..91cef64 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -461,7 +461,7 @@
}
if (out_rewrite_method != nullptr) {
- const StringPiece& type_str = ToString(name.type);
+ const StringPiece& type_str = to_string(name.type);
out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
type_str.data(), field_name.data(),
type_str.data(), field_name.data()));
@@ -584,7 +584,7 @@
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
- ToString(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+ to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
rewrite_method.get(), out_r_txt)) {
return false;
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 882a85b..2d517c7 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -289,7 +289,7 @@
const android::ResTable& table = assets_.getResources(false);
const std::u16string package16 = util::Utf8ToUtf16(name.package);
- const std::u16string type16 = util::Utf8ToUtf16(ToString(name.type));
+ const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
uint32_t type_spec_flags = 0;
diff --git a/tools/aapt2/text/Printer.cpp b/tools/aapt2/text/Printer.cpp
new file mode 100644
index 0000000..38b3585
--- /dev/null
+++ b/tools/aapt2/text/Printer.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Printer.h"
+
+#include <algorithm>
+
+#include "io/Util.h"
+
+using ::aapt::io::OutputStream;
+using ::android::StringPiece;
+
+namespace aapt {
+namespace text {
+
+void Printer::Println(const StringPiece& str) {
+ Print(str);
+ Print("\n");
+}
+
+void Printer::Println() {
+ Print("\n");
+}
+
+void Printer::Print(const StringPiece& str) {
+ if (error_) {
+ return;
+ }
+
+ auto remaining_str_begin = str.begin();
+ const auto remaining_str_end = str.end();
+ while (remaining_str_end != remaining_str_begin) {
+ // Find the next new-line.
+ const auto new_line_iter = std::find(remaining_str_begin, remaining_str_end, '\n');
+
+ // We will copy the string up until the next new-line (or end of string).
+ const StringPiece str_to_copy = str.substr(remaining_str_begin, new_line_iter);
+ if (!str_to_copy.empty()) {
+ if (needs_indent_) {
+ for (int i = 0; i < indent_level_; i++) {
+ if (!io::Copy(out_, " ")) {
+ error_ = true;
+ return;
+ }
+ }
+ needs_indent_ = false;
+ }
+
+ if (!io::Copy(out_, str_to_copy)) {
+ error_ = true;
+ return;
+ }
+ }
+
+ // If we found a new-line.
+ if (new_line_iter != remaining_str_end) {
+ if (!io::Copy(out_, "\n")) {
+ error_ = true;
+ return;
+ }
+ needs_indent_ = true;
+ // Ok to increment iterator here because we know that the '\n' character is one byte.
+ remaining_str_begin = new_line_iter + 1;
+ } else {
+ remaining_str_begin = new_line_iter;
+ }
+ }
+}
+
+void Printer::Indent() {
+ ++indent_level_;
+}
+
+void Printer::Undent() {
+ --indent_level_;
+}
+
+} // namespace text
+} // namespace aapt
diff --git a/tools/aapt2/text/Printer.h b/tools/aapt2/text/Printer.h
new file mode 100644
index 0000000..94b3c0b
--- /dev/null
+++ b/tools/aapt2/text/Printer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_TEXT_PRINTER_H
+#define AAPT_TEXT_PRINTER_H
+
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
+#include "io/Io.h"
+
+namespace aapt {
+namespace text {
+
+// An indenting Printer that helps write formatted text to the OutputStream.
+class Printer {
+ public:
+ explicit Printer(::aapt::io::OutputStream* out) : out_(out) {
+ }
+
+ void Print(const ::android::StringPiece& str);
+ void Println(const ::android::StringPiece& str);
+ void Println();
+
+ void Indent();
+ void Undent();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Printer);
+
+ ::aapt::io::OutputStream* out_;
+ int indent_level_ = 0;
+ bool needs_indent_ = false;
+ bool error_ = false;
+};
+
+} // namespace text
+} // namespace aapt
+
+#endif // AAPT_TEXT_PRINTER_H
diff --git a/tools/aapt2/text/Printer_test.cpp b/tools/aapt2/text/Printer_test.cpp
new file mode 100644
index 0000000..58beae7
--- /dev/null
+++ b/tools/aapt2/text/Printer_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Printer.h"
+
+#include "io/StringStream.h"
+#include "test/Test.h"
+
+using ::aapt::io::StringOutputStream;
+using ::android::StringPiece;
+using ::testing::StrEq;
+
+namespace aapt {
+namespace text {
+
+TEST(PrinterTest, PrintsToStreamWithIndents) {
+ std::string result;
+ StringOutputStream out(&result);
+ Printer printer(&out);
+
+ printer.Print("Hello");
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello"));
+
+ printer.Println();
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n"));
+
+ // This shouldn't print anything yet.
+ printer.Indent();
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n"));
+
+ // Now we should see the indent.
+ printer.Print("world!");
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n world!"));
+
+ printer.Println(" What a\nlovely day.");
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n world! What a\n lovely day.\n"));
+
+ // This shouldn't print anything yet.
+ printer.Undent();
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n world! What a\n lovely day.\n"));
+
+ printer.Println("Isn't it?");
+ out.Flush();
+ EXPECT_THAT(result, StrEq("Hello\n world! What a\n lovely day.\nIsn't it?\n"));
+}
+
+} // namespace text
+} // namespace aapt