AAPT2: Better debugging output

Test: make aapt2_tests
Change-Id: I7778b773201381538dc1f2e376abee4eb33e44c0
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, ", ") << "]";