AAPT2: Improve diff command

Change-Id: Ia1e2f8482c7192ef50126b61bed7975297332767
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index c10b134..4a86579 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -21,6 +21,7 @@
 #include "io/File.h"
 #include "util/Util.h"
 
+#include <algorithm>
 #include <androidfw/ResourceTypes.h>
 #include <limits>
 
@@ -302,18 +303,42 @@
     mWeak = w;
 }
 
+template <typename T>
+T* addPointer(T& val) {
+    return &val;
+}
+
 bool Attribute::equals(const Value* value) const {
     const Attribute* other = valueCast<Attribute>(value);
     if (!other) {
         return false;
     }
 
-    return this->typeMask == other->typeMask && this->minInt == other->minInt &&
-            this->maxInt == other->maxInt &&
-            std::equal(this->symbols.begin(), this->symbols.end(),
-                       other->symbols.begin(),
-                       [](const Symbol& a, const Symbol& b) -> bool {
-        return a.symbol.equals(&b.symbol) && a.value == b.value;
+    if (symbols.size() != other->symbols.size()) {
+        return false;
+    }
+
+    if (typeMask != other->typeMask || minInt != other->minInt || maxInt != other->maxInt) {
+        return false;
+    }
+
+    std::vector<const Symbol*> sortedA;
+    std::transform(symbols.begin(), symbols.end(),
+                   std::back_inserter(sortedA), addPointer<const Symbol>);
+    std::sort(sortedA.begin(), sortedA.end(), [](const Symbol* a, const Symbol* b) -> bool {
+        return a->symbol.name < b->symbol.name;
+    });
+
+    std::vector<const Symbol*> sortedB;
+    std::transform(other->symbols.begin(), other->symbols.end(),
+                   std::back_inserter(sortedB), addPointer<const Symbol>);
+    std::sort(sortedB.begin(), sortedB.end(), [](const Symbol* a, const Symbol* b) -> bool {
+        return a->symbol.name < b->symbol.name;
+    });
+
+    return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
+                      [](const Symbol* a, const Symbol* b) -> bool {
+        return a->symbol.equals(&b->symbol) && a->value == b->value;
     });
 }
 
@@ -526,9 +551,28 @@
             (parent && other->parent && !parent.value().equals(&other->parent.value()))) {
         return false;
     }
-    return std::equal(entries.begin(), entries.end(), other->entries.begin(),
-                      [](const Entry& a, const Entry& b) -> bool {
-        return a.key.equals(&b.key) && a.value->equals(b.value.get());
+
+    if (entries.size() != other->entries.size()) {
+        return false;
+    }
+
+    std::vector<const Entry*> sortedA;
+    std::transform(entries.begin(), entries.end(),
+                   std::back_inserter(sortedA), addPointer<const Entry>);
+    std::sort(sortedA.begin(), sortedA.end(), [](const Entry* a, const Entry* b) -> bool {
+        return a->key.name < b->key.name;
+    });
+
+    std::vector<const Entry*> sortedB;
+    std::transform(other->entries.begin(), other->entries.end(),
+                   std::back_inserter(sortedB), addPointer<const Entry>);
+    std::sort(sortedB.begin(), sortedB.end(), [](const Entry* a, const Entry* b) -> bool {
+        return a->key.name < b->key.name;
+    });
+
+    return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
+                      [](const Entry* a, const Entry* b) -> bool {
+        return a->key.equals(&b->key) && a->value->equals(b->value.get());
     });
 }
 
@@ -563,6 +607,8 @@
 static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
     if (value.key.name) {
         out << value.key.name.value();
+    } else if (value.key.id) {
+        out << value.key.id.value();
     } else {
         out << "???";
     }
@@ -577,6 +623,10 @@
         return false;
     }
 
+    if (items.size() != other->items.size()) {
+        return false;
+    }
+
     return std::equal(items.begin(), items.end(), other->items.begin(),
                       [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
         return a->equals(b.get());
@@ -605,6 +655,10 @@
         return false;
     }
 
+    if (values.size() != other->values.size()) {
+        return false;
+    }
+
     return std::equal(values.begin(), values.end(), other->values.begin(),
                       [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
         if (bool(a) != bool(b)) {
@@ -659,6 +713,11 @@
     if (!other) {
         return false;
     }
+
+    if (entries.size() != other->entries.size()) {
+        return false;
+    }
+
     return std::equal(entries.begin(), entries.end(), other->entries.begin(),
                       [](const Reference& a, const Reference& b) -> bool {
         return a.equals(&b);
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 67333a2..1ff6ef6 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -16,6 +16,7 @@
 
 #include "Flags.h"
 #include "ResourceTable.h"
+#include "ValueVisitor.h"
 #include "io/ZipArchive.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
@@ -385,6 +386,24 @@
     return diff;
 }
 
+class ZeroingReferenceVisitor : public ValueVisitor {
+public:
+    using ValueVisitor::visit;
+
+    void visit(Reference* ref) override {
+        if (ref->name && ref->id) {
+            if (ref->id.value().packageId() == 0x7f) {
+                ref->id = {};
+            }
+        }
+    }
+};
+
+static void zeroOutAppReferences(ResourceTable* table) {
+    ZeroingReferenceVisitor visitor;
+    visitAllValuesInTable(table, &visitor);
+}
+
 int diff(const std::vector<StringPiece>& args) {
     DiffContext context;
 
@@ -405,6 +424,10 @@
         return 1;
     }
 
+    // Zero out Application IDs in references.
+    zeroOutAppReferences(apkA->getResourceTable());
+    zeroOutAppReferences(apkB->getResourceTable());
+
     if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
         // We emitted a diff, so return 1 (failure).
         return 1;
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index dba2d28..8f0dd3a 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -20,6 +20,7 @@
 #include "io/ZipArchive.h"
 #include "process/IResourceTableConsumer.h"
 #include "proto/ProtoSerialize.h"
+#include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
 #include "util/StringPiece.h"
 
@@ -44,18 +45,9 @@
               << "Source:   " << file->source << "\n";
 }
 
-void dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source,
-                       IAaptContext* context) {
-    std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
-                                                                  context->getDiagnostics());
-    if (!table) {
-        return;
-    }
-
-    Debug::printTable(table.get());
-}
-
 void tryDumpFile(IAaptContext* context, const std::string& filePath) {
+    std::unique_ptr<ResourceTable> table;
+
     std::string err;
     std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err);
     if (zip) {
@@ -75,37 +67,62 @@
                 return;
             }
 
-            std::unique_ptr<ResourceTable> table = deserializeTableFromPb(
+            table = deserializeTableFromPb(
                     pbTable, Source(filePath), context->getDiagnostics());
-            if (table) {
-                DebugPrintTableOptions debugPrintTableOptions;
-                debugPrintTableOptions.showSources = true;
-                Debug::printTable(table.get(), debugPrintTableOptions);
+            if (!table) {
+                return;
             }
         }
-        return;
+
+        if (!table) {
+            file = zip->findFile("resources.arsc");
+            if (file) {
+                std::unique_ptr<io::IData> data = file->openAsData();
+                if (!data) {
+                    context->getDiagnostics()->error(DiagMessage(filePath)
+                                                     << "failed to open resources.arsc");
+                    return;
+                }
+
+                table = util::make_unique<ResourceTable>();
+                BinaryResourceParser parser(context, table.get(), Source(filePath),
+                                            data->data(), data->size());
+                if (!parser.parse()) {
+                    return;
+                }
+            }
+        }
     }
 
-    Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
-    if (!file) {
-        context->getDiagnostics()->error(DiagMessage(filePath) << err);
-        return;
+    if (!table) {
+        Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
+        if (!file) {
+            context->getDiagnostics()->error(DiagMessage(filePath) << err);
+            return;
+        }
+
+        android::FileMap* fileMap = &file.value();
+
+        // Try as a compiled table.
+        pb::ResourceTable pbTable;
+        if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
+            table = deserializeTableFromPb(pbTable, Source(filePath), context->getDiagnostics());
+        }
+
+        if (!table) {
+            // Try as a compiled file.
+            CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
+            if (const pb::CompiledFile* pbFile = input.CompiledFile()) {
+               dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context);
+               return;
+            }
+        }
     }
 
-    android::FileMap* fileMap = &file.value();
-
-    // Try as a compiled table.
-    pb::ResourceTable pbTable;
-    if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
-        dumpCompiledTable(pbTable, Source(filePath), context);
-        return;
-    }
-
-    // Try as a compiled file.
-    CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
-    if (const pb::CompiledFile* pbFile = input.CompiledFile()) {
-       dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context);
-       return;
+    if (table) {
+        DebugPrintTableOptions debugPrintTableOptions;
+        debugPrintTableOptions.showSources = true;
+        Debug::printTable(table.get(), debugPrintTableOptions);
     }
 }
 
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 5cd1c8b..d2eccbc 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -61,7 +61,6 @@
         auto cacheIter = mMapping->find(id);
         if (cacheIter != mMapping->end()) {
             reference->name = cacheIter->second;
-            reference->id = {};
         }
     }
 };
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 595db96..a31bc89 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -88,6 +88,8 @@
      */
     const T& value() const;
 
+    T valueOrDefault(const T& def) const;
+
 private:
     template <typename U>
     friend class Maybe;
@@ -263,6 +265,14 @@
 }
 
 template <typename T>
+T Maybe<T>::valueOrDefault(const T& def) const {
+    if (mNothing) {
+        return def;
+    }
+    return reinterpret_cast<const T&>(mStorage);
+}
+
+template <typename T>
 void Maybe<T>::destroy() {
     reinterpret_cast<T&>(mStorage).~T();
 }
@@ -306,6 +316,19 @@
     return !(a == b);
 }
 
+template <typename T, typename U>
+typename std::enable_if<
+        has_lt_op<T, U>::value,
+        bool
+>::type operator<(const Maybe<T>& a, const Maybe<U>& b) {
+    if (a && b) {
+        return a.value() < b.value();
+    } else if (!a && !b) {
+        return false;
+    }
+    return !a;
+}
+
 } // namespace aapt
 
 #endif // AAPT_MAYBE_H