AAPT2: Better debugging output

Test: make aapt2_tests
Change-Id: I7778b773201381538dc1f2e376abee4eb33e44c0
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();
   }
 }