diff options
author | 2017-11-03 15:20:17 -0700 | |
---|---|---|
committer | 2017-11-07 11:21:10 -0800 | |
commit | 93190b79d11d874199cfe7258526a48cfc8399fc (patch) | |
tree | 3a7ae2b44c7f9693d54cdb0849e21338398de3d3 | |
parent | b54be8de65b09c85992b30dbb1d52032f0498b6d (diff) |
AAPT2: Better debugging output
Test: make aapt2_tests
Change-Id: I7778b773201381538dc1f2e376abee4eb33e44c0
25 files changed, 785 insertions, 193 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index ae67f617d3d7..eb3a99ae6331 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -113,6 +113,7 @@ cc_library_host_static { "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 59a6e1281783..f621660cb8ad 100644 --- a/tools/aapt2/ConfigDescription.cpp +++ b/tools/aapt2/ConfigDescription.cpp @@ -882,6 +882,11 @@ std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const { 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 c1d0e1084186..f71955247d78 100644 --- a/tools/aapt2/ConfigDescription.h +++ b/tools/aapt2/ConfigDescription.h @@ -64,6 +64,8 @@ struct ConfigDescription : public android::ResTable_config { // 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 61c304b2c048..08efc276fe86 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; - if (attr->type_mask & kMask) { - for (const auto& symbol : attr->symbols) { - std::cout << "\n " << symbol.symbol.name.value().entry; - if (symbol.symbol.id) { - std::cout << " (" << symbol.symbol.id.value() << ")"; - } - std::cout << " = " << symbol.value; - } + 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 { - std::cout << "(style)"; + printer_->Print(StringPrintf("(style) size=%zd", style->entries.size())); if (style->parent) { + printer_->Print(" parent="); + const Reference& parent_ref = style->parent.value(); - std::cout << " parent="; if (parent_ref.name) { if (parent_ref.private_reference) { - std::cout << "*"; + 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(")"); } - std::cout << parent_ref.name.value() << " "; + } 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())); + } - if (parent_ref.id) { - std::cout << parent_ref.id.value(); + 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) { + printer_->Print(symbol.symbol.name.value().entry); + if (symbol.symbol.id) { + printer_->Print("("); + printer_->Print(symbol.symbol.id.value().to_string()); + printer_->Print(")"); + } + printer_->Println(StringPrintf("=0x%08x", symbol.value)); } } + } + void Visit(const Style* style) override { 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) { - std::cout << "(" << entry.key.id.value() << ")"; + 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("???"); } - 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(":"); + } + printer_->Print(name.entry); + + if (attr.id) { + printer_->Print("("); + printer_->Print(attr.id.value().to_string()); + printer_->Print(")"); } - std::cout << name.entry; } 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; + ValueHeadlinePrinter headline_printer(package->name, printer); + ValueBodyPrinter body_printer(package->name, printer); + + printer->Print("Package name="); + printer->Print(package->name); if (package->id) { - std::cout << " id=" << std::hex << (int)package->id.value() << std::dec; + printer->Print(StringPrintf(" id=%02x", package->id.value())); } - std::cout << std::endl; + 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 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& 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 296d04bd49bd..3c1ee4c0cdba 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 DebugPrintTableOptions { }; 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 a9f5f298e019..b78f48ce7f17 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 87b986784961..96a02033fc1d 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -69,11 +69,10 @@ enum class ResourceType { 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 @@ struct ResourceName { int compare(const ResourceName& other) const; bool is_valid() const; - std::string ToString() const; + std::string to_string() const; }; /** @@ -115,8 +114,10 @@ struct ResourceNameRef { 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 @@ struct ResourceId { 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 @@ inline bool ResourceId::is_valid() const { 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 @@ inline bool operator!=(const ResourceId& lhs, const ResourceId& rhs) { 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 @@ inline bool operator!=(const ResourceName& lhs, const ResourceName& rhs) { 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 @@ inline bool operator!=(const ResourceNameRef& lhs, const ResourceNameRef& rhs) { 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 @@ inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) { 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 082fd86604da..b38d259d6b25 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 @@ void Reference::Print(std::ostream* out) const { } } +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 @@ bool Id::Flatten(android::Res_value* out) const { 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 @@ void String::Print(std::ostream* out) const { *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 @@ void StyledString::Print(std::ostream* out) const { } } -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 @@ void FileReference::Print(std::ostream* out) const { *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 @@ bool BinaryPrimitive::Equals(const Value* value) const { 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 @@ BinaryPrimitive* BinaryPrimitive::Clone(StringPool* /*new_pool*/) const { } 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 Res_value::TYPE_INT_HEX: + printer->Print(StringPrintf("0x%08x", value.data)); + break; + + case Res_value::TYPE_INT_BOOLEAN: + printer->Print(value.data != 0 ? "true" : "false"); + break; + + 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 android::Res_value::TYPE_INT_HEX: - *out << "(integer) 0x" << std::hex << value.data << std::dec; + + case Res_value::TYPE_FLOAT: + printer->Print(StringPrintf("%g", *reinterpret_cast<const float*>(&value.data))); break; - case android::Res_value::TYPE_INT_BOOLEAN: - *out << "(boolean) " << (value.data != 0 ? "true" : "false"); + + case Res_value::TYPE_DIMENSION: + printer->Print(ComplexToString(value.data, false /*fraction*/)); 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_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 @@ Attribute* Attribute::Clone(StringPool* /*new_pool*/) const { 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 fd242a109006..b2ec8bdd7c77 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 @@ class Value { // 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 @@ struct Reference : public BaseItem<Reference> { 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 @@ struct String : public BaseItem<String> { 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 @@ struct BinaryPrimitive : public BaseItem<BinaryPrimitive> { 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 @@ struct Attribute : public BaseValue<Attribute> { 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 d7f2a668477c..0f312d6998f1 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 @@ struct Source { 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 @@ struct Source { // 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 bc8f1dcb2289..bc7f5a86b043 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 const char* ResourceFileTypeToString(const ResourceFile::Type& type) { } 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 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { << "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 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { 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 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { 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 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { 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 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { 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 8b6c524e5c8d..238e339c05af 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp @@ -164,7 +164,7 @@ bool InlineXmlFormatParser::Consume(IAaptContext* context, xml::XmlResource* doc // 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 66510b0e3a9b..5078678e08a3 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -424,7 +424,7 @@ std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef // 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 259f2e9cfcc8..4a1b46c99d4b 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -472,7 +472,7 @@ class PackageFlattener { 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 1d184fe5a8d2..97ce01a9de13 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -284,7 +284,7 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table 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 @@ static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) 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 SerializeItemToPb(const Item& item, pb::Item* out_item) { } 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 2f7a4b3479ca..4ff6d787cf0f 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 @@ std::string FileInputStream::GetError() const { } 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 @@ FileOutputStream::~FileOutputStream() { } bool FileOutputStream::Next(void** data, size_t* size) { - if (fd_ == -1 || HadError()) { + if (HadError()) { return false; } @@ -159,7 +166,8 @@ bool FileOutputStream::FlushImpl() { 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 3b0766750eae..4ed1ad556f10 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 FileInputStream : public InputStream { 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 @@ class FileOutputStream : public OutputStream { 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 68c3cb1f4156..a6d58caa07b7 100644 --- a/tools/aapt2/io/FileStream_test.cpp +++ b/tools/aapt2/io/FileStream_test.cpp @@ -87,10 +87,8 @@ TEST(FileOutputStreamTest, NextAndBackup) { 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 @@ TEST(FileOutputStreamTest, NextAndBackup) { 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 d270340a180c..7ee10161d86f 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 @@ bool Copy(OutputStream* out, InputStream* in) { 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 1e48508bb4b2..de2ab393336e 100644 --- a/tools/aapt2/io/Util.h +++ b/tools/aapt2/io/Util.h @@ -42,6 +42,7 @@ bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* // 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 3ba4dd880607..91cef642fc3f 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -461,7 +461,7 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso } 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 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, (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 882a85b1c1bb..2d517c76944b 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -289,7 +289,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( 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 000000000000..38b3585f81f5 --- /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 000000000000..94b3c0bf9449 --- /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 000000000000..58beae73c9f2 --- /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 |