summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Lesinski <adamlesinski@google.com>2017-09-15 16:57:21 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-09-15 16:57:21 +0000
commite33de76a16f6acc42858766460976f44e629657d (patch)
treece962def3f718730407e6863a14aaf0df9a66787
parentd2d6c257f141ed38da2cf0d0a2f84c6f7f055e92 (diff)
parente02983681ae85212c2263055fd4bcfd8097f19bc (diff)
Merge changes Id8bdb14e,I573a6735,Ia804777f,Ia68122cb,Ia1997800, ... into oc-mr1-dev
am: e02983681a Change-Id: I42369e6fb7bd121e45b5a002cd5f00e05221ead3
-rw-r--r--tools/aapt/AaptXml.cpp42
-rw-r--r--tools/aapt2/Android.bp6
-rw-r--r--tools/aapt2/ConfigDescription.cpp4
-rw-r--r--tools/aapt2/ConfigDescription_test.cpp12
-rw-r--r--tools/aapt2/Debug.cpp64
-rw-r--r--tools/aapt2/Format.proto211
-rw-r--r--tools/aapt2/Main.cpp172
-rw-r--r--tools/aapt2/Resource.cpp3
-rw-r--r--tools/aapt2/Resource.h1
-rw-r--r--tools/aapt2/ResourceParser.cpp2
-rw-r--r--tools/aapt2/ResourceParser_test.cpp32
-rw-r--r--tools/aapt2/ResourceValues.cpp13
-rw-r--r--tools/aapt2/ResourceValues.h2
-rw-r--r--tools/aapt2/ResourceValues_test.cpp18
-rw-r--r--tools/aapt2/Resource_test.cpp4
-rw-r--r--tools/aapt2/Resources.proto471
-rw-r--r--tools/aapt2/ResourcesInternal.proto52
-rw-r--r--tools/aapt2/ValueVisitor.h2
-rw-r--r--tools/aapt2/cmd/Compile.cpp65
-rw-r--r--tools/aapt2/cmd/Dump.cpp6
-rw-r--r--tools/aapt2/cmd/Link.cpp20
-rw-r--r--tools/aapt2/cmd/Util.cpp27
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp108
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.h41
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser_test.cpp95
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp14
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp9
-rw-r--r--tools/aapt2/flatten/Archive.cpp16
-rw-r--r--tools/aapt2/flatten/TableFlattener.cpp2
-rw-r--r--tools/aapt2/flatten/XmlFlattener.cpp57
-rw-r--r--tools/aapt2/integration-tests/AppOne/res/navigation/home.xml2
-rw-r--r--tools/aapt2/io/FileInputStream.cpp102
-rw-r--r--tools/aapt2/io/FileInputStream.h63
-rw-r--r--tools/aapt2/io/FileInputStream_test.cpp87
-rw-r--r--tools/aapt2/io/StringInputStream.cpp50
-rw-r--r--tools/aapt2/io/StringInputStream.h56
-rw-r--r--tools/aapt2/io/StringInputStream_test.cpp72
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp11
-rw-r--r--tools/aapt2/java/ClassDefinition.h63
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp5
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp15
-rw-r--r--tools/aapt2/java/ProguardRules.cpp61
-rw-r--r--tools/aapt2/link/ManifestFixer_test.cpp18
-rw-r--r--tools/aapt2/link/XmlCompatVersioner.cpp2
-rw-r--r--tools/aapt2/link/XmlCompatVersioner_test.cpp215
-rw-r--r--tools/aapt2/link/XmlNamespaceRemover.cpp38
-rw-r--r--tools/aapt2/link/XmlNamespaceRemover_test.cpp38
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp4
-rw-r--r--tools/aapt2/link/XmlReferenceLinker_test.cpp66
-rw-r--r--tools/aapt2/proto/ProtoHelpers.cpp51
-rw-r--r--tools/aapt2/proto/ProtoHelpers.h18
-rw-r--r--tools/aapt2/proto/ProtoSerialize.h16
-rw-r--r--tools/aapt2/proto/TableProtoDeserializer.cpp229
-rw-r--r--tools/aapt2/proto/TableProtoSerializer.cpp126
-rw-r--r--tools/aapt2/proto/TableProtoSerializer_test.cpp59
-rw-r--r--tools/aapt2/readme.md13
-rw-r--r--tools/aapt2/test/Builders.cpp216
-rw-r--r--tools/aapt2/test/Builders.h201
-rw-r--r--tools/aapt2/test/Common.h95
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp2
-rw-r--r--tools/aapt2/util/Files.cpp94
-rw-r--r--tools/aapt2/util/Files.h81
-rw-r--r--tools/aapt2/util/Util.h14
-rw-r--r--tools/aapt2/xml/XmlActionExecutor.cpp2
-rw-r--r--tools/aapt2/xml/XmlDom.cpp346
-rw-r--r--tools/aapt2/xml/XmlDom.h224
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp79
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp32
-rw-r--r--tools/aapt2/xml/XmlPullParser.h15
-rw-r--r--tools/aapt2/xml/XmlPullParser_test.cpp15
-rw-r--r--tools/aapt2/xml/XmlUtil.h83
71 files changed, 2780 insertions, 1770 deletions
diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp
index b04a55d91b9c..6801a4ec7325 100644
--- a/tools/aapt/AaptXml.cpp
+++ b/tools/aapt/AaptXml.cpp
@@ -99,24 +99,40 @@ String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree,
if (idx < 0) {
return String8();
}
+
Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_STRING) {
- size_t len;
- const char16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
+ if (tree.getAttributeValue(idx, &value) == BAD_TYPE) {
+ if (outError != NULL) {
+ *outError = "attribute value is corrupt";
}
- resTable.resolveReference(&value, 0);
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
+ return String8();
+ }
+
+ // Check if the string is inline in the XML.
+ if (value.dataType == Res_value::TYPE_STRING) {
+ size_t len;
+ const char16_t* str = tree.getAttributeStringValue(idx, &len);
+ return str ? String8(str, len) : String8();
+ }
+
+ // Resolve the reference if there is one.
+ ssize_t block = resTable.resolveReference(&value, 0);
+ if (block < 0) {
+ if (outError != NULL) {
+ *outError = "attribute value reference does not exist";
+ }
+ return String8();
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
}
+ return String8();
}
+
size_t len;
- const Res_value* value2 = &value;
- const char16_t* str = resTable.valueToString(value2, 0, NULL, &len);
+ const char16_t* str = resTable.valueToString(&value, static_cast<size_t>(block), NULL, &len);
return str ? String8(str, len) : String8();
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 86f7c31b665d..c6cfd3032762 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -88,7 +88,9 @@ cc_library_host_static {
"flatten/XmlFlattener.cpp",
"io/BigBufferStreams.cpp",
"io/File.cpp",
+ "io/FileInputStream.cpp",
"io/FileSystem.cpp",
+ "io/StringInputStream.cpp",
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
@@ -136,7 +138,8 @@ cc_library_host_static {
"xml/XmlDom.cpp",
"xml/XmlPullParser.cpp",
"xml/XmlUtil.cpp",
- "Format.proto",
+ "Resources.proto",
+ "ResourcesInternal.proto",
],
proto: {
export_proto_headers: true,
@@ -160,6 +163,7 @@ cc_library_host_shared {
cc_test_host {
name: "aapt2_tests",
srcs: [
+ "test/Builders.cpp",
"test/Common.cpp",
"**/*_test.cpp",
],
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 7ff0c7227c9c..a9278c136cff 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -70,7 +70,7 @@ static bool parseMcc(const char* name, ResTable_config* out) {
static bool parseMnc(const char* name, ResTable_config* out) {
if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
+ if (out) out->mnc = 0;
return true;
}
const char* c = name;
@@ -967,8 +967,6 @@ bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const {
o.screenLayout & MASK_LAYOUTDIR) ||
!pred(screenLayout & MASK_SCREENLONG,
o.screenLayout & MASK_SCREENLONG) ||
- !pred(screenLayout & MASK_UI_MODE_TYPE,
- o.screenLayout & MASK_UI_MODE_TYPE) ||
!pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) ||
!pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
!pred(screenLayout2 & MASK_SCREENROUND,
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
index 14a565624e01..1f351bf7481d 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -140,4 +140,16 @@ TEST(ConfigDescriptionTest, ParseVrAttribute) {
EXPECT_EQ(std::string("vrheadset-v26"), config.toString().string());
}
+TEST(ConfigDescriptionTest, RangeQualifiersDoNotConflict) {
+ using test::ParseConfigOrDie;
+
+ EXPECT_FALSE(ParseConfigOrDie("large").ConflictsWith(ParseConfigOrDie("normal-land")));
+ EXPECT_FALSE(ParseConfigOrDie("long-hdpi").ConflictsWith(ParseConfigOrDie("xhdpi")));
+ EXPECT_FALSE(ParseConfigOrDie("sw600dp").ConflictsWith(ParseConfigOrDie("sw700dp")));
+ EXPECT_FALSE(ParseConfigOrDie("v11").ConflictsWith(ParseConfigOrDie("v21")));
+ EXPECT_FALSE(ParseConfigOrDie("h600dp").ConflictsWith(ParseConfigOrDie("h300dp")));
+ EXPECT_FALSE(ParseConfigOrDie("w400dp").ConflictsWith(ParseConfigOrDie("w300dp")));
+ EXPECT_FALSE(ParseConfigOrDie("600x400").ConflictsWith(ParseConfigOrDie("300x200")));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index b872ebbeb159..49ed7780f950 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -33,6 +33,8 @@
namespace aapt {
+namespace {
+
class PrintVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
@@ -88,9 +90,13 @@ class PrintVisitor : public ValueVisitor {
}
}
- void Visit(Array* array) override { array->Print(&std::cout); }
+ void Visit(Array* array) override {
+ array->Print(&std::cout);
+ }
- void Visit(Plural* plural) override { plural->Print(&std::cout); }
+ void Visit(Plural* plural) override {
+ plural->Print(&std::cout);
+ }
void Visit(Styleable* styleable) override {
std::cout << "(styleable)";
@@ -110,11 +116,14 @@ class PrintVisitor : public ValueVisitor {
}
}
- void VisitItem(Item* item) override { item->Print(&std::cout); }
+ void VisitItem(Item* item) override {
+ item->Print(&std::cout);
+ }
};
-void Debug::PrintTable(ResourceTable* table,
- const DebugPrintTableOptions& options) {
+} // namespace
+
+void Debug::PrintTable(ResourceTable* table, const DebugPrintTableOptions& options) {
PrintVisitor visitor;
for (auto& package : table->packages) {
@@ -148,10 +157,9 @@ void Debug::PrintTable(ResourceTable* table,
}
for (const ResourceEntry* entry : sorted_entries) {
- ResourceId id(package->id ? package->id.value() : uint8_t(0),
- type->id ? type->id.value() : uint8_t(0),
- entry->id ? entry->id.value() : uint16_t(0));
- ResourceName name(package->name, type->type, entry->name);
+ 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;
switch (entry->symbol_status.state) {
@@ -180,16 +188,14 @@ void Debug::PrintTable(ResourceTable* table,
}
}
-static size_t GetNodeIndex(const std::vector<ResourceName>& names,
- const ResourceName& name) {
+static size_t GetNodeIndex(const std::vector<ResourceName>& names, const ResourceName& name) {
auto iter = std::lower_bound(names.begin(), names.end(), name);
CHECK(iter != names.end());
CHECK(*iter == name);
return std::distance(names.begin(), iter);
}
-void Debug::PrintStyleGraph(ResourceTable* table,
- const ResourceName& target_style) {
+void Debug::PrintStyleGraph(ResourceTable* table, const ResourceName& target_style) {
std::map<ResourceName, std::set<ResourceName>> graph;
std::queue<ResourceName> styles_to_visit;
@@ -223,8 +229,7 @@ void Debug::PrintStyleGraph(ResourceTable* table,
std::cout << "digraph styles {\n";
for (const auto& name : names) {
- std::cout << " node_" << GetNodeIndex(names, name) << " [label=\"" << name
- << "\"];\n";
+ std::cout << " node_" << GetNodeIndex(names, name) << " [label=\"" << name << "\"];\n";
}
for (const auto& entry : graph) {
@@ -243,8 +248,7 @@ void Debug::PrintStyleGraph(ResourceTable* table,
void Debug::DumpHex(const void* data, size_t len) {
const uint8_t* d = (const uint8_t*)data;
for (size_t i = 0; i < len; i++) {
- std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i]
- << " ";
+ std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i] << " ";
if (i % 8 == 7) {
std::cerr << "\n";
}
@@ -262,8 +266,15 @@ class XmlPrinter : public xml::Visitor {
using xml::Visitor::Visit;
void Visit(xml::Element* el) override {
- std::cerr << prefix_;
- std::cerr << "E: ";
+ const size_t previous_size = prefix_.size();
+
+ for (const xml::NamespaceDecl& decl : el->namespace_decls) {
+ std::cerr << prefix_ << "N: " << decl.prefix << "=" << decl.uri
+ << " (line=" << decl.line_number << ")\n";
+ prefix_ += " ";
+ }
+
+ std::cerr << prefix_ << "E: ";
if (!el->namespace_uri.empty()) {
std::cerr << el->namespace_uri << ":";
}
@@ -283,26 +294,13 @@ class XmlPrinter : public xml::Visitor {
std::cerr << "=" << attr.value << "\n";
}
- const size_t previous_size = prefix_.size();
prefix_ += " ";
xml::Visitor::Visit(el);
prefix_.resize(previous_size);
}
- void Visit(xml::Namespace* ns) override {
- std::cerr << prefix_;
- std::cerr << "N: " << ns->namespace_prefix << "=" << ns->namespace_uri
- << " (line=" << ns->line_number << ")\n";
-
- const size_t previous_size = prefix_.size();
- prefix_ += " ";
- xml::Visitor::Visit(ns);
- prefix_.resize(previous_size);
- }
-
void Visit(xml::Text* text) override {
- std::cerr << prefix_;
- std::cerr << "T: '" << text->text << "'\n";
+ std::cerr << prefix_ << "T: '" << text->text << "'\n";
}
private:
diff --git a/tools/aapt2/Format.proto b/tools/aapt2/Format.proto
deleted file mode 100644
index 870b735f70a7..000000000000
--- a/tools/aapt2/Format.proto
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-package aapt.pb;
-
-message ConfigDescription {
- optional bytes data = 1;
- optional string product = 2;
-}
-
-message StringPool {
- optional bytes data = 1;
-}
-
-message CompiledFile {
- message Symbol {
- optional string resource_name = 1;
- optional uint32 line_no = 2;
- }
-
- optional string resource_name = 1;
- optional ConfigDescription config = 2;
- optional string source_path = 3;
- repeated Symbol exported_symbols = 4;
-}
-
-message ResourceTable {
- optional StringPool string_pool = 1;
- optional StringPool source_pool = 2;
- optional StringPool symbol_pool = 3;
- repeated Package packages = 4;
-}
-
-message Package {
- optional uint32 package_id = 1;
- optional string package_name = 2;
- repeated Type types = 3;
-}
-
-message Type {
- optional uint32 id = 1;
- optional string name = 2;
- repeated Entry entries = 3;
-}
-
-message SymbolStatus {
- enum Visibility {
- Unknown = 0;
- Private = 1;
- Public = 2;
- }
- optional Visibility visibility = 1;
- optional Source source = 2;
- optional string comment = 3;
- optional bool allow_new = 4;
-}
-
-message Entry {
- optional uint32 id = 1;
- optional string name = 2;
- optional SymbolStatus symbol_status = 3;
- repeated ConfigValue config_values = 4;
-}
-
-message ConfigValue {
- optional ConfigDescription config = 1;
- optional Value value = 2;
-}
-
-message Source {
- optional uint32 path_idx = 1;
- optional uint32 line_no = 2;
- optional uint32 col_no = 3;
-}
-
-message Reference {
- enum Type {
- Ref = 0;
- Attr = 1;
- }
- optional Type type = 1;
- optional uint32 id = 2;
- optional uint32 symbol_idx = 3;
- optional bool private = 4;
-}
-
-message Id {
-}
-
-message String {
- optional uint32 idx = 1;
-}
-
-message RawString {
- optional uint32 idx = 1;
-}
-
-message FileReference {
- optional uint32 path_idx = 1;
-}
-
-message Primitive {
- optional uint32 type = 1;
- optional uint32 data = 2;
-}
-
-message Attribute {
- message Symbol {
- optional Source source = 1;
- optional string comment = 2;
- optional Reference name = 3;
- optional uint32 value = 4;
- }
- optional uint32 format_flags = 1;
- optional int32 min_int = 2;
- optional int32 max_int = 3;
- repeated Symbol symbols = 4;
-}
-
-message Style {
- message Entry {
- optional Source source = 1;
- optional string comment = 2;
- optional Reference key = 3;
- optional Item item = 4;
- }
-
- optional Reference parent = 1;
- optional Source parent_source = 2;
- repeated Entry entries = 3;
-}
-
-message Styleable {
- message Entry {
- optional Source source = 1;
- optional string comment = 2;
- optional Reference attr = 3;
- }
- repeated Entry entries = 1;
-}
-
-message Array {
- message Entry {
- optional Source source = 1;
- optional string comment = 2;
- optional Item item = 3;
- }
- repeated Entry entries = 1;
-}
-
-message Plural {
- enum Arity {
- Zero = 0;
- One = 1;
- Two = 2;
- Few = 3;
- Many = 4;
- Other = 5;
- }
-
- message Entry {
- optional Source source = 1;
- optional string comment = 2;
- optional Arity arity = 3;
- optional Item item = 4;
- }
- repeated Entry entries = 1;
-}
-
-message Item {
- optional Reference ref = 1;
- optional String str = 2;
- optional RawString raw_str = 3;
- optional FileReference file = 4;
- optional Id id = 5;
- optional Primitive prim = 6;
-}
-
-message CompoundValue {
- optional Attribute attr = 1;
- optional Style style = 2;
- optional Styleable styleable = 3;
- optional Array array = 4;
- optional Plural plural = 5;
-}
-
-message Value {
- optional Source source = 1;
- optional string comment = 2;
- optional bool weak = 3;
-
- optional Item item = 4;
- optional CompoundValue compound_value = 5;
-}
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index c5d38abcdf71..36ab30c7fb6e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -14,12 +14,26 @@
* limitations under the License.
*/
+#ifdef _WIN32
+// clang-format off
+#include <windows.h>
+#include <shellapi.h>
+// clang-format on
+#endif
+
#include <iostream>
#include <vector>
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
#include "androidfw/StringPiece.h"
#include "Diagnostics.h"
+#include "util/Files.h"
+#include "util/Util.h"
+
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -27,54 +41,136 @@ namespace aapt {
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "18";
+static const char* sMinorVersion = "19";
-int PrintVersion() {
- std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
- << sMinorVersion << std::endl;
- return 0;
+static void PrintVersion() {
+ std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
+ sMinorVersion)
+ << std::endl;
+}
+
+static void PrintUsage() {
+ std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl;
}
-extern int Compile(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics);
-extern int Link(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics);
-extern int Dump(const std::vector<android::StringPiece>& args);
-extern int Diff(const std::vector<android::StringPiece>& args);
-extern int Optimize(const std::vector<android::StringPiece>& args);
+extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
+extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
+extern int Dump(const std::vector<StringPiece>& args);
+extern int Diff(const std::vector<StringPiece>& args);
+extern int Optimize(const std::vector<StringPiece>& args);
-} // namespace aapt
+static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args,
+ IDiagnostics* diagnostics) {
+ if (command == "compile" || command == "c") {
+ return Compile(args, diagnostics);
+ } else if (command == "link" || command == "l") {
+ return Link(args, diagnostics);
+ } else if (command == "dump" || command == "d") {
+ return Dump(args);
+ } else if (command == "diff") {
+ return Diff(args);
+ } else if (command == "optimize") {
+ return Optimize(args);
+ } else if (command == "version") {
+ PrintVersion();
+ return 0;
+ }
+ diagnostics->Error(DiagMessage() << "unknown command '" << command << "'");
+ return -1;
+}
-int main(int argc, char** argv) {
- if (argc >= 2) {
- argv += 1;
- argc -= 1;
+static void RunDaemon(IDiagnostics* diagnostics) {
+ std::cout << "Ready" << std::endl;
- std::vector<android::StringPiece> args;
- for (int i = 1; i < argc; i++) {
- args.push_back(argv[i]);
+ // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
+ // the daemon mode. Each subsequent line is a single parameter to the command. The end of a
+ // invocation is signaled by providing an empty line. At any point, an EOF signal or the
+ // command 'quit' will end the daemon mode.
+ while (true) {
+ std::vector<std::string> raw_args;
+ for (std::string line; std::getline(std::cin, line) && !line.empty();) {
+ raw_args.push_back(line);
}
- android::StringPiece command(argv[0]);
- if (command == "compile" || command == "c") {
- aapt::StdErrDiagnostics diagnostics;
- return aapt::Compile(args, &diagnostics);
- } else if (command == "link" || command == "l") {
- aapt::StdErrDiagnostics diagnostics;
- return aapt::Link(args, &diagnostics);
- } else if (command == "dump" || command == "d") {
- return aapt::Dump(args);
- } else if (command == "diff") {
- return aapt::Diff(args);
- } else if (command == "optimize") {
- return aapt::Optimize(args);
- } else if (command == "version") {
- return aapt::PrintVersion();
+ if (!std::cin) {
+ break;
}
- std::cerr << "unknown command '" << command << "'\n";
- } else {
+
+ // An empty command does nothing.
+ if (raw_args.empty()) {
+ continue;
+ }
+
+ if (raw_args[0] == "quit") {
+ break;
+ }
+
+ std::vector<StringPiece> args;
+ args.insert(args.end(), ++raw_args.begin(), raw_args.end());
+ int ret = ExecuteCommand(raw_args[0], args, diagnostics);
+ if (ret != 0) {
+ std::cerr << "Error" << std::endl;
+ }
+ std::cerr << "Done" << std::endl;
+ }
+ std::cout << "Exiting daemon" << std::endl;
+}
+
+} // namespace aapt
+
+int MainImpl(int argc, char** argv) {
+ if (argc < 2) {
std::cerr << "no command specified\n";
+ aapt::PrintUsage();
+ return -1;
}
- std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..."
- << std::endl;
- return 1;
+ argv += 1;
+ argc -= 1;
+
+ aapt::StdErrDiagnostics diagnostics;
+
+ // Collect the arguments starting after the program name and command name.
+ std::vector<StringPiece> args;
+ for (int i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
+ }
+
+ const StringPiece command(argv[0]);
+ if (command != "daemon" && command != "m") {
+ // Single execution.
+ const int result = aapt::ExecuteCommand(command, args, &diagnostics);
+ if (result < 0) {
+ aapt::PrintUsage();
+ }
+ return result;
+ }
+
+ aapt::RunDaemon(&diagnostics);
+ return 0;
+}
+
+int main(int argc, char** argv) {
+#ifdef _WIN32
+ LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ CHECK(wide_argv != nullptr) << "invalid command line parameters passed to process";
+
+ std::vector<std::string> utf8_args;
+ for (int i = 0; i < argc; i++) {
+ std::string utf8_arg;
+ if (!::android::base::WideToUTF8(wide_argv[i], &utf8_arg)) {
+ std::cerr << "error converting input arguments to UTF-8" << std::endl;
+ return 1;
+ }
+ utf8_args.push_back(std::move(utf8_arg));
+ }
+ LocalFree(wide_argv);
+
+ std::unique_ptr<char* []> utf8_argv(new char*[utf8_args.size()]);
+ for (int i = 0; i < argc; i++) {
+ utf8_argv[i] = const_cast<char*>(utf8_args[i].c_str());
+ }
+ argv = utf8_argv.get();
+#endif
+ return MainImpl(argc, argv);
}
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 35971e7bd99b..a9f5f298e019 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -61,6 +61,8 @@ StringPiece ToString(ResourceType type) {
return "menu";
case ResourceType::kMipmap:
return "mipmap";
+ case ResourceType::kNavigation:
+ return "navigation";
case ResourceType::kPlurals:
return "plurals";
case ResourceType::kRaw:
@@ -98,6 +100,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{
{"layout", ResourceType::kLayout},
{"menu", ResourceType::kMenu},
{"mipmap", ResourceType::kMipmap},
+ {"navigation", ResourceType::kNavigation},
{"plurals", ResourceType::kPlurals},
{"raw", ResourceType::kRaw},
{"string", ResourceType::kString},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 0a74c1a0f77d..cbcc8fb805aa 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -59,6 +59,7 @@ enum class ResourceType {
kLayout,
kMenu,
kMipmap,
+ kNavigation,
kPlurals,
kRaw,
kString,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index a5783a532e23..1c3ac2ad4f17 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1219,7 +1219,7 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
continue;
}
item->SetSource(item_source);
- array->items.emplace_back(std::move(item));
+ array->elements.emplace_back(std::move(item));
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 1683c64a6a5c..144ebd22e105 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -22,9 +22,11 @@
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
+#include "io/StringInputStream.h"
#include "test/Test.h"
#include "xml/XmlPullParser.h"
+using ::aapt::io::StringInputStream;
using ::aapt::test::StrValueEq;
using ::aapt::test::ValueEq;
using ::android::ResTable_map;
@@ -43,11 +45,13 @@ constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::stringstream input(kXmlPreamble);
- input << R"(<attr name="foo"/>)" << std::endl;
ResourceTable table;
ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
- xml::XmlPullParser xml_parser(input);
+
+ std::string input = kXmlPreamble;
+ input += R"(<attr name="foo"/>)";
+ StringInputStream in(input);
+ xml::XmlPullParser xml_parser(&in);
ASSERT_FALSE(parser.Parse(&xml_parser));
}
@@ -62,12 +66,16 @@ class ResourceParserTest : public ::testing::Test {
}
::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
- std::stringstream input(kXmlPreamble);
- input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
parserOptions);
- xml::XmlPullParser xmlParser(input);
+
+ std::string input = kXmlPreamble;
+ input += "<resources>\n";
+ input.append(str.data(), str.size());
+ input += "\n</resources>";
+ StringInputStream in(input);
+ xml::XmlPullParser xmlParser(&in);
if (parser.Parse(&xmlParser)) {
return ::testing::AssertionSuccess();
}
@@ -532,11 +540,11 @@ TEST_F(ResourceParserTest, ParseArray) {
Array* array = test::GetValue<Array>(&table_, "array/foo");
ASSERT_THAT(array, NotNull());
- ASSERT_THAT(array->items, SizeIs(3));
+ ASSERT_THAT(array->elements, SizeIs(3));
- EXPECT_THAT(ValueCast<Reference>(array->items[0].get()), NotNull());
- EXPECT_THAT(ValueCast<String>(array->items[1].get()), NotNull());
- EXPECT_THAT(ValueCast<BinaryPrimitive>(array->items[2].get()), NotNull());
+ EXPECT_THAT(ValueCast<Reference>(array->elements[0].get()), NotNull());
+ EXPECT_THAT(ValueCast<String>(array->elements[1].get()), NotNull());
+ EXPECT_THAT(ValueCast<BinaryPrimitive>(array->elements[2].get()), NotNull());
}
TEST_F(ResourceParserTest, ParseStringArray) {
@@ -557,9 +565,9 @@ TEST_F(ResourceParserTest, ParseArrayWithFormat) {
Array* array = test::GetValue<Array>(&table_, "array/foo");
ASSERT_THAT(array, NotNull());
- ASSERT_THAT(array->items, SizeIs(1));
+ ASSERT_THAT(array->elements, SizeIs(1));
- String* str = ValueCast<String>(array->items[0].get());
+ String* str = ValueCast<String>(array->elements[0].get());
ASSERT_THAT(str, NotNull());
EXPECT_THAT(*str, StrValueEq("100"));
}
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index eb59175edf3b..1cba19462839 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -805,13 +805,12 @@ bool Array::Equals(const Value* value) const {
return false;
}
- if (items.size() != other->items.size()) {
+ if (elements.size() != other->elements.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 std::equal(elements.begin(), elements.end(), other->elements.begin(),
+ [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
return a->Equals(b.get());
});
}
@@ -820,14 +819,14 @@ Array* Array::Clone(StringPool* new_pool) const {
Array* array = new Array();
array->comment_ = comment_;
array->source_ = source_;
- for (auto& item : items) {
- array->items.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
+ for (auto& item : elements) {
+ array->elements.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
}
return array;
}
void Array::Print(std::ostream* out) const {
- *out << "(array) [" << util::Joiner(items, ", ") << "]";
+ *out << "(array) [" << util::Joiner(elements, ", ") << "]";
}
bool Plural::Equals(const Value* value) const {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 7e7547fc1b94..275864bbcd3e 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -292,7 +292,7 @@ struct Style : public BaseValue<Style> {
};
struct Array : public BaseValue<Array> {
- std::vector<std::unique_ptr<Item>> items;
+ std::vector<std::unique_ptr<Item>> elements;
bool Equals(const Value* value) const override;
Array* Clone(StringPool* new_pool) const override;
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index 06c3404561de..10f9b55ede08 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -54,19 +54,19 @@ TEST(ResourceValuesTest, ArrayEquals) {
StringPool pool;
Array a;
- a.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
- a.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+ a.elements.push_back(util::make_unique<String>(pool.MakeRef("one")));
+ a.elements.push_back(util::make_unique<String>(pool.MakeRef("two")));
Array b;
- b.items.push_back(util::make_unique<String>(pool.MakeRef("une")));
- b.items.push_back(util::make_unique<String>(pool.MakeRef("deux")));
+ b.elements.push_back(util::make_unique<String>(pool.MakeRef("une")));
+ b.elements.push_back(util::make_unique<String>(pool.MakeRef("deux")));
Array c;
- c.items.push_back(util::make_unique<String>(pool.MakeRef("uno")));
+ c.elements.push_back(util::make_unique<String>(pool.MakeRef("uno")));
Array d;
- d.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
- d.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+ d.elements.push_back(util::make_unique<String>(pool.MakeRef("one")));
+ d.elements.push_back(util::make_unique<String>(pool.MakeRef("two")));
EXPECT_FALSE(a.Equals(&b));
EXPECT_FALSE(a.Equals(&c));
@@ -78,8 +78,8 @@ TEST(ResourceValuesTest, ArrayClone) {
StringPool pool;
Array a;
- a.items.push_back(util::make_unique<String>(pool.MakeRef("one")));
- a.items.push_back(util::make_unique<String>(pool.MakeRef("two")));
+ a.elements.push_back(util::make_unique<String>(pool.MakeRef("one")));
+ a.elements.push_back(util::make_unique<String>(pool.MakeRef("two")));
std::unique_ptr<Array> b(a.Clone(&pool));
EXPECT_TRUE(a.Equals(b.get()));
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index ad4e3ce02b32..c557f3c77654 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -93,6 +93,10 @@ TEST(ResourceTypeTest, ParseResourceTypes) {
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kMipmap);
+ type = ParseResourceType("navigation");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kNavigation);
+
type = ParseResourceType("plurals");
ASSERT_NE(type, nullptr);
EXPECT_EQ(*type, ResourceType::kPlurals);
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
new file mode 100644
index 000000000000..71f33b0853ad
--- /dev/null
+++ b/tools/aapt2/Resources.proto
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Keep proto2 syntax because we require the distinction between fields that
+// are set and unset.
+syntax = "proto2";
+
+option java_package = "com.android.aapt";
+option optimize_for = LITE_RUNTIME;
+
+package aapt.pb;
+
+// A configuration description that wraps the binary form of the C++ class
+// aapt::ConfigDescription, with an added product definition.
+// TODO(adamlesinski): Flesh this out to be represented in proto.
+message ConfigDescription {
+ optional bytes data = 1;
+ optional string product = 2;
+}
+
+// A string pool that wraps the binary form of the C++ class android::ResStringPool.
+message StringPool {
+ optional bytes data = 1;
+}
+
+// The position of a declared entity within a file.
+message SourcePosition {
+ optional uint32 line_number = 1;
+ optional uint32 column_number = 2;
+}
+
+// Developer friendly source file information for an entity in the resource table.
+message Source {
+ // The index of the string path within the source string pool of a ResourceTable.
+ optional uint32 path_idx = 1;
+ optional SourcePosition position = 2;
+}
+
+// Top level message representing a resource table.
+message ResourceTable {
+ // The string pool containing source paths referenced throughout the resource table. This does
+ // not end up in the final binary ARSC file.
+ optional StringPool source_pool = 1;
+
+ // Resource definitions corresponding to an Android package.
+ repeated Package package = 2;
+}
+
+// Defines resources for an Android package.
+message Package {
+ // The package ID of this package, in the range [0x00, 0xff].
+ // The ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time.
+ // The ID 0x01 is reserved for the 'android' package (framework).
+ // The ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time.
+ // The ID 0x7f is reserved for the application package.
+ // IDs > 0x7f are reserved for the application as well and are treated as feature splits.
+ optional uint32 package_id = 1;
+
+ // The Java compatible Android package name of the app.
+ optional string package_name = 2;
+
+ // The series of types defined by the package.
+ repeated Type type = 3;
+}
+
+// A set of resources grouped under a common type. Such types include string, layout, xml, dimen,
+// attr, etc. This maps to the second part of a resource identifier in Java (R.type.entry).
+message Type {
+ // The ID of the type. This may be 0, which indicates no ID is set.
+ optional uint32 id = 1;
+
+ // The name of the type. This corresponds to the 'type' part of a full resource name of the form
+ // package:type/entry. The set of legal type names is listed in Resource.cpp.
+ optional string name = 2;
+
+ // The entries defined for this type.
+ repeated Entry entry = 3;
+}
+
+// The status of a symbol/entry. This contains information like visibility (public/private),
+// comments, and whether the entry can be overridden.
+message SymbolStatus {
+ // The visibility of the resource outside of its package.
+ enum Visibility {
+ // No visibility was explicitly specified. This is typically treated as private.
+ // The distinction is important when two separate R.java files are generated: a public and
+ // private one. An unknown visibility, in this case, would cause the resource to be omitted
+ // from either R.java.
+ UNKNOWN = 0;
+
+ // A resource was explicitly marked as private. This means the resource can not be accessed
+ // outside of its package unless the @*package:type/entry notation is used (the asterisk being
+ // the private accessor). If two R.java files are generated (private + public), the resource
+ // will only be emitted to the private R.java file.
+ PRIVATE = 1;
+
+ // A resource was explicitly marked as public. This means the resource can be accessed
+ // from any package, and is emitted into all R.java files, public and private.
+ PUBLIC = 2;
+ }
+
+ optional Visibility visibility = 1;
+
+ // The path at which this entry's visibility was defined (eg. public.xml).
+ optional Source source = 2;
+
+ // The comment associated with the <public> tag.
+ optional string comment = 3;
+
+ // Whether the symbol can be merged into another resource table without there being an existing
+ // definition to override. Used for overlays and set to true when <add-resource> is specified.
+ optional bool allow_new = 4;
+}
+
+// An entry declaration. An entry has a full resource ID that is the combination of package ID,
+// type ID, and its own entry ID. An entry on its own has no value, but values are defined for
+// various configurations/variants.
+message Entry {
+ // The ID of this entry. Together with the package ID and type ID, this forms a full resource ID
+ // of the form 0xPPTTEEEE, where PP is the package ID, TT is the type ID, and EEEE is the entry
+ // ID.
+ optional uint32 id = 1;
+
+ // The name of this entry. This corresponds to the 'entry' part of a full resource name of the
+ // form package:type/entry.
+ optional string name = 2;
+
+ // The symbol status of this entry, which includes visibility information.
+ optional SymbolStatus symbol_status = 3;
+
+ // The set of values defined for this entry, each corresponding to a different
+ // configuration/variant.
+ repeated ConfigValue config_value = 4;
+}
+
+// A Configuration/Value pair.
+message ConfigValue {
+ optional ConfigDescription config = 1;
+ optional Value value = 2;
+}
+
+// The generic meta-data for every value in a resource table.
+message Value {
+ // Where the value was defined.
+ optional Source source = 1;
+
+ // Any comment associated with the value.
+ optional string comment = 2;
+
+ // Whether the value can be overridden.
+ optional bool weak = 3;
+
+ // If the value is an Item, this is set.
+ optional Item item = 4;
+
+ // If the value is a CompoundValue, this is set.
+ optional CompoundValue compound_value = 5;
+}
+
+// An Item is an abstract type. It represents a value that can appear inline in many places, such
+// as XML attribute values or on the right hand side of style attribute definitions. The concrete
+// type is one of the types below. Only one can be set.
+message Item {
+ optional Reference ref = 1;
+ optional String str = 2;
+ optional RawString raw_str = 3;
+ optional StyledString styled_str = 4;
+ optional FileReference file = 5;
+ optional Id id = 6;
+ optional Primitive prim = 7;
+}
+
+// A CompoundValue is an abstract type. It represents a value that is a made of other values.
+// These can only usually appear as top-level resources. The concrete type is one of the types
+// below. Only one can be set.
+message CompoundValue {
+ optional Attribute attr = 1;
+ optional Style style = 2;
+ optional Styleable styleable = 3;
+ optional Array array = 4;
+ optional Plural plural = 5;
+}
+
+// A value that is a reference to another resource. This reference can be by name or resource ID.
+message Reference {
+ enum Type {
+ // A plain reference (@package:type/entry).
+ REFERENCE = 0;
+
+ // A reference to a theme attribute (?package:type/entry).
+ ATTRIBUTE = 1;
+ }
+
+ optional Type type = 1;
+
+ // The resource ID (0xPPTTEEEE) of the resource being referred.
+ optional uint32 id = 2;
+
+ // The optional resource name.
+ optional string name = 3;
+
+ // Whether this reference is referencing a private resource (@*package:type/entry).
+ optional bool private = 4;
+}
+
+// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
+// resource ID (0xPPTTEEEE) as a unique identifier. Their value is unimportant.
+message Id {
+}
+
+// A value that is a string.
+message String {
+ optional string value = 1;
+}
+
+// A value that is a raw string, which is unescaped/uninterpreted. This is typically used to
+// represent the value of a style attribute before the attribute is compiled and the set of
+// allowed values is known.
+message RawString {
+ optional string value = 1;
+}
+
+// A string with styling information, like html tags that specify boldness, italics, etc.
+message StyledString {
+ // The raw text of the string.
+ optional string value = 1;
+
+ // A Span marks a region of the string text that is styled.
+ message Span {
+ // The name of the tag, and its attributes, encoded as follows:
+ // tag_name;attr1=value1;attr2=value2;[...]
+ optional string tag = 1;
+
+ // The first character position this span applies to, in UTF-16 offset.
+ optional uint32 first_char = 2;
+
+ // The last character position this span applies to, in UTF-16 offset.
+ optional uint32 last_char = 3;
+ }
+
+ repeated Span span = 2;
+}
+
+// A value that is a reference to an external entity, like an XML file or a PNG.
+message FileReference {
+ // Path to a file within the APK (typically res/type-config/entry.ext).
+ optional string path = 1;
+}
+
+// A value that represents a primitive data type (float, int, boolean, etc.).
+// Corresponds to the fields (type/data) of the C struct android::Res_value.
+message Primitive {
+ optional uint32 type = 1;
+ optional uint32 data = 2;
+}
+
+// A value that represents an XML attribute and what values it accepts.
+message Attribute {
+ // A Symbol used to represent an enum or a flag.
+ message Symbol {
+ // Where the enum/flag item was defined.
+ optional Source source = 1;
+
+ // Any comments associated with the enum or flag.
+ optional string comment = 2;
+
+ // The name of the enum/flag as a reference. Enums/flag items are generated as ID resource
+ // values.
+ optional Reference name = 3;
+
+ // The value of the enum/flag.
+ optional uint32 value = 4;
+ }
+
+ // Bitmask of formats allowed for an attribute.
+ enum FormatFlags {
+ ANY = 0x0000ffff; // Allows any type except ENUM and FLAGS.
+ REFERENCE = 0x01; // Allows Reference values.
+ STRING = 0x02; // Allows String/StyledString values.
+ INTEGER = 0x04; // Allows any integer BinaryPrimitive values.
+ BOOLEAN = 0x08; // Allows any boolean BinaryPrimitive values.
+ COLOR = 0x010; // Allows any color BinaryPrimitive values.
+ FLOAT = 0x020; // Allows any float BinaryPrimitive values.
+ DIMENSION = 0x040; // Allows any dimension BinaryPrimitive values.
+ FRACTION = 0x080; // Allows any fraction BinaryPrimitive values.
+ ENUM = 0x00010000; // Allows enums that are defined in the Attribute's symbols.
+ // ENUM and FLAGS cannot BOTH be set.
+ FLAGS = 0x00020000; // Allows flags that are defined in the Attribute's symbols.
+ // ENUM and FLAGS cannot BOTH be set.
+ }
+
+ // A bitmask of types that this XML attribute accepts. Corresponds to the flags in the
+ // enum FormatFlags.
+ optional uint32 format_flags = 1;
+
+ // The smallest integer allowed for this XML attribute. Only makes sense if the format includes
+ // FormatFlags::INTEGER.
+ optional int32 min_int = 2;
+
+ // The largest integer allowed for this XML attribute. Only makes sense if the format includes
+ // FormatFlags::INTEGER.
+ optional int32 max_int = 3;
+
+ // The set of enums/flags defined in this attribute. Only makes sense if the format includes
+ // either FormatFlags::ENUM or FormatFlags::FLAGS. Having both is an error.
+ repeated Symbol symbol = 4;
+}
+
+// A value that represents a style.
+message Style {
+ // An XML attribute/value pair defined in the style.
+ message Entry {
+ // Where the entry was defined.
+ optional Source source = 1;
+
+ // Any comments associated with the entry.
+ optional string comment = 2;
+
+ // A reference to the XML attribute.
+ optional Reference key = 3;
+
+ // The Item defined for this XML attribute.
+ optional Item item = 4;
+ }
+
+ // The optinal style from which this style inherits attributes.
+ optional Reference parent = 1;
+
+ // The source file information of the parent inheritance declaration.
+ optional Source parent_source = 2;
+
+ // The set of XML attribute/value pairs for this style.
+ repeated Entry entry = 3;
+}
+
+// A value that represents a <declare-styleable> XML resource. These are not real resources and
+// only end up as Java fields in the generated R.java. They do not end up in the binary ARSC file.
+message Styleable {
+ // An attribute defined for this styleable.
+ message Entry {
+ // Where the attribute was defined within the <declare-styleable> block.
+ optional Source source = 1;
+
+ // Any comments associated with the declaration.
+ optional string comment = 2;
+
+ // The reference to the attribute.
+ optional Reference attr = 3;
+ }
+
+ // The set of attribute declarations.
+ repeated Entry entry = 1;
+}
+
+// A value that represents an array of resource values.
+message Array {
+ // A single element of the array.
+ message Element {
+ // Where the element was defined.
+ optional Source source = 1;
+
+ // Any comments associated with the element.
+ optional string comment = 2;
+
+ // The value assigned to this element.
+ optional Item item = 3;
+ }
+
+ // The list of array elements.
+ repeated Element element = 1;
+}
+
+// A value that represents a string and its many variations based on plurality.
+message Plural {
+ // The arity of the plural.
+ enum Arity {
+ ZERO = 0;
+ ONE = 1;
+ TWO = 2;
+ FEW = 3;
+ MANY = 4;
+ OTHER = 5;
+ }
+
+ // The plural value for a given arity.
+ message Entry {
+ // Where the plural was defined.
+ optional Source source = 1;
+
+ // Any comments associated with the plural.
+ optional string comment = 2;
+
+ // The arity of the plural.
+ optional Arity arity = 3;
+
+ // The value assigned to this plural.
+ optional Item item = 4;
+ }
+
+ // The set of arity/plural mappings.
+ repeated Entry entry = 1;
+}
+
+// Defines an abstract XmlNode that must be either an XmlElement, or
+// a text node represented by a string.
+message XmlNode {
+ // If set, this node is an element/tag.
+ optional XmlElement element = 1;
+
+ // If set, this node is a chunk of text.
+ optional string text = 2;
+
+ // Source line and column info.
+ optional SourcePosition source = 3;
+}
+
+// An <element> in an XML document.
+message XmlElement {
+ // Namespaces defined on this element.
+ repeated XmlNamespace namespace_declaration = 1;
+
+ // The namespace URI of this element.
+ optional string namespace_uri = 2;
+
+ // The name of this element.
+ optional string name = 3;
+
+ // The attributes of this element.
+ repeated XmlAttribute attribute = 4;
+
+ // The children of this element.
+ repeated XmlNode child = 5;
+}
+
+// A namespace declaration on an XmlElement (xmlns:android="http://...").
+message XmlNamespace {
+ optional string prefix = 1;
+ optional string uri = 2;
+
+ // Source line and column info.
+ optional SourcePosition source = 3;
+}
+
+// An attribute defined on an XmlElement (android:text="...").
+message XmlAttribute {
+ optional string namespace_uri = 1;
+ optional string name = 2;
+ optional string value = 3;
+
+ // Source line and column info.
+ optional SourcePosition source = 4;
+
+ // The resource ID (0xPPTTEEEE) of the attribute.
+ optional uint32 resource_id = 5;
+
+ // The interpreted/compiled version of the `value` string.
+ optional Item compiled_item = 6;
+}
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
new file mode 100644
index 000000000000..31179174b843
--- /dev/null
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android.aapt.pb.internal";
+option optimize_for = LITE_RUNTIME;
+
+import "frameworks/base/tools/aapt2/Resources.proto";
+
+package aapt.pb.internal;
+
+// The top level message representing an external resource file (layout XML, PNG, etc).
+// This is used to represent a compiled file before it is linked. Only useful to aapt2.
+message CompiledFile {
+ message Symbol {
+ // The name of the symbol (in the form package:type/name).
+ optional string resource_name = 1;
+
+ // The position in the file at which this symbol is defined. For debug use.
+ optional aapt.pb.SourcePosition source = 2;
+ }
+
+ // The name of the resource (in the form package:type/name).
+ optional string resource_name = 1;
+
+ // The configuration for which the resource is defined.
+ optional aapt.pb.ConfigDescription config = 2;
+
+ // The filesystem path to where the source file originated.
+ // Mainly used to display helpful error messages.
+ optional string source_path = 3;
+
+ // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
+ repeated Symbol exported_symbol = 4;
+
+ // If this is a compiled XML file, this is the root node.
+ optional aapt.pb.XmlNode xml_root = 5;
+}
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 2763d49f15c4..eb4fa494e53f 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -80,7 +80,7 @@ struct ValueVisitor : public RawValueVisitor {
}
void VisitSubValues(Array* array) {
- for (std::unique_ptr<Item>& item : array->items) {
+ for (std::unique_ptr<Item>& item : array->elements) {
item->Accept(this);
}
}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index b64cd8c432d4..7f5bbf042766 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -16,11 +16,11 @@
#include <dirent.h>
-#include <fstream>
#include <string>
#include "android-base/errors.h"
#include "android-base/file.h"
+#include "android-base/utf8.h"
#include "androidfw/StringPiece.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
@@ -38,6 +38,7 @@
#include "flatten/Archive.h"
#include "flatten/XmlFlattener.h"
#include "io/BigBufferOutputStream.h"
+#include "io/FileInputStream.h"
#include "io/Util.h"
#include "proto/ProtoSerialize.h"
#include "util/Files.h"
@@ -46,8 +47,9 @@
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
-using android::StringPiece;
-using google::protobuf::io::CopyingOutputStreamAdaptor;
+using ::aapt::io::FileInputStream;
+using ::android::StringPiece;
+using ::google::protobuf::io::CopyingOutputStreamAdaptor;
namespace aapt {
@@ -57,19 +59,14 @@ struct ResourcePathData {
std::string name;
std::string extension;
- // Original config str. We keep this because when we parse the config, we may
- // add on
- // version qualifiers. We want to preserve the original input so the output is
- // easily
+ // Original config str. We keep this because when we parse the config, we may add on
+ // version qualifiers. We want to preserve the original input so the output is easily
// computed before hand.
std::string config_str;
ConfigDescription config;
};
-/**
- * Resource file paths are expected to look like:
- * [--/res/]type[-config]/name
- */
+// Resource file paths are expected to look like: [--/res/]type[-config]/name
static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
std::string* out_error) {
std::vector<std::string> parts = util::Split(path, file::sDirSep);
@@ -137,9 +134,7 @@ static bool IsHidden(const StringPiece& filename) {
return util::StartsWith(filename, ".");
}
-/**
- * Walks the res directory structure, looking for resource files.
- */
+// Walks the res directory structure, looking for resource files.
static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
std::vector<ResourcePathData>* out_path_data) {
const std::string& root_dir = options.res_dir.value();
@@ -195,22 +190,20 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
const std::string& output_path) {
ResourceTable table;
{
- std::ifstream fin(path_data.source.path, std::ifstream::binary);
- if (!fin) {
+ FileInputStream fin(path_data.source.path);
+ if (fin.HadError()) {
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
- << "failed to open file: "
- << android::base::SystemErrorCodeToString(errno));
+ << "failed to open file: " << fin.GetError());
return false;
}
// Parse the values file from XML.
- xml::XmlPullParser xml_parser(fin);
+ xml::XmlPullParser xml_parser(&fin);
ResourceParserOptions parser_options;
parser_options.error_on_positional_arguments = !options.legacy_mode;
- // If the filename includes donottranslate, then the default translatable is
- // false.
+ // If the filename includes donottranslate, then the default translatable is false.
parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
@@ -218,8 +211,6 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
if (!res_parser.Parse(&xml_parser)) {
return false;
}
-
- fin.close();
}
if (options.pseudolocalize) {
@@ -239,8 +230,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
// Assign an ID to any package that has resources.
for (auto& pkg : table.packages) {
if (!pkg->id) {
- // If no package ID was set while parsing (public identifiers), auto
- // assign an ID.
+ // If no package ID was set while parsing (public identifiers), auto assign an ID.
pkg->id = context->GetPackageId();
}
}
@@ -292,7 +282,7 @@ static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const R
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
+ std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
output_stream.WriteCompiledFile(compiled_file.get());
output_stream.WriteData(&buffer);
@@ -329,7 +319,7 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const Res
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
+ std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
output_stream.WriteCompiledFile(compiled_file.get());
output_stream.WriteData(map.getDataPtr(), map.getDataLength());
@@ -356,7 +346,8 @@ static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& outp
return false;
}
- std::unique_ptr<pb::CompiledFile> pb_compiled_file = SerializeCompiledFileToPb(xmlres->file);
+ std::unique_ptr<pb::internal::CompiledFile> pb_compiled_file =
+ SerializeCompiledFileToPb(xmlres->file);
out->WriteCompiledFile(pb_compiled_file.get());
out->WriteData(&buffer);
@@ -367,7 +358,7 @@ static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& outp
return true;
}
-static bool IsValidFile(IAaptContext* context, const StringPiece& input_path) {
+static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
const file::FileType file_type = file::GetFileType(input_path);
if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
if (file_type == file::FileType::kDirectory) {
@@ -393,17 +384,14 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
std::unique_ptr<xml::XmlResource> xmlres;
{
- std::ifstream fin(path_data.source.path, std::ifstream::binary);
- if (!fin) {
+ FileInputStream fin(path_data.source.path);
+ if (fin.HadError()) {
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
- << "failed to open file: "
- << android::base::SystemErrorCodeToString(errno));
+ << "failed to open file: " << fin.GetError());
return false;
}
xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
-
- fin.close();
}
if (!xmlres) {
@@ -432,12 +420,9 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
return false;
}
- // Make sure CopyingOutputStreamAdaptor is deleted before we call
- // writer->FinishEntry().
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
{
- // Wrap our IArchiveWriter with an adaptor that implements the
- // ZeroCopyOutputStream
- // interface.
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
CompiledFileOutputStream output_stream(&copying_adaptor);
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index aa9472361def..0965910ca853 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -27,11 +27,11 @@
#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
-bool DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data, size_t len,
+bool DumpCompiledFile(const pb::internal::CompiledFile& pb_file, const void* data, size_t len,
const Source& source, IAaptContext* context) {
std::unique_ptr<ResourceFile> file =
DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
@@ -118,7 +118,7 @@ bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
}
for (uint32_t i = 0; i < num_files; i++) {
- pb::CompiledFile compiled_file;
+ pb::internal::CompiledFile compiled_file;
if (!input.ReadCompiledFile(&compiled_file)) {
context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
return false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 5ad0cdd995ed..d9e7ac6c1e08 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -40,6 +40,7 @@
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
+#include "io/FileInputStream.h"
#include "io/FileSystem.h"
#include "io/Util.h"
#include "io/ZipArchive.h"
@@ -61,8 +62,9 @@
#include "util/Files.h"
#include "xml/XmlDom.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::FileInputStream;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -284,13 +286,11 @@ static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, cons
return table;
}
-/**
- * Inflates an XML file from the source path.
- */
+// Inflates an XML file from the source path.
static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) {
- std::ifstream fin(path, std::ifstream::binary);
- if (!fin) {
- diag->Error(DiagMessage(path) << strerror(errno));
+ FileInputStream fin(path);
+ if (fin.HadError()) {
+ diag->Error(DiagMessage(path) << "failed to load XML file: " << fin.GetError());
return {};
}
return xml::Inflate(&fin, diag, Source(path));
@@ -482,7 +482,7 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
if (options_.no_version_vectors || options_.no_version_transitions) {
// Skip this if it is a vector or animated-vector.
- xml::Element* el = xml::FindRootElement(doc);
+ xml::Element* el = doc->root.get();
if (el && el->namespace_uri.empty()) {
if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
(options_.no_version_transitions && IsTransitionElement(el->name))) {
@@ -1295,7 +1295,7 @@ class LinkCommand {
}
for (uint32_t i = 0; i < num_files; i++) {
- pb::CompiledFile compiled_file;
+ pb::internal::CompiledFile compiled_file;
if (!input_stream.ReadCompiledFile(&compiled_file)) {
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "failed to read compiled file header");
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index e1c45d68f611..d17858d45d08 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -28,7 +28,7 @@
#include "util/Maybe.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -134,19 +134,21 @@ static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) {
return xml::AaptAttribute(Attribute(), id);
}
+static xml::NamespaceDecl CreateAndroidNamespaceDecl() {
+ xml::NamespaceDecl decl;
+ decl.prefix = "android";
+ decl.uri = xml::kSchemaAndroid;
+ return decl;
+}
+
std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
const SplitConstraints& constraints) {
const ResourceId kVersionCode(0x0101021b);
const ResourceId kRevisionCode(0x010104d5);
const ResourceId kHasCode(0x0101000c);
- std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
-
- std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>();
- namespace_android->namespace_uri = xml::kSchemaAndroid;
- namespace_android->namespace_prefix = "android";
-
std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>();
+ manifest_el->namespace_decls.push_back(CreateAndroidNamespaceDecl());
manifest_el->name = "manifest";
manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package});
@@ -179,8 +181,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
xml::Attribute{"", "configForSplit", app_info.split_name.value()});
}
- // Splits may contain more configurations than originally desired (fallback densities, etc.).
- // This makes programmatic discovery of split targetting difficult. Encode the original
+ // Splits may contain more configurations than originally desired (fall-back densities, etc.).
+ // This makes programmatic discovery of split targeting difficult. Encode the original
// split constraints intended for this split.
std::stringstream target_config_str;
target_config_str << util::Joiner(constraints.configs, ",");
@@ -193,8 +195,9 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, 0u)});
manifest_el->AppendChild(std::move(application_el));
- namespace_android->AppendChild(std::move(manifest_el));
- doc->root = std::move(namespace_android);
+
+ std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
+ doc->root = std::move(manifest_el);
return doc;
}
@@ -284,7 +287,7 @@ static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error
Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
// Make sure the first element is <manifest> with package attribute.
- xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
+ xml::Element* manifest_el = xml_res->root.get();
if (manifest_el == nullptr) {
return {};
}
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 786494b6ad1c..857cdd5365a0 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -16,12 +16,8 @@
#include "compile/InlineXmlFormatParser.h"
-#include <sstream>
#include <string>
-#include "android-base/macros.h"
-
-#include "Debug.h"
#include "ResourceUtils.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
@@ -31,19 +27,17 @@ namespace aapt {
namespace {
-/**
- * XML Visitor that will find all <aapt:attr> elements for extraction.
- */
+struct InlineDeclaration {
+ xml::Element* el;
+ std::string attr_namespace_uri;
+ std::string attr_name;
+};
+
+// XML Visitor that will find all <aapt:attr> elements for extraction.
class Visitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
- struct InlineDeclaration {
- xml::Element* el;
- std::string attr_namespace_uri;
- std::string attr_name;
- };
-
explicit Visitor(IAaptContext* context, xml::XmlResource* xml_resource)
: context_(context), xml_resource_(xml_resource) {}
@@ -53,51 +47,44 @@ class Visitor : public xml::PackageAwareVisitor {
return;
}
- const Source& src = xml_resource_->file.source.WithLine(el->line_number);
+ const Source src = xml_resource_->file.source.WithLine(el->line_number);
xml::Attribute* attr = el->FindAttribute({}, "name");
if (!attr) {
- context_->GetDiagnostics()->Error(DiagMessage(src)
- << "missing 'name' attribute");
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "missing 'name' attribute");
error_ = true;
return;
}
Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
if (!ref) {
- context_->GetDiagnostics()->Error(
- DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid XML attribute '" << attr->value
+ << "'");
error_ = true;
return;
}
const ResourceName& name = ref.value().name.value();
- // Use an empty string for the compilation package because we don't want to
- // default to
- // the local package if the user specified name="style" or something. This
- // should just
+ // Use an empty string for the compilation package because we don't want to default to
+ // the local package if the user specified name="style" or something. This should just
// be the default namespace.
- Maybe<xml::ExtractedPackage> maybe_pkg =
- TransformPackageAlias(name.package, {});
+ Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package, {});
if (!maybe_pkg) {
- context_->GetDiagnostics()->Error(DiagMessage(src)
- << "invalid namespace prefix '"
- << name.package << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid namespace prefix '"
+ << name.package << "'");
error_ = true;
return;
}
const xml::ExtractedPackage& pkg = maybe_pkg.value();
- const bool private_namespace =
- pkg.private_namespace || ref.value().private_reference;
+ const bool private_namespace = pkg.private_namespace || ref.value().private_reference;
InlineDeclaration decl;
decl.el = el;
decl.attr_name = name.entry;
if (!pkg.package.empty()) {
- decl.attr_namespace_uri =
- xml::BuildPackageNamespace(pkg.package, private_namespace);
+ decl.attr_namespace_uri = xml::BuildPackageNamespace(pkg.package, private_namespace);
}
inline_declarations_.push_back(std::move(decl));
@@ -107,7 +94,9 @@ class Visitor : public xml::PackageAwareVisitor {
return inline_declarations_;
}
- bool HasError() const { return error_; }
+ bool HasError() const {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(Visitor);
@@ -120,8 +109,7 @@ class Visitor : public xml::PackageAwareVisitor {
} // namespace
-bool InlineXmlFormatParser::Consume(IAaptContext* context,
- xml::XmlResource* doc) {
+bool InlineXmlFormatParser::Consume(IAaptContext* context, xml::XmlResource* doc) {
Visitor visitor(context, doc);
doc->root->Accept(&visitor);
if (visitor.HasError()) {
@@ -129,69 +117,53 @@ bool InlineXmlFormatParser::Consume(IAaptContext* context,
}
size_t name_suffix_counter = 0;
- for (const Visitor::InlineDeclaration& decl :
- visitor.GetInlineDeclarations()) {
+ for (const InlineDeclaration& decl : visitor.GetInlineDeclarations()) {
auto new_doc = util::make_unique<xml::XmlResource>();
new_doc->file.config = doc->file.config;
new_doc->file.source = doc->file.source.WithLine(decl.el->line_number);
new_doc->file.name = doc->file.name;
// Modify the new entry name. We need to suffix the entry with a number to
- // avoid
- // local collisions, then mangle it with the empty package, such that it
- // won't show up
+ // avoid local collisions, then mangle it with the empty package, such that it won't show up
// in R.java.
-
- new_doc->file.name.entry =
- NameMangler::MangleEntry({}, new_doc->file.name.entry + "__" +
- std::to_string(name_suffix_counter));
+ new_doc->file.name.entry = NameMangler::MangleEntry(
+ {}, new_doc->file.name.entry + "__" + std::to_string(name_suffix_counter));
// Extracted elements must be the only child of <aapt:attr>.
// Make sure there is one root node in the children (ignore empty text).
- for (auto& child : decl.el->children) {
+ for (std::unique_ptr<xml::Node>& child : decl.el->children) {
const Source child_source = doc->file.source.WithLine(child->line_number);
if (xml::Text* t = xml::NodeCast<xml::Text>(child.get())) {
if (!util::TrimWhitespace(t->text).empty()) {
- context->GetDiagnostics()->Error(
- DiagMessage(child_source)
- << "can't extract text into its own resource");
+ context->GetDiagnostics()->Error(DiagMessage(child_source)
+ << "can't extract text into its own resource");
return false;
}
} else if (new_doc->root) {
- context->GetDiagnostics()->Error(
- DiagMessage(child_source)
- << "inline XML resources must have a single root");
+ context->GetDiagnostics()->Error(DiagMessage(child_source)
+ << "inline XML resources must have a single root");
return false;
} else {
- new_doc->root = std::move(child);
+ new_doc->root.reset(static_cast<xml::Element*>(child.release()));
new_doc->root->parent = nullptr;
}
}
- // Walk up and find the parent element.
- xml::Node* node = decl.el;
- xml::Element* parent_el = nullptr;
- while (node->parent &&
- (parent_el = xml::NodeCast<xml::Element>(node->parent)) == nullptr) {
- node = node->parent;
- }
-
+ // Get the parent element of <aapt:attr>
+ xml::Element* parent_el = decl.el->parent;
if (!parent_el) {
- context->GetDiagnostics()->Error(
- DiagMessage(new_doc->file.source)
- << "no suitable parent for inheriting attribute");
+ context->GetDiagnostics()->Error(DiagMessage(new_doc->file.source)
+ << "no suitable parent for inheriting attribute");
return false;
}
// 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()});
+ parent_el->attributes.push_back(xml::Attribute{decl.attr_namespace_uri, decl.attr_name,
+ "@" + new_doc->file.name.ToString()});
// Delete the subtree.
- for (auto iter = parent_el->children.begin();
- iter != parent_el->children.end(); ++iter) {
- if (iter->get() == node) {
+ for (auto iter = parent_el->children.begin(); iter != parent_el->children.end(); ++iter) {
+ if (iter->get() == decl.el) {
parent_el->children.erase(iter);
break;
}
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.h b/tools/aapt2/compile/InlineXmlFormatParser.h
index 1a658fd6a180..4300023e7726 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.h
+++ b/tools/aapt2/compile/InlineXmlFormatParser.h
@@ -26,35 +26,30 @@
namespace aapt {
-/**
- * Extracts Inline XML definitions into their own xml::XmlResource objects.
- *
- * Inline XML looks like:
- *
- * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- * xmlns:aapt="http://schemas.android.com/aapt" >
- * <aapt:attr name="android:drawable" >
- * <vector
- * android:height="64dp"
- * android:width="64dp"
- * android:viewportHeight="600"
- * android:viewportWidth="600"/>
- * </aapt:attr>
- * </animated-vector>
- *
- * The <vector> will be extracted into its own XML file and <animated-vector>
- * will
- * gain an attribute 'android:drawable' set to a reference to the extracted
- * <vector> resource.
- */
+// Extracts Inline XML definitions into their own xml::XmlResource objects.
+//
+// Inline XML looks like:
+//
+// <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+// xmlns:aapt="http://schemas.android.com/aapt" >
+// <aapt:attr name="android:drawable" >
+// <vector
+// android:height="64dp"
+// android:width="64dp"
+// android:viewportHeight="600"
+// android:viewportWidth="600"/>
+// </aapt:attr>
+// </animated-vector>
+//
+// The <vector> will be extracted into its own XML file and <animated-vector> will
+// gain an attribute 'android:drawable' set to a reference to the extracted <vector> resource.
class InlineXmlFormatParser : public IXmlResourceConsumer {
public:
explicit InlineXmlFormatParser() = default;
bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
- std::vector<std::unique_ptr<xml::XmlResource>>&
- GetExtractedInlineXmlDocuments() {
+ std::vector<std::unique_ptr<xml::XmlResource>>& GetExtractedInlineXmlDocuments() {
return queue_;
}
diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
index 348796c98c22..de7739ada407 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -18,25 +18,32 @@
#include "test/Test.h"
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
namespace aapt {
TEST(InlineXmlFormatParserTest, PassThrough) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<View android:text="hey">
<View android:id="hi" />
</View>
- </View>)EOF");
+ </View>)");
InlineXmlFormatParser parser;
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
- EXPECT_EQ(0u, parser.GetExtractedInlineXmlDocuments().size());
+ EXPECT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(0u));
}
TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -44,7 +51,7 @@ TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
<View3 android:id="hi" />
</View2>
</aapt:attr>
- </View1>)EOF");
+ </View1>)");
doc->file.name = test::ParseNameOrDie("layout/main");
@@ -52,42 +59,38 @@ TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
// One XML resource should have been extracted.
- EXPECT_EQ(1u, parser.GetExtractedInlineXmlDocuments().size());
-
- xml::Element* el = xml::FindRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ EXPECT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(1u));
- EXPECT_EQ("View1", el->name);
+ xml::Element* el = doc->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->name, StrEq("View1"));
// The <aapt:attr> tag should be extracted.
- EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
+ EXPECT_THAT(el->FindChild(xml::kSchemaAapt, "attr"), IsNull());
// The 'android:text' attribute should be set with a reference.
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attr);
+ ASSERT_THAT(attr, NotNull());
ResourceNameRef name_ref;
ASSERT_TRUE(ResourceUtils::ParseReference(attr->value, &name_ref));
- xml::XmlResource* extracted_doc =
- parser.GetExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extracted_doc);
+ xml::XmlResource* extracted_doc = parser.GetExtractedInlineXmlDocuments()[0].get();
+ ASSERT_THAT(extracted_doc, NotNull());
// Make sure the generated reference is correct.
- EXPECT_EQ(name_ref.package, extracted_doc->file.name.package);
- EXPECT_EQ(name_ref.type, extracted_doc->file.name.type);
- EXPECT_EQ(name_ref.entry, extracted_doc->file.name.entry);
+ EXPECT_THAT(extracted_doc->file.name, Eq(name_ref));
// Verify the structure of the extracted XML.
- el = xml::FindRootElement(extracted_doc);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
- EXPECT_NE(nullptr, el->FindChild({}, "View3"));
+ el = extracted_doc->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->name, StrEq("View2"));
+ EXPECT_THAT(el->FindChild({}, "View3"), NotNull());
}
TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -99,45 +102,39 @@ TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
<aapt:attr name="android:drawable">
<vector />
</aapt:attr>
- </View1>)EOF");
+ </View1>)");
doc->file.name = test::ParseNameOrDie("layout/main");
InlineXmlFormatParser parser;
ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
- ASSERT_EQ(2u, parser.GetExtractedInlineXmlDocuments().size());
-
- xml::Element* el = xml::FindRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(2u));
- EXPECT_EQ("View1", el->name);
+ xml::Element* el = doc->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->name, StrEq("View1"));
xml::Attribute* attr_text = el->FindAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attr_text);
+ ASSERT_THAT(attr_text, NotNull());
- xml::Attribute* attr_drawable =
- el->FindAttribute(xml::kSchemaAndroid, "drawable");
- ASSERT_NE(nullptr, attr_drawable);
+ xml::Attribute* attr_drawable = el->FindAttribute(xml::kSchemaAndroid, "drawable");
+ ASSERT_THAT(attr_drawable, NotNull());
// The two extracted resources should have different names.
- EXPECT_NE(attr_text->value, attr_drawable->value);
+ EXPECT_THAT(attr_text->value, Not(Eq(attr_drawable->value)));
// The child <aapt:attr> elements should be gone.
- EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
-
- xml::XmlResource* extracted_doc_text =
- parser.GetExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extracted_doc_text);
- el = xml::FindRootElement(extracted_doc_text);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
-
- xml::XmlResource* extracted_doc_drawable =
- parser.GetExtractedInlineXmlDocuments()[1].get();
- ASSERT_NE(nullptr, extracted_doc_drawable);
- el = xml::FindRootElement(extracted_doc_drawable);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("vector", el->name);
+ EXPECT_THAT(el->FindChild(xml::kSchemaAapt, "attr"), IsNull());
+
+ xml::XmlResource* extracted_doc_text = parser.GetExtractedInlineXmlDocuments()[0].get();
+ ASSERT_THAT(extracted_doc_text, NotNull());
+ ASSERT_THAT(extracted_doc_text->root, NotNull());
+ EXPECT_THAT(extracted_doc_text->root->name, StrEq("View2"));
+
+ xml::XmlResource* extracted_doc_drawable = parser.GetExtractedInlineXmlDocuments()[1].get();
+ ASSERT_THAT(extracted_doc_drawable, NotNull());
+ ASSERT_THAT(extracted_doc_drawable->root, NotNull());
+ EXPECT_THAT(extracted_doc_drawable->root->name, StrEq("vector"));
}
} // namespace aapt
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index d051120b9445..bdccf8bcae3a 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -22,13 +22,14 @@
#include <memory>
#include <utility>
-#include <android-base/file.h>
-#include <android-base/logging.h>
+#include "android-base/file.h"
+#include "android-base/logging.h"
#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "io/File.h"
#include "io/FileSystem.h"
+#include "io/StringInputStream.h"
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
@@ -49,9 +50,9 @@ using ::aapt::configuration::Group;
using ::aapt::configuration::Locale;
using ::aapt::io::IFile;
using ::aapt::io::RegularFile;
+using ::aapt::io::StringInputStream;
using ::aapt::util::TrimWhitespace;
using ::aapt::xml::Element;
-using ::aapt::xml::FindRootElement;
using ::aapt::xml::NodeCast;
using ::aapt::xml::XmlActionExecutor;
using ::aapt::xml::XmlActionExecutorPolicy;
@@ -182,15 +183,14 @@ ConfigurationParser::ConfigurationParser(std::string contents)
}
Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
- std::istringstream in(contents_);
-
- auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
+ StringInputStream in(contents_);
+ std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
if (!doc) {
return {};
}
// Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
- auto* root = FindRootElement(doc.get());
+ Element* root = doc->root.get();
if (root == nullptr) {
diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
return {};
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index fb71e98d2fb5..f89773720cc5 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -18,9 +18,6 @@
#include <string>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
#include "androidfw/ResourceTypes.h"
#include "test/Test.h"
@@ -29,7 +26,7 @@
namespace aapt {
namespace {
-using android::ResTable_config;
+using ::android::ResTable_config;
using configuration::Abi;
using configuration::AndroidSdk;
using configuration::Artifact;
@@ -38,7 +35,7 @@ using configuration::DeviceFeature;
using configuration::GlTexture;
using configuration::Locale;
using configuration::AndroidManifest;
-using testing::ElementsAre;
+using ::testing::ElementsAre;
using xml::Element;
using xml::NodeCast;
@@ -192,7 +189,7 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
ASSERT_TRUE(ok);
EXPECT_EQ(1ul, config.artifacts.size());
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 826f91b4a2fd..5f8bd063f9b0 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -23,12 +23,14 @@
#include "android-base/errors.h"
#include "android-base/macros.h"
+#include "android-base/utf8.h"
#include "androidfw/StringPiece.h"
#include "ziparchive/zip_writer.h"
#include "util/Files.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::android::base::SystemErrorCodeToString;
namespace aapt {
@@ -58,11 +60,11 @@ class DirectoryWriter : public IArchiveWriter {
std::string full_path = dir_;
file::AppendPath(&full_path, path);
- file::mkdirs(file::GetStem(full_path));
+ file::mkdirs(file::GetStem(full_path).to_string());
- file_ = {fopen(full_path.data(), "wb"), fclose};
+ file_ = {::android::base::utf8::fopen(full_path.c_str(), "wb"), fclose};
if (!file_) {
- error_ = android::base::SystemErrorCodeToString(errno);
+ error_ = SystemErrorCodeToString(errno);
return false;
}
return true;
@@ -74,7 +76,7 @@ class DirectoryWriter : public IArchiveWriter {
}
if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
- error_ = android::base::SystemErrorCodeToString(errno);
+ error_ = SystemErrorCodeToString(errno);
file_.reset(nullptr);
return false;
}
@@ -121,9 +123,9 @@ class ZipFileWriter : public IArchiveWriter {
ZipFileWriter() = default;
bool Open(const StringPiece& path) {
- file_ = {fopen(path.data(), "w+b"), fclose};
+ file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose};
if (!file_) {
- error_ = android::base::SystemErrorCodeToString(errno);
+ error_ = SystemErrorCodeToString(errno);
return false;
}
writer_ = util::make_unique<ZipWriter>(file_.get());
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index e5993a65366d..14b776b1bd99 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -133,7 +133,7 @@ class MapFlattenVisitor : public RawValueVisitor {
}
void Visit(Array* array) override {
- for (auto& item : array->items) {
+ for (auto& item : array->elements) {
ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
FlattenValue(item.get(), out_entry);
out_entry->value.size = util::HostToDevice16(sizeof(out_entry->value));
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 331ef784a7da..b3b308a29fc5 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -38,12 +38,10 @@ namespace {
constexpr uint32_t kLowPriority = 0xffffffffu;
-static bool cmp_xml_attribute_by_id(const xml::Attribute* a,
- const xml::Attribute* b) {
+static bool cmp_xml_attribute_by_id(const xml::Attribute* a, const xml::Attribute* b) {
if (a->compiled_attribute && a->compiled_attribute.value().id) {
if (b->compiled_attribute && b->compiled_attribute.value().id) {
- return a->compiled_attribute.value().id.value() <
- b->compiled_attribute.value().id.value();
+ return a->compiled_attribute.value().id.value() < b->compiled_attribute.value().id.value();
}
return true;
} else if (!b->compiled_attribute) {
@@ -75,17 +73,6 @@ class XmlFlattenerVisitor : public xml::Visitor {
XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
: buffer_(buffer), options_(options) {}
- void Visit(xml::Namespace* node) override {
- if (node->namespace_uri == xml::kSchemaTools) {
- // Skip dedicated tools namespace.
- xml::Visitor::Visit(node);
- } else {
- WriteNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
- xml::Visitor::Visit(node);
- WriteNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
- }
- }
-
void Visit(xml::Text* node) override {
if (util::TrimWhitespace(node->text).empty()) {
// Skip whitespace only text nodes.
@@ -93,8 +80,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
}
ChunkWriter writer(buffer_);
- ResXMLTree_node* flat_node =
- writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
+ ResXMLTree_node* flat_node = writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
flat_node->lineNumber = util::HostToDevice32(node->line_number);
flat_node->comment.index = util::HostToDevice32(-1);
@@ -109,6 +95,13 @@ class XmlFlattenerVisitor : public xml::Visitor {
}
void Visit(xml::Element* node) override {
+ for (const xml::NamespaceDecl& decl : node->namespace_decls) {
+ // Skip dedicated tools namespace.
+ if (decl.uri != xml::kSchemaTools) {
+ WriteNamespace(decl, android::RES_XML_START_NAMESPACE_TYPE);
+ }
+ }
+
{
ChunkWriter start_writer(buffer_);
ResXMLTree_node* flat_node =
@@ -116,19 +109,15 @@ class XmlFlattenerVisitor : public xml::Visitor {
flat_node->lineNumber = util::HostToDevice32(node->line_number);
flat_node->comment.index = util::HostToDevice32(-1);
- ResXMLTree_attrExt* flat_elem =
- start_writer.NextBlock<ResXMLTree_attrExt>();
+ ResXMLTree_attrExt* flat_elem = start_writer.NextBlock<ResXMLTree_attrExt>();
- // A missing namespace must be null, not an empty string. Otherwise the
- // runtime complains.
+ // A missing namespace must be null, not an empty string. Otherwise the runtime complains.
AddString(node->namespace_uri, kLowPriority, &flat_elem->ns,
true /* treat_empty_string_as_null */);
- AddString(node->name, kLowPriority, &flat_elem->name,
- true /* treat_empty_string_as_null */);
+ AddString(node->name, kLowPriority, &flat_elem->name, true /* treat_empty_string_as_null */);
flat_elem->attributeStart = util::HostToDevice16(sizeof(*flat_elem));
- flat_elem->attributeSize =
- util::HostToDevice16(sizeof(ResXMLTree_attribute));
+ flat_elem->attributeSize = util::HostToDevice16(sizeof(ResXMLTree_attribute));
WriteAttributes(node, flat_elem, &start_writer);
@@ -144,14 +133,20 @@ class XmlFlattenerVisitor : public xml::Visitor {
flat_end_node->lineNumber = util::HostToDevice32(node->line_number);
flat_end_node->comment.index = util::HostToDevice32(-1);
- ResXMLTree_endElementExt* flat_end_elem =
- end_writer.NextBlock<ResXMLTree_endElementExt>();
+ ResXMLTree_endElementExt* flat_end_elem = end_writer.NextBlock<ResXMLTree_endElementExt>();
AddString(node->namespace_uri, kLowPriority, &flat_end_elem->ns,
true /* treat_empty_string_as_null */);
AddString(node->name, kLowPriority, &flat_end_elem->name);
end_writer.Finish();
}
+
+ for (auto iter = node->namespace_decls.rbegin(); iter != node->namespace_decls.rend(); ++iter) {
+ // Skip dedicated tools namespace.
+ if (iter->uri != xml::kSchemaTools) {
+ WriteNamespace(*iter, android::RES_XML_END_NAMESPACE_TYPE);
+ }
+ }
}
private:
@@ -173,16 +168,16 @@ class XmlFlattenerVisitor : public xml::Visitor {
string_refs.push_back(StringFlattenDest{ref, dest});
}
- void WriteNamespace(xml::Namespace* node, uint16_t type) {
+ void WriteNamespace(const xml::NamespaceDecl& decl, uint16_t type) {
ChunkWriter writer(buffer_);
ResXMLTree_node* flatNode = writer.StartChunk<ResXMLTree_node>(type);
- flatNode->lineNumber = util::HostToDevice32(node->line_number);
+ flatNode->lineNumber = util::HostToDevice32(decl.line_number);
flatNode->comment.index = util::HostToDevice32(-1);
ResXMLTree_namespaceExt* flat_ns = writer.NextBlock<ResXMLTree_namespaceExt>();
- AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix);
- AddString(node->namespace_uri, kLowPriority, &flat_ns->uri);
+ AddString(decl.prefix, kLowPriority, &flat_ns->prefix);
+ AddString(decl.uri, kLowPriority, &flat_ns->uri);
writer.Finish();
}
diff --git a/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml b/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml
new file mode 100644
index 000000000000..ade271d60ab6
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/navigation/home.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation />
diff --git a/tools/aapt2/io/FileInputStream.cpp b/tools/aapt2/io/FileInputStream.cpp
new file mode 100644
index 000000000000..07dbb5a98add
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "io/FileInputStream.h"
+
+#include <errno.h> // for errno
+#include <fcntl.h> // for O_RDONLY
+#include <unistd.h> // for read
+
+#include "android-base/errors.h"
+#include "android-base/file.h" // for O_BINARY
+#include "android-base/macros.h"
+#include "android-base/utf8.h"
+
+using ::android::base::SystemErrorCodeToString;
+
+namespace aapt {
+namespace io {
+
+FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
+ : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY),
+ buffer_capacity) {
+}
+
+FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
+ : fd_(fd),
+ buffer_capacity_(buffer_capacity),
+ buffer_offset_(0u),
+ buffer_size_(0u),
+ total_byte_count_(0u) {
+ if (fd_ == -1) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
+}
+
+bool FileInputStream::Next(const void** data, size_t* size) {
+ if (HadError()) {
+ return false;
+ }
+
+ // Deal with any remaining bytes after BackUp was called.
+ if (buffer_offset_ != buffer_size_) {
+ *data = buffer_.get() + buffer_offset_;
+ *size = buffer_size_ - buffer_offset_;
+ total_byte_count_ += buffer_size_ - buffer_offset_;
+ buffer_offset_ = buffer_size_;
+ return true;
+ }
+
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd_, buffer_.get(), buffer_capacity_));
+ if (n < 0) {
+ error_ = SystemErrorCodeToString(errno);
+ fd_.reset();
+ return false;
+ }
+
+ buffer_size_ = static_cast<size_t>(n);
+ buffer_offset_ = buffer_size_;
+ total_byte_count_ += buffer_size_;
+
+ *data = buffer_.get();
+ *size = buffer_size_;
+ return buffer_size_ != 0u;
+}
+
+void FileInputStream::BackUp(size_t count) {
+ if (count > buffer_offset_) {
+ count = buffer_offset_;
+ }
+ buffer_offset_ -= count;
+ total_byte_count_ -= count;
+}
+
+size_t FileInputStream::ByteCount() const {
+ return total_byte_count_;
+}
+
+bool FileInputStream::HadError() const {
+ return !error_.empty();
+}
+
+std::string FileInputStream::GetError() const {
+ return error_;
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/FileInputStream.h b/tools/aapt2/io/FileInputStream.h
new file mode 100644
index 000000000000..6beb9a186ce5
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream.h
@@ -0,0 +1,63 @@
+/*
+ * 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_IO_FILEINPUTSTREAM_H
+#define AAPT_IO_FILEINPUTSTREAM_H
+
+#include "io/Io.h"
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+namespace aapt {
+namespace io {
+
+class FileInputStream : public InputStream {
+ public:
+ explicit FileInputStream(const std::string& path, size_t buffer_capacity = 4096);
+
+ // Takes ownership of `fd`.
+ explicit FileInputStream(int fd, size_t buffer_capacity = 4096);
+
+ bool Next(const void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ size_t ByteCount() const override;
+
+ bool HadError() const override;
+
+ std::string GetError() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileInputStream);
+
+ android::base::unique_fd fd_;
+ std::string error_;
+ std::unique_ptr<uint8_t[]> buffer_;
+ size_t buffer_capacity_;
+ size_t buffer_offset_;
+ size_t buffer_size_;
+ size_t total_byte_count_;
+};
+
+} // namespace io
+} // namespace aapt
+
+#endif // AAPT_IO_FILEINPUTSTREAM_H
diff --git a/tools/aapt2/io/FileInputStream_test.cpp b/tools/aapt2/io/FileInputStream_test.cpp
new file mode 100644
index 000000000000..7314ab7beeba
--- /dev/null
+++ b/tools/aapt2/io/FileInputStream_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "io/FileInputStream.h"
+
+#include "android-base/macros.h"
+#include "android-base/test_utils.h"
+
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
+namespace aapt {
+namespace io {
+
+TEST(FileInputStreamTest, NextAndBackup) {
+ std::string input = "this is a cool string";
+ TemporaryFile file;
+ ASSERT_THAT(TEMP_FAILURE_RETRY(write(file.fd, input.c_str(), input.size())), Eq(21));
+ lseek64(file.fd, 0, SEEK_SET);
+
+ // Use a small buffer size so that we can call Next() a few times.
+ FileInputStream in(file.fd, 10u);
+ ASSERT_FALSE(in.HadError());
+ EXPECT_THAT(in.ByteCount(), Eq(0u));
+
+ const char* buffer;
+ size_t size;
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError();
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(in.ByteCount(), Eq(10u));
+ EXPECT_THAT(StringPiece(buffer, size), Eq("this is a "));
+
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(in.ByteCount(), Eq(20u));
+ EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+
+ in.BackUp(5u);
+ EXPECT_THAT(in.ByteCount(), Eq(15u));
+
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(5u));
+ ASSERT_THAT(buffer, NotNull());
+ ASSERT_THAT(in.ByteCount(), Eq(20u));
+ EXPECT_THAT(StringPiece(buffer, size), Eq("strin"));
+
+ // Backup 1 more than possible. Should clamp.
+ in.BackUp(11u);
+ EXPECT_THAT(in.ByteCount(), Eq(10u));
+
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(10u));
+ ASSERT_THAT(buffer, NotNull());
+ ASSERT_THAT(in.ByteCount(), Eq(20u));
+ EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(1u));
+ ASSERT_THAT(buffer, NotNull());
+ ASSERT_THAT(in.ByteCount(), Eq(21u));
+ EXPECT_THAT(StringPiece(buffer, size), Eq("g"));
+
+ EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ EXPECT_FALSE(in.HadError());
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/StringInputStream.cpp b/tools/aapt2/io/StringInputStream.cpp
new file mode 100644
index 000000000000..51a18a7d8a9f
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "io/StringInputStream.h"
+
+using ::android::StringPiece;
+
+namespace aapt {
+namespace io {
+
+StringInputStream::StringInputStream(const StringPiece& str) : str_(str), offset_(0u) {
+}
+
+bool StringInputStream::Next(const void** data, size_t* size) {
+ if (offset_ == str_.size()) {
+ return false;
+ }
+
+ *data = str_.data() + offset_;
+ *size = str_.size() - offset_;
+ offset_ = str_.size();
+ return true;
+}
+
+void StringInputStream::BackUp(size_t count) {
+ if (count > offset_) {
+ count = offset_;
+ }
+ offset_ -= count;
+}
+
+size_t StringInputStream::ByteCount() const {
+ return offset_;
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/StringInputStream.h b/tools/aapt2/io/StringInputStream.h
new file mode 100644
index 000000000000..ff5b112ef274
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream.h
@@ -0,0 +1,56 @@
+/*
+ * 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_IO_STRINGINPUTSTREAM_H
+#define AAPT_IO_STRINGINPUTSTREAM_H
+
+#include "io/Io.h"
+
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
+namespace aapt {
+namespace io {
+
+class StringInputStream : public InputStream {
+ public:
+ explicit StringInputStream(const android::StringPiece& str);
+
+ bool Next(const void** data, size_t* size) override;
+
+ void BackUp(size_t count) override;
+
+ size_t ByteCount() const override;
+
+ inline bool HadError() const override {
+ return false;
+ }
+
+ inline std::string GetError() const override {
+ return {};
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringInputStream);
+
+ android::StringPiece str_;
+ size_t offset_;
+};
+
+} // namespace io
+} // namespace aapt
+
+#endif // AAPT_IO_STRINGINPUTSTREAM_H
diff --git a/tools/aapt2/io/StringInputStream_test.cpp b/tools/aapt2/io/StringInputStream_test.cpp
new file mode 100644
index 000000000000..cc57bc498313
--- /dev/null
+++ b/tools/aapt2/io/StringInputStream_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "io/StringInputStream.h"
+
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
+namespace aapt {
+namespace io {
+
+TEST(StringInputStreamTest, OneCallToNextShouldReturnEntireBuffer) {
+ constexpr const size_t kCount = 1000;
+ std::string input;
+ input.resize(kCount, 0x7f);
+ input[0] = 0x00;
+ input[kCount - 1] = 0xff;
+ StringInputStream in(input);
+
+ const char* buffer;
+ size_t size;
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(kCount));
+ ASSERT_THAT(buffer, NotNull());
+
+ EXPECT_THAT(buffer[0], Eq(0x00));
+ EXPECT_THAT(buffer[kCount - 1], Eq('\xff'));
+
+ EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ EXPECT_FALSE(in.HadError());
+}
+
+TEST(StringInputStreamTest, BackUp) {
+ std::string input = "hello this is a string";
+ StringInputStream in(input);
+
+ const char* buffer;
+ size_t size;
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(input.size()));
+ ASSERT_THAT(buffer, NotNull());
+ EXPECT_THAT(in.ByteCount(), Eq(input.size()));
+
+ in.BackUp(6u);
+ EXPECT_THAT(in.ByteCount(), Eq(input.size() - 6u));
+
+ ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+ ASSERT_THAT(size, Eq(6u));
+ ASSERT_THAT(buffer, NotNull());
+ ASSERT_THAT(buffer, StrEq("string"));
+ EXPECT_THAT(in.ByteCount(), Eq(input.size()));
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 0cec9ae407f5..d7508d257db4 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -39,6 +39,17 @@ void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final,
*out << prefix << "}";
}
+ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
+ Result result = Result::kAdded;
+ auto iter = members_.find(member);
+ if (iter != members_.end()) {
+ members_.erase(iter);
+ result = Result::kOverridden;
+ }
+ members_.insert(std::move(member));
+ return result;
+}
+
bool ClassDefinition::empty() const {
for (const std::unique_ptr<ClassMember>& member : members_) {
if (!member->empty()) {
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index ca76421390d6..6c4bcad83d2a 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -18,6 +18,7 @@
#define AAPT_JAVA_CLASSDEFINITION_H
#include <ostream>
+#include <set>
#include <string>
#include "android-base/macros.h"
@@ -37,10 +38,14 @@ class ClassMember {
public:
virtual ~ClassMember() = default;
- AnnotationProcessor* GetCommentBuilder() { return &processor_; }
+ AnnotationProcessor* GetCommentBuilder() {
+ return &processor_;
+ }
virtual bool empty() const = 0;
+ virtual const std::string& GetName() const = 0;
+
// Writes the class member to the out stream. Subclasses should derive this method
// to write their own data. Call this base method from the subclass to write out
// this member's comments/annotations.
@@ -57,7 +62,13 @@ class PrimitiveMember : public ClassMember {
PrimitiveMember(const android::StringPiece& name, const T& val)
: name_(name.to_string()), val_(val) {}
- bool empty() const override { return false; }
+ bool empty() const override {
+ return false;
+ }
+
+ const std::string& GetName() const override {
+ return name_;
+ }
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override {
@@ -83,7 +94,13 @@ class PrimitiveMember<std::string> : public ClassMember {
PrimitiveMember(const android::StringPiece& name, const std::string& val)
: name_(name.to_string()), val_(val) {}
- bool empty() const override { return false; }
+ bool empty() const override {
+ return false;
+ }
+
+ const std::string& GetName() const override {
+ return name_;
+ }
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override {
@@ -109,9 +126,17 @@ class PrimitiveArrayMember : public ClassMember {
public:
explicit PrimitiveArrayMember(const android::StringPiece& name) : name_(name.to_string()) {}
- void AddElement(const T& val) { elements_.push_back(val); }
+ void AddElement(const T& val) {
+ elements_.push_back(val);
+ }
- bool empty() const override { return false; }
+ bool empty() const override {
+ return false;
+ }
+
+ const std::string& GetName() const override {
+ return name_;
+ }
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override {
@@ -154,6 +179,11 @@ class MethodDefinition : public ClassMember {
// formatting may be broken.
void AppendStatement(const android::StringPiece& statement);
+ // Not quite the same as a name, but good enough.
+ const std::string& GetName() const override {
+ return signature_;
+ }
+
// Even if the method is empty, we always want to write the method signature.
bool empty() const override { return false; }
@@ -175,19 +205,34 @@ class ClassDefinition : public ClassMember {
ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
: name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
- void AddMember(std::unique_ptr<ClassMember> member) {
- members_.push_back(std::move(member));
- }
+ enum class Result {
+ kAdded,
+ kOverridden,
+ };
+
+ Result AddMember(std::unique_ptr<ClassMember> member);
bool empty() const override;
+
+ const std::string& GetName() const override {
+ return name_;
+ }
+
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override;
private:
+ struct ClassMemberCompare {
+ using T = std::unique_ptr<ClassMember>;
+ bool operator()(const T& a, const T& b) const {
+ return a->GetName() < b->GetName();
+ }
+ };
+
std::string name_;
ClassQualifier qualifier_;
bool create_if_empty_;
- std::vector<std::unique_ptr<ClassMember>> members_;
+ std::set<std::unique_ptr<ClassMember>, ClassMemberCompare> members_;
DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
};
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index f49e4985fcf1..cad4c6c7c94f 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -81,7 +81,10 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag,
util::make_unique<StringMember>(result.value(), attr->value);
string_member->GetCommentBuilder()->AppendComment(el->comment);
- class_def->AddMember(std::move(string_member));
+ if (class_def->AddMember(std::move(string_member)) == ClassDefinition::Result::kOverridden) {
+ diag->Warn(DiagMessage(source.WithLine(el->line_number))
+ << "duplicate definitions of '" << result.value() << "', overriding previous");
+ }
return true;
}
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index 9f6ec210a6a7..44b6a1ffd5ae 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -112,6 +112,21 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
EXPECT_THAT(actual, HasSubstr(expected_secret));
}
+// This is bad but part of public API behaviour so we need to preserve it.
+TEST(ManifestClassGeneratorTest, LastSeenPermissionWithSameLeafNameTakesPrecedence) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
+ </manifest>)");
+
+ std::string actual;
+ ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
+ EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
+ EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
+}
+
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
std::string* out_str) {
std::unique_ptr<ClassDefinition> manifest_class =
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 5f61faeeebe7..10c46101123c 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -29,18 +29,12 @@ namespace proguard {
class BaseVisitor : public xml::Visitor {
public:
- BaseVisitor(const Source& source, KeepSet* keep_set)
- : source_(source), keep_set_(keep_set) {}
+ using xml::Visitor::Visit;
- virtual void Visit(xml::Text*) override{};
-
- virtual void Visit(xml::Namespace* node) override {
- for (const auto& child : node->children) {
- child->Accept(this);
- }
+ BaseVisitor(const Source& source, KeepSet* keep_set) : source_(source), keep_set_(keep_set) {
}
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
if (!node->namespace_uri.empty()) {
Maybe<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(node->namespace_uri);
@@ -78,10 +72,10 @@ class BaseVisitor : public xml::Visitor {
class LayoutVisitor : public BaseVisitor {
public:
- LayoutVisitor(const Source& source, KeepSet* keep_set)
- : BaseVisitor(source, keep_set) {}
+ LayoutVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ }
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
bool check_class = false;
bool check_name = false;
if (node->namespace_uri.empty()) {
@@ -119,7 +113,7 @@ class MenuVisitor : public BaseVisitor {
MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
}
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
if (node->namespace_uri.empty() && node->name == "item") {
for (const auto& attr : node->attributes) {
if (attr.namespace_uri == xml::kSchemaAndroid) {
@@ -142,10 +136,10 @@ class MenuVisitor : public BaseVisitor {
class XmlResourceVisitor : public BaseVisitor {
public:
- XmlResourceVisitor(const Source& source, KeepSet* keep_set)
- : BaseVisitor(source, keep_set) {}
+ XmlResourceVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ }
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
bool check_fragment = false;
if (node->namespace_uri.empty()) {
check_fragment =
@@ -169,13 +163,12 @@ class XmlResourceVisitor : public BaseVisitor {
class TransitionVisitor : public BaseVisitor {
public:
- TransitionVisitor(const Source& source, KeepSet* keep_set)
- : BaseVisitor(source, keep_set) {}
+ TransitionVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ }
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
bool check_class =
- node->namespace_uri.empty() &&
- (node->name == "transition" || node->name == "pathMotion");
+ node->namespace_uri.empty() && (node->name == "transition" || node->name == "pathMotion");
if (check_class) {
xml::Attribute* attr = node->FindAttribute({}, "class");
if (attr && util::IsJavaClassName(attr->value)) {
@@ -195,7 +188,7 @@ class ManifestVisitor : public BaseVisitor {
ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
: BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
- virtual void Visit(xml::Element* node) override {
+ void Visit(xml::Element* node) override {
if (node->namespace_uri.empty()) {
bool get_name = false;
if (node->name == "manifest") {
@@ -205,18 +198,15 @@ class ManifestVisitor : public BaseVisitor {
}
} else if (node->name == "application") {
get_name = true;
- xml::Attribute* attr =
- node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
+ xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
if (attr) {
- Maybe<std::string> result =
- util::GetFullyQualifiedClassName(package_, attr->value);
+ Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value());
}
}
if (main_dex_only_) {
- xml::Attribute* default_process =
- node->FindAttribute(xml::kSchemaAndroid, "process");
+ xml::Attribute* default_process = node->FindAttribute(xml::kSchemaAndroid, "process");
if (default_process) {
default_process_ = default_process->value;
}
@@ -226,8 +216,7 @@ class ManifestVisitor : public BaseVisitor {
get_name = true;
if (main_dex_only_) {
- xml::Attribute* component_process =
- node->FindAttribute(xml::kSchemaAndroid, "process");
+ xml::Attribute* component_process = node->FindAttribute(xml::kSchemaAndroid, "process");
const std::string& process =
component_process ? component_process->value : default_process_;
@@ -242,8 +231,7 @@ class ManifestVisitor : public BaseVisitor {
get_name = attr != nullptr;
if (get_name) {
- Maybe<std::string> result =
- util::GetFullyQualifiedClassName(package_, attr->value);
+ Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value());
}
@@ -261,8 +249,7 @@ class ManifestVisitor : public BaseVisitor {
std::string default_process_;
};
-bool CollectProguardRulesForManifest(const Source& source,
- xml::XmlResource* res, KeepSet* keep_set,
+bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keep_set,
bool main_dex_only) {
ManifestVisitor visitor(source, keep_set, main_dex_only);
if (res->root) {
@@ -272,8 +259,7 @@ bool CollectProguardRulesForManifest(const Source& source,
return false;
}
-bool CollectProguardRules(const Source& source, xml::XmlResource* res,
- KeepSet* keep_set) {
+bool CollectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keep_set) {
if (!res->root) {
return false;
}
@@ -321,8 +307,7 @@ bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
for (const Source& source : entry.second) {
*out << "# Referenced at " << source << "\n";
}
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n"
- << std::endl;
+ *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
}
return true;
}
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 80edb352f42c..da7f410b8b08 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -122,7 +122,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
xml::Element* el;
xml::Attribute* attr;
- el = xml::FindRootElement(doc.get());
+ el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
@@ -141,7 +141,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
options);
ASSERT_NE(nullptr, doc);
- el = xml::FindRootElement(doc.get());
+ el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
@@ -160,7 +160,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
options);
ASSERT_NE(nullptr, doc);
- el = xml::FindRootElement(doc.get());
+ el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
@@ -177,7 +177,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
options);
ASSERT_NE(nullptr, doc);
- el = xml::FindRootElement(doc.get());
+ el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
@@ -199,7 +199,7 @@ TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
options);
ASSERT_NE(nullptr, doc);
- xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
ASSERT_EQ("manifest", manifest_el->name);
@@ -248,7 +248,7 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
options);
ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::FindRootElement(doc.get());
+ xml::Element* manifestEl = doc->root.get();
ASSERT_NE(nullptr, manifestEl);
xml::Attribute* attr = nullptr;
@@ -297,7 +297,7 @@ TEST_F(ManifestFixerTest,
options);
ASSERT_NE(nullptr, doc);
- xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
xml::Element* instrumentation_el =
@@ -321,7 +321,7 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
options);
ASSERT_NE(nullptr, doc);
- xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
xml::Attribute* attr =
@@ -344,7 +344,7 @@ TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
Verify("<manifest package=\"android\" coreApp=\"true\" />");
ASSERT_NE(nullptr, doc);
- xml::Element* el = xml::FindRootElement(doc.get());
+ xml::Element* el = doc->root.get();
ASSERT_NE(nullptr, el);
EXPECT_EQ("manifest", el->name);
diff --git a/tools/aapt2/link/XmlCompatVersioner.cpp b/tools/aapt2/link/XmlCompatVersioner.cpp
index f1f4e3b7f05f..20ebdc696814 100644
--- a/tools/aapt2/link/XmlCompatVersioner.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner.cpp
@@ -107,7 +107,7 @@ std::unique_ptr<xml::XmlResource> XmlCompatVersioner::ProcessDoc(
std::unique_ptr<xml::XmlResource> cloned_doc = util::make_unique<xml::XmlResource>(doc->file);
cloned_doc->file.config.sdkVersion = static_cast<uint16_t>(target_api);
- cloned_doc->root = doc->root->Clone([&](const xml::Element& el, xml::Element* out_el) {
+ cloned_doc->root = doc->root->CloneElement([&](const xml::Element& el, xml::Element* out_el) {
for (const auto& attr : el.attributes) {
if (!attr.compiled_attribute) {
// Just copy if this isn't a compiled attribute.
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index ce6605c6ad97..29ad25f79ab9 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -19,6 +19,12 @@
#include "Linkers.h"
#include "test/Test.h"
+using ::aapt::test::ValueEq;
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+
namespace aapt {
constexpr auto TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
@@ -68,12 +74,12 @@ class XmlCompatVersionerTest : public ::testing::Test {
};
TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) {
- auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="24dp"
app:foo="16dp"
- foo="bar"/>)EOF");
+ foo="bar"/>)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
@@ -84,35 +90,35 @@ TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) {
XmlCompatVersioner versioner(&rules);
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
versioner.Process(context_.get(), doc.get(), api_range);
- ASSERT_EQ(2u, versioned_docs.size());
+ ASSERT_THAT(versioned_docs, SizeIs(2u));
xml::Element* el;
// Source XML file's sdkVersion == 0, so the first one must also have the same sdkVersion.
- EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[0].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(2u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
- EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
-
- EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[1].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(3u, el->attributes.size());
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
- EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+ EXPECT_THAT(versioned_docs[0]->file.config.sdkVersion, Eq(0u));
+ el = versioned_docs[0]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(2u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), IsNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
+ EXPECT_THAT(el->FindAttribute({}, "foo"), NotNull());
+
+ EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
+ el = versioned_docs[1]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(3u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), NotNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
+ EXPECT_THAT(el->FindAttribute({}, "foo"), NotNull());
}
TEST_F(XmlCompatVersionerTest, SingleRule) {
- auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="24dp"
app:foo="16dp"
- foo="bar"/>)EOF");
+ foo="bar"/>)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
@@ -129,51 +135,51 @@ TEST_F(XmlCompatVersionerTest, SingleRule) {
XmlCompatVersioner versioner(&rules);
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
versioner.Process(context_.get(), doc.get(), api_range);
- ASSERT_EQ(2u, versioned_docs.size());
+ ASSERT_THAT(versioned_docs, SizeIs(2u));
xml::Element* el;
- EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[0].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(4u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
- EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+ EXPECT_THAT(versioned_docs[0]->file.config.sdkVersion, Eq(0u));
+ el = versioned_docs[0]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(4u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), IsNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
+ EXPECT_THAT(el->FindAttribute({}, "foo"), NotNull());
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[1].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(5u, el->attributes.size());
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
- EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
- EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+ EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
+ el = versioned_docs[1]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(5u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), NotNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
+ EXPECT_THAT(el->FindAttribute({}, "foo"), NotNull());
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
}
TEST_F(XmlCompatVersionerTest, ChainedRule) {
- auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingHorizontal="24dp" />)EOF");
+ android:paddingHorizontal="24dp" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
@@ -193,71 +199,70 @@ TEST_F(XmlCompatVersionerTest, ChainedRule) {
XmlCompatVersioner versioner(&rules);
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
versioner.Process(context_.get(), doc.get(), api_range);
- ASSERT_EQ(3u, versioned_docs.size());
+ ASSERT_THAT(versioned_docs, SizeIs(3u));
xml::Element* el;
- EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[0].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(2u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+ EXPECT_THAT(versioned_docs[0]->file.config.sdkVersion, Eq(0u));
+ el = versioned_docs[0]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(2u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), IsNull());
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- EXPECT_EQ(static_cast<uint16_t>(SDK_HONEYCOMB), versioned_docs[1]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[1].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(1u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
+ EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_HONEYCOMB));
+ el = versioned_docs[1]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(1u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), IsNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"), IsNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingRight"), IsNull());
attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[2]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[2].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(2u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
+ EXPECT_THAT(versioned_docs[2]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
+ el = versioned_docs[2]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(2u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"), IsNull());
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingRight"), IsNull());
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
}
TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) {
- auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ auto doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingHorizontal="24dp"
android:paddingLeft="16dp"
- android:paddingRight="16dp"/>)EOF");
+ android:paddingRight="16dp"/>)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- Item* padding_horizontal_value = xml::FindRootElement(doc.get())
- ->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")
- ->compiled_value.get();
- ASSERT_NE(nullptr, padding_horizontal_value);
+ Item* padding_horizontal_value =
+ doc->root->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")->compiled_value.get();
+ ASSERT_THAT(padding_horizontal_value, NotNull());
XmlCompatVersioner::Rules rules;
rules[R::attr::paddingHorizontal] =
@@ -271,50 +276,50 @@ TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) {
XmlCompatVersioner versioner(&rules);
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
versioner.Process(context_.get(), doc.get(), api_range);
- ASSERT_EQ(2u, versioned_docs.size());
+ ASSERT_THAT(versioned_docs, SizeIs(2u));
xml::Element* el;
- EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[0].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(2u, el->attributes.size());
- EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+ EXPECT_THAT(versioned_docs[0]->file.config.sdkVersion, Eq(0u));
+ el = versioned_docs[0]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(2u));
+ EXPECT_THAT(el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"), IsNull());
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
- ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
+ ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
- ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
+ ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
- EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
- el = xml::FindRootElement(versioned_docs[1].get());
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(3u, el->attributes.size());
+ EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
+ el = versioned_docs[1]->root.get();
+ ASSERT_THAT(el, NotNull());
+ EXPECT_THAT(el->attributes, SizeIs(3u));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
- ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
+ ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
- ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
+ ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
- ASSERT_NE(nullptr, attr);
- ASSERT_NE(nullptr, attr->compiled_value);
- ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
+ ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
}
} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp
index 24aa5660ae30..b5e2423d58dc 100644
--- a/tools/aapt2/link/XmlNamespaceRemover.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover.cpp
@@ -24,37 +24,19 @@ namespace aapt {
namespace {
-/**
- * Visits each xml Node, removing URI references and nested namespaces.
- */
+// Visits each xml Node, removing URI references and nested namespaces.
class XmlVisitor : public xml::Visitor {
public:
explicit XmlVisitor(bool keep_uris) : keep_uris_(keep_uris) {}
void Visit(xml::Element* el) override {
- // Strip namespaces
- for (auto& child : el->children) {
- while (child && xml::NodeCast<xml::Namespace>(child.get())) {
- if (child->children.empty()) {
- child = {};
- } else {
- child = std::move(child->children.front());
- child->parent = el;
- }
- }
- }
- el->children.erase(
- std::remove_if(el->children.begin(), el->children.end(),
- [](const std::unique_ptr<xml::Node>& child) -> bool {
- return child == nullptr;
- }),
- el->children.end());
+ el->namespace_decls.clear();
if (!keep_uris_) {
for (xml::Attribute& attr : el->attributes) {
- attr.namespace_uri = std::string();
+ attr.namespace_uri.clear();
}
- el->namespace_uri = std::string();
+ el->namespace_uri.clear();
}
xml::Visitor::Visit(el);
}
@@ -67,19 +49,11 @@ class XmlVisitor : public xml::Visitor {
} // namespace
-bool XmlNamespaceRemover::Consume(IAaptContext* context,
- xml::XmlResource* resource) {
+bool XmlNamespaceRemover::Consume(IAaptContext* context, xml::XmlResource* resource) {
if (!resource->root) {
return false;
}
- // Replace any root namespaces until the root is a non-namespace node
- while (xml::NodeCast<xml::Namespace>(resource->root.get())) {
- if (resource->root->children.empty()) {
- break;
- }
- resource->root = std::move(resource->root->children.front());
- resource->root->parent = nullptr;
- }
+
XmlVisitor visitor(keep_uris_);
resource->root->Accept(&visitor);
return true;
diff --git a/tools/aapt2/link/XmlNamespaceRemover_test.cpp b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
index a176c03a6432..df4920fde37f 100644
--- a/tools/aapt2/link/XmlNamespaceRemover_test.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
@@ -18,6 +18,10 @@
#include "test/Test.h"
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
namespace aapt {
class XmlUriTestVisitor : public xml::Visitor {
@@ -25,16 +29,14 @@ class XmlUriTestVisitor : public xml::Visitor {
XmlUriTestVisitor() = default;
void Visit(xml::Element* el) override {
+ EXPECT_THAT(el->namespace_decls, SizeIs(0u));
+
for (const auto& attr : el->attributes) {
- EXPECT_EQ(std::string(), attr.namespace_uri);
+ EXPECT_THAT(attr.namespace_uri, StrEq(""));
}
- EXPECT_EQ(std::string(), el->namespace_uri);
- xml::Visitor::Visit(el);
- }
+ EXPECT_THAT(el->namespace_uri, StrEq(""));
- void Visit(xml::Namespace* ns) override {
- EXPECT_EQ(std::string(), ns->namespace_uri);
- xml::Visitor::Visit(ns);
+ xml::Visitor::Visit(el);
}
private:
@@ -45,10 +47,9 @@ class XmlNamespaceTestVisitor : public xml::Visitor {
public:
XmlNamespaceTestVisitor() = default;
- void Visit(xml::Namespace* ns) override {
- ADD_FAILURE() << "Detected namespace: " << ns->namespace_prefix << "=\""
- << ns->namespace_uri << "\"";
- xml::Visitor::Visit(ns);
+ void Visit(xml::Element* el) override {
+ EXPECT_THAT(el->namespace_decls, SizeIs(0u));
+ xml::Visitor::Visit(el);
}
private:
@@ -58,8 +59,7 @@ class XmlNamespaceTestVisitor : public xml::Visitor {
class XmlNamespaceRemoverTest : public ::testing::Test {
public:
void SetUp() override {
- context_ =
- test::ContextBuilder().SetCompilationPackage("com.app.test").Build();
+ context_ = test::ContextBuilder().SetCompilationPackage("com.app.test").Build();
}
protected:
@@ -75,8 +75,8 @@ TEST_F(XmlNamespaceRemoverTest, RemoveUris) {
XmlNamespaceRemover remover;
ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
XmlUriTestVisitor visitor;
root->Accept(&visitor);
@@ -93,8 +93,8 @@ TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) {
XmlNamespaceRemover remover;
ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
XmlNamespaceTestVisitor visitor;
root->Accept(&visitor);
@@ -112,8 +112,8 @@ TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) {
XmlNamespaceRemover remover;
ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
XmlNamespaceTestVisitor visitor;
root->Accept(&visitor);
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 721fc26399bc..bcecd2003846 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -144,7 +144,9 @@ class XmlVisitor : public xml::PackageAwareVisitor {
xml::PackageAwareVisitor::Visit(el);
}
- bool HasError() { return error_ || reference_visitor_.HasError(); }
+ bool HasError() {
+ return error_ || reference_visitor_.HasError();
+ }
private:
DISALLOW_COPY_AND_ASSIGN(XmlVisitor);
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 228cfb9e3d66..ef99355e5b5f 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -79,20 +79,20 @@ class XmlReferenceLinkerTest : public ::testing::Test {
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:background="@color/green"
- android:text="hello"
- android:attr="\?hello"
- nonAaptAttr="1"
- nonAaptAttrRef="@id/id"
- class="hello" />)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:background="@color/green"
+ android:text="hello"
+ android:attr="\?hello"
+ nonAaptAttr="1"
+ nonAaptAttrRef="@id/id"
+ class="hello" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* view_el = xml::FindRootElement(doc.get());
+ xml::Element* view_el = doc->root.get();
ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
@@ -138,32 +138,32 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
}
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:android="http://schemas.android.com/apk/res/android"
- android:colorAccent="@android:color/hidden" />)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:colorAccent="@android:color/hidden" />)");
XmlReferenceLinker linker;
ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
}
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:colorAccent="@*android:color/hidden" />)EOF");
+ android:colorAccent="@*android:color/hidden" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
}
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
- support:colorAccent="#ff0000" />)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
+ support:colorAccent="#ff0000" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* view_el = xml::FindRootElement(doc.get());
+ xml::Element* view_el = doc->root.get();
ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr =
@@ -175,14 +175,14 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
}
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:app="http://schemas.android.com/apk/res-auto"
- app:colorAccent="@app:color/red" />)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:app="http://schemas.android.com/apk/res-auto"
+ app:colorAccent="@app:color/red" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* view_el = xml::FindRootElement(doc.get());
+ xml::Element* view_el = doc->root.get();
ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
@@ -196,17 +196,15 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:app="http://schemas.android.com/apk/res/android"
- app:attr="@app:id/id">
- <View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
- app:attr="@app:id/id"/>
- </View>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
+ <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
+ </View>)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* view_el = xml::FindRootElement(doc.get());
+ xml::Element* view_el = doc->root.get();
ASSERT_THAT(view_el, NotNull());
// All attributes and references in this element should be referring to
@@ -235,14 +233,14 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
- <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
- android:attr="@id/id"/>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
+ android:attr="@id/id"/>)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* view_el = xml::FindRootElement(doc.get());
+ xml::Element* view_el = doc->root.get();
ASSERT_THAT(view_el, NotNull());
// All attributes and references in this element should be referring to
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
index 6b21364b5eb2..aa99c982f6ae 100644
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -36,7 +36,7 @@ void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source*
StringPool::Ref ref = src_pool->MakeRef(source.path);
out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
if (source.line) {
- out_pb_source->set_line_no(static_cast<uint32_t>(source.line.value()));
+ out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
}
}
@@ -46,29 +46,28 @@ void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStri
out_source->path = util::GetString(src_pool, pb_source.path_idx());
}
- if (pb_source.has_line_no()) {
- out_source->line = static_cast<size_t>(pb_source.line_no());
+ if (pb_source.has_position()) {
+ out_source->line = static_cast<size_t>(pb_source.position().line_number());
}
}
pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
switch (state) {
case SymbolState::kPrivate:
- return pb::SymbolStatus_Visibility_Private;
+ return pb::SymbolStatus_Visibility_PRIVATE;
case SymbolState::kPublic:
- return pb::SymbolStatus_Visibility_Public;
+ return pb::SymbolStatus_Visibility_PUBLIC;
default:
break;
}
- return pb::SymbolStatus_Visibility_Unknown;
+ return pb::SymbolStatus_Visibility_UNKNOWN;
}
-SymbolState DeserializeVisibilityFromPb(
- pb::SymbolStatus_Visibility pb_visibility) {
+SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility) {
switch (pb_visibility) {
- case pb::SymbolStatus_Visibility_Private:
+ case pb::SymbolStatus_Visibility_PRIVATE:
return SymbolState::kPrivate;
- case pb::SymbolStatus_Visibility_Public:
+ case pb::SymbolStatus_Visibility_PUBLIC:
return SymbolState::kPublic;
default:
break;
@@ -102,20 +101,20 @@ bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
switch (type) {
case Reference::Type::kResource:
- return pb::Reference_Type_Ref;
+ return pb::Reference_Type_REFERENCE;
case Reference::Type::kAttribute:
- return pb::Reference_Type_Attr;
+ return pb::Reference_Type_ATTRIBUTE;
default:
break;
}
- return pb::Reference_Type_Ref;
+ return pb::Reference_Type_REFERENCE;
}
Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
switch (pb_type) {
- case pb::Reference_Type_Ref:
+ case pb::Reference_Type_REFERENCE:
return Reference::Type::kResource;
- case pb::Reference_Type_Attr:
+ case pb::Reference_Type_ATTRIBUTE:
return Reference::Type::kAttribute;
default:
break;
@@ -126,32 +125,32 @@ Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
switch (plural_idx) {
case Plural::Zero:
- return pb::Plural_Arity_Zero;
+ return pb::Plural_Arity_ZERO;
case Plural::One:
- return pb::Plural_Arity_One;
+ return pb::Plural_Arity_ONE;
case Plural::Two:
- return pb::Plural_Arity_Two;
+ return pb::Plural_Arity_TWO;
case Plural::Few:
- return pb::Plural_Arity_Few;
+ return pb::Plural_Arity_FEW;
case Plural::Many:
- return pb::Plural_Arity_Many;
+ return pb::Plural_Arity_MANY;
default:
break;
}
- return pb::Plural_Arity_Other;
+ return pb::Plural_Arity_OTHER;
}
size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity) {
switch (arity) {
- case pb::Plural_Arity_Zero:
+ case pb::Plural_Arity_ZERO:
return Plural::Zero;
- case pb::Plural_Arity_One:
+ case pb::Plural_Arity_ONE:
return Plural::One;
- case pb::Plural_Arity_Two:
+ case pb::Plural_Arity_TWO:
return Plural::Two;
- case pb::Plural_Arity_Few:
+ case pb::Plural_Arity_FEW:
return Plural::Few;
- case pb::Plural_Arity_Many:
+ case pb::Plural_Arity_MANY:
return Plural::Many;
default:
break;
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
index 344e9477ea71..2f268f44752c 100644
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -23,27 +23,23 @@
#include "ResourceTable.h"
#include "Source.h"
#include "StringPool.h"
-#include "Format.pb.h"
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
namespace aapt {
-void SerializeStringPoolToPb(const StringPool& pool,
- pb::StringPool* out_pb_pool);
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool);
-void SerializeSourceToPb(const Source& source, StringPool* src_pool,
- pb::Source* out_pb_source);
+void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source);
-void DeserializeSourceFromPb(const pb::Source& pb_source,
- const android::ResStringPool& src_pool,
+void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
Source* out_source);
pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state);
-SymbolState DeserializeVisibilityFromPb(
- pb::SymbolStatus_Visibility pb_visibility);
+SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility);
-void SerializeConfig(const ConfigDescription& config,
- pb::ConfigDescription* out_pb_config);
+void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config);
bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
ConfigDescription* out_config);
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
index 39c50038d599..8c46642e9090 100644
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -30,11 +30,10 @@ namespace aapt {
class CompiledFileOutputStream {
public:
- explicit CompiledFileOutputStream(
- google::protobuf::io::ZeroCopyOutputStream* out);
+ explicit CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out);
void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::CompiledFile* compiledFile);
+ void WriteCompiledFile(const pb::internal::CompiledFile* compiledFile);
void WriteData(const BigBuffer* buffer);
void WriteData(const void* data, size_t len);
bool HadError();
@@ -52,7 +51,7 @@ class CompiledFileInputStream {
explicit CompiledFileInputStream(const void* data, size_t size);
bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::CompiledFile* outVal);
+ bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
private:
@@ -64,13 +63,12 @@ class CompiledFileInputStream {
};
std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table);
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(
- const pb::ResourceTable& pbTable, const Source& source, IDiagnostics* diag);
+std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pbTable,
+ const Source& source, IDiagnostics* diag);
-std::unique_ptr<pb::CompiledFile> SerializeCompiledFileToPb(
- const ResourceFile& file);
+std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file);
std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
+ const pb::internal::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 37d5ed0cf59d..b9d5878f2f71 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -55,66 +55,61 @@ class ReferenceIdToNameVisitor : public ValueVisitor {
class PackagePbDeserializer {
public:
- PackagePbDeserializer(const android::ResStringPool* valuePool,
- const android::ResStringPool* sourcePool,
- const android::ResStringPool* symbolPool,
- const Source& source, IDiagnostics* diag)
- : value_pool_(valuePool),
- source_pool_(sourcePool),
- symbol_pool_(symbolPool),
- source_(source),
- diag_(diag) {}
+ PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source,
+ IDiagnostics* diag)
+ : source_pool_(sourcePool), source_(source), diag_(diag) {
+ }
public:
- bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
+ bool DeserializeFromPb(const pb::Package& pb_package, ResourceTable* table) {
Maybe<uint8_t> id;
- if (pbPackage.has_package_id()) {
- id = static_cast<uint8_t>(pbPackage.package_id());
+ if (pb_package.has_package_id()) {
+ id = static_cast<uint8_t>(pb_package.package_id());
}
- std::map<ResourceId, ResourceNameRef> idIndex;
+ std::map<ResourceId, ResourceNameRef> id_index;
- ResourceTablePackage* pkg = table->CreatePackage(pbPackage.package_name(), id);
- for (const pb::Type& pbType : pbPackage.types()) {
- const ResourceType* resType = ParseResourceType(pbType.name());
- if (!resType) {
- diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name() << "'");
+ ResourceTablePackage* pkg = table->CreatePackage(pb_package.package_name(), id);
+ for (const pb::Type& pb_type : pb_package.type()) {
+ const ResourceType* res_type = ParseResourceType(pb_type.name());
+ if (res_type == nullptr) {
+ diag_->Error(DiagMessage(source_) << "unknown type '" << pb_type.name() << "'");
return {};
}
- ResourceTableType* type = pkg->FindOrCreateType(*resType);
+ ResourceTableType* type = pkg->FindOrCreateType(*res_type);
- for (const pb::Entry& pbEntry : pbType.entries()) {
- ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name());
+ for (const pb::Entry& pb_entry : pb_type.entry()) {
+ ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
- // Deserialize the symbol status (public/private with source and
- // comments).
- if (pbEntry.has_symbol_status()) {
- const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
- if (pbStatus.has_source()) {
- DeserializeSourceFromPb(pbStatus.source(), *source_pool_, &entry->symbol_status.source);
+ // Deserialize the symbol status (public/private with source and comments).
+ if (pb_entry.has_symbol_status()) {
+ const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
+ if (pb_status.has_source()) {
+ DeserializeSourceFromPb(pb_status.source(), *source_pool_,
+ &entry->symbol_status.source);
}
- if (pbStatus.has_comment()) {
- entry->symbol_status.comment = pbStatus.comment();
+ if (pb_status.has_comment()) {
+ entry->symbol_status.comment = pb_status.comment();
}
- entry->symbol_status.allow_new = pbStatus.allow_new();
+ entry->symbol_status.allow_new = pb_status.allow_new();
- SymbolState visibility = DeserializeVisibilityFromPb(pbStatus.visibility());
+ SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
entry->symbol_status.state = visibility;
if (visibility == SymbolState::kPublic) {
// This is a public symbol, we must encode the ID now if there is one.
- if (pbEntry.has_id()) {
- entry->id = static_cast<uint16_t>(pbEntry.id());
+ if (pb_entry.has_id()) {
+ entry->id = static_cast<uint16_t>(pb_entry.id());
}
if (type->symbol_status.state != SymbolState::kPublic) {
// If the type has not been made public, do so now.
type->symbol_status.state = SymbolState::kPublic;
- if (pbType.has_id()) {
- type->id = static_cast<uint8_t>(pbType.id());
+ if (pb_type.has_id()) {
+ type->id = static_cast<uint8_t>(pb_type.id());
}
}
} else if (visibility == SymbolState::kPrivate) {
@@ -124,45 +119,44 @@ class PackagePbDeserializer {
}
}
- ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
- if (resId.is_valid()) {
- idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
+ ResourceId resid(pb_package.package_id(), pb_type.id(), pb_entry.id());
+ if (resid.is_valid()) {
+ id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
}
- for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
- const pb::ConfigDescription& pbConfig = pbConfigValue.config();
+ for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
+ const pb::ConfigDescription& pb_config = pb_config_value.config();
ConfigDescription config;
- if (!DeserializeConfigDescriptionFromPb(pbConfig, &config)) {
+ if (!DeserializeConfigDescriptionFromPb(pb_config, &config)) {
diag_->Error(DiagMessage(source_) << "invalid configuration");
return {};
}
- ResourceConfigValue* configValue = entry->FindOrCreateValue(config, pbConfig.product());
- if (configValue->value) {
+ ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
+ if (config_value->value) {
// Duplicate config.
diag_->Error(DiagMessage(source_) << "duplicate configuration");
return {};
}
- configValue->value =
- DeserializeValueFromPb(pbConfigValue.value(), config, &table->string_pool);
- if (!configValue->value) {
+ config_value->value =
+ DeserializeValueFromPb(pb_config_value.value(), config, &table->string_pool);
+ if (!config_value->value) {
return {};
}
}
}
}
- ReferenceIdToNameVisitor visitor(&idIndex);
+ ReferenceIdToNameVisitor visitor(&id_index);
VisitAllValuesInPackage(pkg, &visitor);
return true;
}
private:
std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
- const ConfigDescription& config,
- StringPool* pool) {
+ const ConfigDescription& config, StringPool* pool) {
if (pb_item.has_ref()) {
const pb::Reference& pb_ref = pb_item.ref();
std::unique_ptr<Reference> ref = util::make_unique<Reference>();
@@ -173,45 +167,32 @@ class PackagePbDeserializer {
} else if (pb_item.has_prim()) {
const pb::Primitive& pb_prim = pb_item.prim();
- android::Res_value prim = {};
- prim.dataType = static_cast<uint8_t>(pb_prim.type());
- prim.data = pb_prim.data();
- return util::make_unique<BinaryPrimitive>(prim);
+ return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
+ pb_prim.data());
} else if (pb_item.has_id()) {
return util::make_unique<Id>();
} else if (pb_item.has_str()) {
- const uint32_t idx = pb_item.str().idx();
- const std::string str = util::GetString(*value_pool_, idx);
-
- const android::ResStringPool_span* spans = value_pool_->styleAt(idx);
- if (spans && spans->name.index != android::ResStringPool_span::END) {
- StyleString style_str = {str};
- while (spans->name.index != android::ResStringPool_span::END) {
- style_str.spans.push_back(
- Span{util::GetString(*value_pool_, spans->name.index),
- spans->firstChar, spans->lastChar});
- spans++;
- }
- return util::make_unique<StyledString>(pool->MakeRef(
- style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
- }
return util::make_unique<String>(
- pool->MakeRef(str, StringPool::Context(config)));
+ pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
} else if (pb_item.has_raw_str()) {
- const uint32_t idx = pb_item.raw_str().idx();
- const std::string str = util::GetString(*value_pool_, idx);
return util::make_unique<RawString>(
- pool->MakeRef(str, StringPool::Context(config)));
+ pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
+
+ } else if (pb_item.has_styled_str()) {
+ const pb::StyledString& pb_str = pb_item.styled_str();
+ StyleString style_str{pb_str.value()};
+ for (const pb::StyledString::Span& pb_span : pb_str.span()) {
+ style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
+ }
+ return util::make_unique<StyledString>(pool->MakeRef(
+ style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
} else if (pb_item.has_file()) {
- const uint32_t idx = pb_item.file().path_idx();
- const std::string str = util::GetString(*value_pool_, idx);
return util::make_unique<FileReference>(pool->MakeRef(
- str,
- StringPool::Context(StringPool::Context::kHighPriority, config)));
+ pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
} else {
diag_->Error(DiagMessage(source_) << "unknown item");
@@ -222,8 +203,6 @@ class PackagePbDeserializer {
std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
const ConfigDescription& config,
StringPool* pool) {
- const bool is_weak = pb_value.has_weak() ? pb_value.weak() : false;
-
std::unique_ptr<Value> value;
if (pb_value.has_item()) {
value = DeserializeItemFromPb(pb_value.item(), config, pool);
@@ -235,11 +214,11 @@ class PackagePbDeserializer {
const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
if (pb_compound_value.has_attr()) {
const pb::Attribute& pb_attr = pb_compound_value.attr();
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
attr->type_mask = pb_attr.format_flags();
attr->min_int = pb_attr.min_int();
attr->max_int = pb_attr.max_int();
- for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbols()) {
+ for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
Attribute::Symbol symbol;
DeserializeItemCommon(pb_symbol, &symbol.symbol);
if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
@@ -255,20 +234,18 @@ class PackagePbDeserializer {
std::unique_ptr<Style> style = util::make_unique<Style>();
if (pb_style.has_parent()) {
style->parent = Reference();
- if (!DeserializeReferenceFromPb(pb_style.parent(),
- &style->parent.value())) {
+ if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) {
return {};
}
if (pb_style.has_parent_source()) {
Source parent_source;
- DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_,
- &parent_source);
+ DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source);
style->parent.value().SetSource(std::move(parent_source));
}
}
- for (const pb::Style_Entry& pb_entry : pb_style.entries()) {
+ for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
Style::Entry entry;
DeserializeItemCommon(pb_entry, &entry.key);
if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
@@ -288,7 +265,7 @@ class PackagePbDeserializer {
} else if (pb_compound_value.has_styleable()) {
const pb::Styleable& pb_styleable = pb_compound_value.styleable();
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const pb::Styleable_Entry& pb_entry : pb_styleable.entries()) {
+ for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
Reference attr_ref;
DeserializeItemCommon(pb_entry, &attr_ref);
DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
@@ -299,25 +276,23 @@ class PackagePbDeserializer {
} else if (pb_compound_value.has_array()) {
const pb::Array& pb_array = pb_compound_value.array();
std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const pb::Array_Entry& pb_entry : pb_array.entries()) {
- std::unique_ptr<Item> item =
- DeserializeItemFromPb(pb_entry.item(), config, pool);
+ for (const pb::Array_Element& pb_entry : pb_array.element()) {
+ std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool);
if (!item) {
return {};
}
DeserializeItemCommon(pb_entry, item.get());
- array->items.push_back(std::move(item));
+ array->elements.push_back(std::move(item));
}
value = std::move(array);
} else if (pb_compound_value.has_plural()) {
const pb::Plural& pb_plural = pb_compound_value.plural();
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) {
+ for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
- plural->values[pluralIdx] =
- DeserializeItemFromPb(pb_entry.item(), config, pool);
+ plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool);
if (!plural->values[pluralIdx]) {
return {};
}
@@ -337,7 +312,7 @@ class PackagePbDeserializer {
CHECK(value) << "forgot to set value";
- value->SetWeak(is_weak);
+ value->SetWeak(pb_value.weak());
DeserializeItemCommon(pb_value, value.get());
return value;
}
@@ -350,11 +325,10 @@ class PackagePbDeserializer {
out_ref->id = ResourceId(pb_ref.id());
}
- if (pb_ref.has_symbol_idx()) {
- const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx());
+ if (pb_ref.has_name()) {
ResourceNameRef name_ref;
- if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
- diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'");
+ if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
+ diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'");
return false;
}
@@ -377,61 +351,33 @@ class PackagePbDeserializer {
}
private:
- const android::ResStringPool* value_pool_;
const android::ResStringPool* source_pool_;
- const android::ResStringPool* symbol_pool_;
const Source source_;
IDiagnostics* diag_;
};
} // namespace
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(
- const pb::ResourceTable& pb_table, const Source& source,
- IDiagnostics* diag) {
- // We import the android namespace because on Windows NO_ERROR is a macro, not
- // an enum, which
+std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table,
+ const Source& source, IDiagnostics* diag) {
+ // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
// causes errors when qualifying it with android::
using namespace android;
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- if (!pb_table.has_string_pool()) {
- diag->Error(DiagMessage(source) << "no string pool found");
- return {};
- }
-
- ResStringPool value_pool;
- status_t result = value_pool.setTo(pb_table.string_pool().data().data(),
- pb_table.string_pool().data().size());
- if (result != NO_ERROR) {
- diag->Error(DiagMessage(source) << "invalid string pool");
- return {};
- }
-
ResStringPool source_pool;
if (pb_table.has_source_pool()) {
- result = source_pool.setTo(pb_table.source_pool().data().data(),
- pb_table.source_pool().data().size());
+ status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
+ pb_table.source_pool().data().size());
if (result != NO_ERROR) {
diag->Error(DiagMessage(source) << "invalid source pool");
return {};
}
}
- ResStringPool symbol_pool;
- if (pb_table.has_symbol_pool()) {
- result = symbol_pool.setTo(pb_table.symbol_pool().data().data(),
- pb_table.symbol_pool().data().size());
- if (result != NO_ERROR) {
- diag->Error(DiagMessage(source) << "invalid symbol pool");
- return {};
- }
- }
-
- PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool,
- &symbol_pool, source, diag);
- for (const pb::Package& pb_package : pb_table.packages()) {
+ PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag);
+ for (const pb::Package& pb_package : pb_table.package()) {
if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
return {};
}
@@ -440,7 +386,7 @@ std::unique_ptr<ResourceTable> DeserializeTableFromPb(
}
std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
+ const pb::internal::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
ResourceNameRef name_ref;
@@ -456,19 +402,20 @@ std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
file->source.path = pb_file.source_path();
DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
- for (const pb::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbols()) {
- // Need to create an lvalue here so that nameRef can point to something
- // real.
- if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(),
- &name_ref)) {
+ for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
+ // Need to create an lvalue here so that nameRef can point to something real.
+ if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
diag->Error(DiagMessage(source)
<< "invalid resource name for exported symbol in "
"compiled file header: "
<< pb_file.resource_name());
return {};
}
- file->exported_symbols.push_back(
- SourcedResourceName{name_ref.ToResourceName(), pb_symbol.line_no()});
+ size_t line = 0u;
+ if (pb_symbol.has_source()) {
+ line = pb_symbol.source().line_number();
+ }
+ file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
}
return file;
}
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 730442c62836..a08df71eae1e 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -24,9 +24,9 @@
#include "android-base/logging.h"
-using google::protobuf::io::CodedOutputStream;
-using google::protobuf::io::CodedInputStream;
-using google::protobuf::io::ZeroCopyOutputStream;
+using ::google::protobuf::io::CodedInputStream;
+using ::google::protobuf::io::CodedOutputStream;
+using ::google::protobuf::io::ZeroCopyOutputStream;
namespace aapt {
@@ -36,46 +36,46 @@ class PbSerializerVisitor : public RawValueVisitor {
public:
using RawValueVisitor::Visit;
- /**
- * Constructor to use when expecting to serialize any value.
- */
- PbSerializerVisitor(StringPool* source_pool, StringPool* symbol_pool,
- pb::Value* out_pb_value)
- : source_pool_(source_pool),
- symbol_pool_(symbol_pool),
- out_pb_value_(out_pb_value),
- out_pb_item_(nullptr) {}
-
- /**
- * Constructor to use when expecting to serialize an Item.
- */
- PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool,
- pb::Item* outPbItem)
- : source_pool_(sourcePool),
- symbol_pool_(symbolPool),
- out_pb_value_(nullptr),
- out_pb_item_(outPbItem) {}
+ // Constructor to use when expecting to serialize any value.
+ PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value)
+ : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {
+ }
+
+ // Constructor to use when expecting to serialize an Item.
+ PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem)
+ : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {
+ }
void Visit(Reference* ref) override {
SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
}
void Visit(String* str) override {
- pb_item()->mutable_str()->set_idx(str->value.index());
+ pb_item()->mutable_str()->set_value(*str->value);
+ }
+
+ void Visit(RawString* str) override {
+ pb_item()->mutable_raw_str()->set_value(*str->value);
}
void Visit(StyledString* str) override {
- pb_item()->mutable_str()->set_idx(str->value.index());
+ pb::StyledString* pb_str = pb_item()->mutable_styled_str();
+ pb_str->set_value(str->value->value);
+
+ for (const StringPool::Span& span : str->value->spans) {
+ pb::StyledString::Span* pb_span = pb_str->add_span();
+ pb_span->set_tag(*span.name);
+ pb_span->set_first_char(span.first_char);
+ pb_span->set_last_char(span.last_char);
+ }
}
void Visit(FileReference* file) override {
- pb_item()->mutable_file()->set_path_idx(file->path.index());
+ pb_item()->mutable_file()->set_path(*file->path);
}
- void Visit(Id* id) override { pb_item()->mutable_id(); }
-
- void Visit(RawString* raw_str) override {
- pb_item()->mutable_raw_str()->set_idx(raw_str->value.index());
+ void Visit(Id* /*id*/) override {
+ pb_item()->mutable_id();
}
void Visit(BinaryPrimitive* prim) override {
@@ -98,7 +98,7 @@ class PbSerializerVisitor : public RawValueVisitor {
pb_attr->set_max_int(attr->max_int);
for (auto& symbol : attr->symbols) {
- pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbols();
+ pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
SerializeItemCommonToPb(symbol.symbol, pb_symbol);
SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
pb_symbol->set_value(symbol.value);
@@ -114,12 +114,12 @@ class PbSerializerVisitor : public RawValueVisitor {
}
for (Style::Entry& entry : style->entries) {
- pb::Style_Entry* pb_entry = pb_style->add_entries();
+ pb::Style_Entry* pb_entry = pb_style->add_entry();
SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
pb::Item* pb_item = pb_entry->mutable_item();
SerializeItemCommonToPb(entry.key, pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item);
+ PbSerializerVisitor sub_visitor(source_pool_, pb_item);
entry.value->Accept(&sub_visitor);
}
}
@@ -127,7 +127,7 @@ class PbSerializerVisitor : public RawValueVisitor {
void Visit(Styleable* styleable) override {
pb::Styleable* pb_styleable = pb_compound_value()->mutable_styleable();
for (Reference& entry : styleable->entries) {
- pb::Styleable_Entry* pb_entry = pb_styleable->add_entries();
+ pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
SerializeItemCommonToPb(entry, pb_entry);
SerializeReferenceToPb(entry, pb_entry->mutable_attr());
}
@@ -135,11 +135,10 @@ class PbSerializerVisitor : public RawValueVisitor {
void Visit(Array* array) override {
pb::Array* pb_array = pb_compound_value()->mutable_array();
- for (auto& value : array->items) {
- pb::Array_Entry* pb_entry = pb_array->add_entries();
- SerializeItemCommonToPb(*value, pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_,
- pb_entry->mutable_item());
+ for (auto& value : array->elements) {
+ pb::Array_Element* pb_element = pb_array->add_element();
+ SerializeItemCommonToPb(*value, pb_element);
+ PbSerializerVisitor sub_visitor(source_pool_, pb_element->mutable_item());
value->Accept(&sub_visitor);
}
}
@@ -153,11 +152,11 @@ class PbSerializerVisitor : public RawValueVisitor {
continue;
}
- pb::Plural_Entry* pb_entry = pb_plural->add_entries();
+ pb::Plural_Entry* pb_entry = pb_plural->add_entry();
pb_entry->set_arity(SerializePluralEnumToPb(i));
pb::Item* pb_element = pb_entry->mutable_item();
SerializeItemCommonToPb(*plural->values[i], pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element);
+ PbSerializerVisitor sub_visitor(source_pool_, pb_element);
plural->values[i]->Accept(&sub_visitor);
}
}
@@ -179,8 +178,7 @@ class PbSerializerVisitor : public RawValueVisitor {
template <typename T>
void SerializeItemCommonToPb(const Item& item, T* pb_item) {
- SerializeSourceToPb(item.GetSource(), source_pool_,
- pb_item->mutable_source());
+ SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source());
if (!item.GetComment().empty()) {
pb_item->set_comment(item.GetComment());
}
@@ -192,8 +190,7 @@ class PbSerializerVisitor : public RawValueVisitor {
}
if (ref.name) {
- StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString());
- pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index()));
+ pb_ref->set_name(ref.name.value().ToString());
}
pb_ref->set_private_(ref.private_reference);
@@ -201,7 +198,6 @@ class PbSerializerVisitor : public RawValueVisitor {
}
StringPool* source_pool_;
- StringPool* symbol_pool_;
pb::Value* out_pb_value_;
pb::Item* out_pb_item_;
};
@@ -220,26 +216,24 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
});
auto pb_table = util::make_unique<pb::ResourceTable>();
- SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool());
-
- StringPool source_pool, symbol_pool;
+ StringPool source_pool;
for (auto& package : table->packages) {
- pb::Package* pb_package = pb_table->add_packages();
+ pb::Package* pb_package = pb_table->add_package();
if (package->id) {
pb_package->set_package_id(package->id.value());
}
pb_package->set_package_name(package->name);
for (auto& type : package->types) {
- pb::Type* pb_type = pb_package->add_types();
+ pb::Type* pb_type = pb_package->add_type();
if (type->id) {
pb_type->set_id(type->id.value());
}
pb_type->set_name(ToString(type->type).to_string());
for (auto& entry : type->entries) {
- pb::Entry* pb_entry = pb_type->add_entries();
+ pb::Entry* pb_entry = pb_type->add_entry();
if (entry->id) {
pb_entry->set_id(entry->id.value());
}
@@ -253,7 +247,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
pb_status->set_allow_new(entry->symbol_status.allow_new);
for (auto& config_value : entry->values) {
- pb::ConfigValue* pb_config_value = pb_entry->add_config_values();
+ pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
SerializeConfig(config_value->config, pb_config_value->mutable_config());
if (!config_value->product.empty()) {
pb_config_value->mutable_config()->set_product(config_value->product);
@@ -270,7 +264,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
pb_value->set_weak(true);
}
- PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value);
+ PbSerializerVisitor visitor(&source_pool, pb_value);
config_value->value->Accept(&visitor);
}
}
@@ -278,27 +272,25 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
}
SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
- SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool());
return pb_table;
}
-std::unique_ptr<pb::CompiledFile> SerializeCompiledFileToPb(
- const ResourceFile& file) {
- auto pb_file = util::make_unique<pb::CompiledFile>();
+std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file) {
+ auto pb_file = util::make_unique<pb::internal::CompiledFile>();
pb_file->set_resource_name(file.name.ToString());
pb_file->set_source_path(file.source.path);
SerializeConfig(file.config, pb_file->mutable_config());
for (const SourcedResourceName& exported : file.exported_symbols) {
- pb::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbols();
+ pb::internal::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbol();
pb_symbol->set_resource_name(exported.name.ToString());
- pb_symbol->set_line_no(exported.line);
+ pb_symbol->mutable_source()->set_line_number(exported.line);
}
return pb_file;
}
-CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out)
- : out_(out) {}
+CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
+}
void CompiledFileOutputStream::EnsureAlignedWrite() {
const int padding = out_.ByteCount() % 4;
@@ -313,8 +305,7 @@ void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
out_.WriteLittleEndian32(val);
}
-void CompiledFileOutputStream::WriteCompiledFile(
- const pb::CompiledFile* compiled_file) {
+void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile* compiled_file) {
EnsureAlignedWrite();
out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file->ByteSize()));
compiled_file->SerializeWithCachedSizes(&out_);
@@ -334,7 +325,9 @@ void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
out_.WriteRaw(data, len);
}
-bool CompiledFileOutputStream::HadError() { return out_.HadError(); }
+bool CompiledFileOutputStream::HadError() {
+ return out_.HadError();
+}
CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
: in_(static_cast<const uint8_t*>(data), size) {}
@@ -352,7 +345,7 @@ bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
return in_.ReadLittleEndian32(out_val);
}
-bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
+bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
EnsureAlignedRead();
google::protobuf::uint64 pb_size = 0u;
@@ -379,8 +372,7 @@ bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
return true;
}
-bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset,
- uint64_t* out_len) {
+bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
EnsureAlignedRead();
google::protobuf::uint64 pb_size = 0u;
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 3ebb08eb791e..80608b3d9c05 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -20,7 +20,9 @@
#include "test/Test.h"
using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
using ::testing::NotNull;
+using ::testing::SizeIs;
namespace aapt {
@@ -38,12 +40,12 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
Symbol public_symbol;
public_symbol.state = SymbolState::kPublic;
- ASSERT_TRUE(table->SetSymbolState(
- test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000),
- public_symbol, context->GetDiagnostics()));
+ ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
+ ResourceId(0x7f020000), public_symbol,
+ context->GetDiagnostics()));
Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
- ASSERT_NE(nullptr, id);
+ ASSERT_THAT(id, NotNull());
// Make a plural.
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
@@ -52,6 +54,15 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
ConfigDescription{}, {}, std::move(plural),
context->GetDiagnostics()));
+ // Make a styled string.
+ StyleString style_string;
+ style_string.str = "hello";
+ style_string.spans.push_back(Span{"b", 0u, 4u});
+ ASSERT_TRUE(
+ table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
+ util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
+ context->GetDiagnostics()));
+
// Make a resource with different products.
ASSERT_TRUE(table->AddResource(
test::ParseNameOrDie("com.app.a:integer/one"),
@@ -65,9 +76,8 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
context->GetDiagnostics()));
// Make a reference with both resource name and resource ID.
- // The reference should point to a resource outside of this table to test that
- // both
- // name and id get serialized.
+ // The reference should point to a resource outside of this table to test that both name and id
+ // get serialized.
Reference expected_ref;
expected_ref.name = test::ParseNameOrDie("android:layout/main");
expected_ref.id = ResourceId(0x01020000);
@@ -85,36 +95,45 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
ASSERT_THAT(new_id, NotNull());
- EXPECT_EQ(id->IsWeak(), new_id->IsWeak());
+ EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
Maybe<ResourceTable::SearchResult> result =
new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
+
+ EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state);
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
// Find the product-dependent values
BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
ASSERT_THAT(prim, NotNull());
- EXPECT_EQ(123u, prim->value.data);
+ EXPECT_THAT(prim->value.data, Eq(123u));
prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
ASSERT_THAT(prim, NotNull());
- EXPECT_EQ(321u, prim->value.data);
+ EXPECT_THAT(prim->value.data, Eq(321u));
Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
ASSERT_THAT(actual_ref, NotNull());
ASSERT_TRUE(actual_ref->name);
ASSERT_TRUE(actual_ref->id);
- EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value());
- EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value());
+ EXPECT_THAT(*actual_ref, Eq(expected_ref));
+
+ StyledString* actual_styled_str =
+ test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled");
+ ASSERT_THAT(actual_styled_str, NotNull());
+ EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
+ ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
+ EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
+ EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
+ EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
}
TEST(TableProtoSerializer, SerializeFileHeader) {
@@ -132,10 +151,10 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
std::string output_str;
{
- std::unique_ptr<pb::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
+ std::unique_ptr<pb::internal::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
f.name.entry = "__" + f.name.entry + "$0";
- std::unique_ptr<pb::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
+ std::unique_ptr<pb::internal::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
StringOutputStream out_stream(&output_str);
CompiledFileOutputStream out_file_stream(&out_stream);
@@ -154,7 +173,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
// Read the first compiled file.
- pb::CompiledFile new_pb_file;
+ pb::internal::CompiledFile new_pb_file;
ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(
@@ -191,7 +210,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
ResourceFile f;
- std::unique_ptr<pb::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
+ std::unique_ptr<pb::internal::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
const std::string expected_data = "1234";
@@ -213,7 +232,7 @@ TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
EXPECT_EQ(1u, num_files);
- pb::CompiledFile new_pb_file;
+ pb::internal::CompiledFile new_pb_file;
EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
uint64_t offset, len;
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index c8d36177beb4..8368f9d16af8 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,18 @@
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
+## Version 2.19
+- Added navigation resource type.
+- Fixed issue with resource deduplication. (bug 64397629)
+- Added a daemon mode for issuing commands. This is invoked with `aapt2 daemon`.
+ Command line arguments are separated by newlines, with an empty line signalling the
+ end of a command. Sending `EOF (Ctrl+D)` to the daemon will exit.
+- Fixed an issue where multiple permissions defined in AndroidManifest.xml would generate
+ conflicting definitions for the same Java constant in Manifest.java. Changed the implementation
+ to match that of `aapt`, which will take the last definition as the sole definition.
+ A warning is logged if such a scenario occurs. (bug 64472942)
+- Made improvements to handling of paths on Windows. This should resolve a lot of issues with
+ Unicode paths. (bug 62336414, 63830502)
+
## Version 2.18
### `aapt2 ...`
- Fixed issue where enum values were interpreted as integers and range checked. (bug 62358540)
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
new file mode 100644
index 000000000000..8f9788e8a25d
--- /dev/null
+++ b/tools/aapt2/test/Builders.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "test/Builders.h"
+
+#include "android-base/logging.h"
+#include "androidfw/StringPiece.h"
+
+#include "io/StringInputStream.h"
+#include "test/Common.h"
+#include "util/Util.h"
+
+using ::aapt::io::StringInputStream;
+using ::android::StringPiece;
+
+namespace aapt {
+namespace test {
+
+ResourceTableBuilder& ResourceTableBuilder::SetPackageId(const StringPiece& package_name,
+ uint8_t id) {
+ ResourceTablePackage* package = table_->CreatePackage(package_name, id);
+ CHECK(package != nullptr);
+ return *this;
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name,
+ const ResourceId& id) {
+ return AddValue(name, id, util::make_unique<Id>());
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name,
+ const ConfigDescription& config,
+ const ResourceId& id) {
+ return AddValue(name, config, id, util::make_unique<Id>());
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name,
+ const StringPiece& ref) {
+ return AddReference(name, {}, ref);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name,
+ const ResourceId& id,
+ const StringPiece& ref) {
+ return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name,
+ const StringPiece& str) {
+ return AddString(name, {}, str);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id,
+ const StringPiece& str) {
+ return AddValue(name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id,
+ const ConfigDescription& config,
+ const StringPiece& str) {
+ return AddValue(name, config, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+ const StringPiece& path) {
+ return AddFileReference(name, {}, path);
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+ const ResourceId& id,
+ const StringPiece& path) {
+ return AddValue(name, id, util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
+ const StringPiece& path,
+ const ConfigDescription& config) {
+ return AddValue(name, config, {},
+ util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name,
+ std::unique_ptr<Value> value) {
+ return AddValue(name, {}, std::move(value));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, const ResourceId& id,
+ std::unique_ptr<Value> value) {
+ return AddValue(name, {}, id, std::move(value));
+}
+
+ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name,
+ const ConfigDescription& config,
+ const ResourceId& id,
+ std::unique_ptr<Value> value) {
+ ResourceName res_name = ParseNameOrDie(name);
+ CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
+ GetDiagnostics()));
+ return *this;
+}
+
+ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& name,
+ const ResourceId& id, SymbolState state,
+ bool allow_new) {
+ ResourceName res_name = ParseNameOrDie(name);
+ Symbol symbol;
+ symbol.state = state;
+ symbol.allow_new = allow_new;
+ CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
+ return *this;
+}
+
+StringPool* ResourceTableBuilder::string_pool() {
+ return &table_->string_pool;
+}
+
+std::unique_ptr<ResourceTable> ResourceTableBuilder::Build() {
+ return std::move(table_);
+}
+
+std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) {
+ std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref));
+ reference->id = id;
+ return reference;
+}
+
+std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data) {
+ android::Res_value value = {};
+ value.size = sizeof(value);
+ value.dataType = type;
+ value.data = data;
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+AttributeBuilder::AttributeBuilder(bool weak) : attr_(util::make_unique<Attribute>(weak)) {
+ attr_->type_mask = android::ResTable_map::TYPE_ANY;
+}
+
+AttributeBuilder& AttributeBuilder::SetTypeMask(uint32_t typeMask) {
+ attr_->type_mask = typeMask;
+ return *this;
+}
+
+AttributeBuilder& AttributeBuilder::AddItem(const StringPiece& name, uint32_t value) {
+ attr_->symbols.push_back(
+ Attribute::Symbol{Reference(ResourceName({}, ResourceType::kId, name)), value});
+ return *this;
+}
+
+std::unique_ptr<Attribute> AttributeBuilder::Build() {
+ return std::move(attr_);
+}
+
+StyleBuilder& StyleBuilder::SetParent(const StringPiece& str) {
+ style_->parent = Reference(ParseNameOrDie(str));
+ return *this;
+}
+
+StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, std::unique_ptr<Item> value) {
+ style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
+ return *this;
+}
+
+StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, const ResourceId& id,
+ std::unique_ptr<Item> value) {
+ AddItem(str, std::move(value));
+ style_->entries.back().key.id = id;
+ return *this;
+}
+
+std::unique_ptr<Style> StyleBuilder::Build() {
+ return std::move(style_);
+}
+
+StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) {
+ styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
+ styleable_->entries.back().id = id;
+ return *this;
+}
+
+std::unique_ptr<Styleable> StyleableBuilder::Build() {
+ return std::move(styleable_);
+}
+
+std::unique_ptr<xml::XmlResource> BuildXmlDom(const StringPiece& str) {
+ std::string input = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+ input.append(str.data(), str.size());
+ StringInputStream in(input);
+ StdErrDiagnostics diag;
+ std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, Source("test.xml"));
+ CHECK(doc != nullptr && doc->root != nullptr) << "failed to parse inline XML string";
+ return doc;
+}
+
+std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
+ const StringPiece& str) {
+ std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
+ doc->file.name.package = context->GetCompilationPackage();
+ return doc;
+}
+
+} // namespace test
+} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 6b8207647471..d9f3912fb4c6 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -19,13 +19,13 @@
#include <memory>
-#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "test/Common.h"
-#include "util/Util.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
#include "xml/XmlDom.h"
namespace aapt {
@@ -35,97 +35,37 @@ class ResourceTableBuilder {
public:
ResourceTableBuilder() = default;
- StringPool* string_pool() { return &table_->string_pool; }
-
- ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id) {
- ResourceTablePackage* package = table_->CreatePackage(package_name, id);
- CHECK(package != nullptr);
- return *this;
- }
-
- ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}) {
- return AddValue(name, id, util::make_unique<Id>());
- }
-
+ ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id);
+ ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {});
ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config,
- const ResourceId& id = {}) {
- return AddValue(name, config, id, util::make_unique<Id>());
- }
-
+ const ResourceId& id = {});
ResourceTableBuilder& AddReference(const android::StringPiece& name,
- const android::StringPiece& ref) {
- return AddReference(name, {}, ref);
- }
-
+ const android::StringPiece& ref);
ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id,
- const android::StringPiece& ref) {
- return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
- }
-
+ const android::StringPiece& ref);
ResourceTableBuilder& AddString(const android::StringPiece& name,
- const android::StringPiece& str) {
- return AddString(name, {}, str);
- }
-
+ const android::StringPiece& str);
ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
- const android::StringPiece& str) {
- return AddValue(
- name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
- }
-
+ const android::StringPiece& str);
ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
- const ConfigDescription& config,
- const android::StringPiece& str) {
- return AddValue(name, config, id, util::make_unique<String>(
- table_->string_pool.MakeRef(str)));
- }
-
+ const ConfigDescription& config, const android::StringPiece& str);
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
- const android::StringPiece& path) {
- return AddFileReference(name, {}, path);
- }
-
+ const android::StringPiece& path);
ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id,
- const android::StringPiece& path) {
- return AddValue(name, id, util::make_unique<FileReference>(
- table_->string_pool.MakeRef(path)));
- }
-
+ const android::StringPiece& path);
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
const android::StringPiece& path,
- const ConfigDescription& config) {
- return AddValue(name, config, {}, util::make_unique<FileReference>(
- table_->string_pool.MakeRef(path)));
- }
-
- ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value) {
- return AddValue(name, {}, std::move(value));
- }
-
+ const ConfigDescription& config);
+ ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value);
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id,
- std::unique_ptr<Value> value) {
- return AddValue(name, {}, id, std::move(value));
- }
-
+ std::unique_ptr<Value> value);
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
- const ResourceId& id, std::unique_ptr<Value> value) {
- ResourceName res_name = ParseNameOrDie(name);
- CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
- GetDiagnostics()));
- return *this;
- }
-
+ const ResourceId& id, std::unique_ptr<Value> value);
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
- SymbolState state, bool allow_new = false) {
- ResourceName res_name = ParseNameOrDie(name);
- Symbol symbol;
- symbol.state = state;
- symbol.allow_new = allow_new;
- CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
- return *this;
- }
+ SymbolState state, bool allow_new = false);
- std::unique_ptr<ResourceTable> Build() { return std::move(table_); }
+ StringPool* string_pool();
+ std::unique_ptr<ResourceTable> Build();
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
@@ -133,29 +73,16 @@ class ResourceTableBuilder {
std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
};
-inline std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
- const Maybe<ResourceId>& id = {}) {
- std::unique_ptr<Reference> reference =
- util::make_unique<Reference>(ParseNameOrDie(ref));
- reference->id = id;
- return reference;
-}
-
-inline std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type,
- uint32_t data) {
- android::Res_value value = {};
- value.size = sizeof(value);
- value.dataType = type;
- value.data = data;
- return util::make_unique<BinaryPrimitive>(value);
-}
+std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
+ const Maybe<ResourceId>& id = {});
+std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
template <typename T>
class ValueBuilder {
public:
template <typename... Args>
- explicit ValueBuilder(Args&&... args)
- : value_(new T{std::forward<Args>(args)...}) {}
+ explicit ValueBuilder(Args&&... args) : value_(new T{std::forward<Args>(args)...}) {
+ }
template <typename... Args>
ValueBuilder& SetSource(Args&&... args) {
@@ -168,7 +95,9 @@ class ValueBuilder {
return *this;
}
- std::unique_ptr<Value> Build() { return std::move(value_); }
+ std::unique_ptr<Value> Build() {
+ return std::move(value_);
+ }
private:
DISALLOW_COPY_AND_ASSIGN(ValueBuilder);
@@ -178,23 +107,10 @@ class ValueBuilder {
class AttributeBuilder {
public:
- explicit AttributeBuilder(bool weak = false)
- : attr_(util::make_unique<Attribute>(weak)) {
- attr_->type_mask = android::ResTable_map::TYPE_ANY;
- }
-
- AttributeBuilder& SetTypeMask(uint32_t typeMask) {
- attr_->type_mask = typeMask;
- return *this;
- }
-
- AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value) {
- attr_->symbols.push_back(Attribute::Symbol{
- Reference(ResourceName({}, ResourceType::kId, name)), value});
- return *this;
- }
-
- std::unique_ptr<Attribute> Build() { return std::move(attr_); }
+ explicit AttributeBuilder(bool weak = false);
+ AttributeBuilder& SetTypeMask(uint32_t typeMask);
+ AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value);
+ std::unique_ptr<Attribute> Build();
private:
DISALLOW_COPY_AND_ASSIGN(AttributeBuilder);
@@ -205,27 +121,11 @@ class AttributeBuilder {
class StyleBuilder {
public:
StyleBuilder() = default;
-
- StyleBuilder& SetParent(const android::StringPiece& str) {
- style_->parent = Reference(ParseNameOrDie(str));
- return *this;
- }
-
- StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value) {
- style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
- return *this;
- }
-
+ StyleBuilder& SetParent(const android::StringPiece& str);
+ StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value);
StyleBuilder& AddItem(const android::StringPiece& str, const ResourceId& id,
- std::unique_ptr<Item> value) {
- AddItem(str, std::move(value));
- style_->entries.back().key.id = id;
- return *this;
- }
-
- std::unique_ptr<Style> Build() {
- return std::move(style_);
- }
+ std::unique_ptr<Item> value);
+ std::unique_ptr<Style> Build();
private:
DISALLOW_COPY_AND_ASSIGN(StyleBuilder);
@@ -236,14 +136,8 @@ class StyleBuilder {
class StyleableBuilder {
public:
StyleableBuilder() = default;
-
- StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}) {
- styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
- styleable_->entries.back().id = id;
- return *this;
- }
-
- std::unique_ptr<Styleable> Build() { return std::move(styleable_); }
+ StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
+ std::unique_ptr<Styleable> Build();
private:
DISALLOW_COPY_AND_ASSIGN(StyleableBuilder);
@@ -251,22 +145,9 @@ class StyleableBuilder {
std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>();
};
-inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str) {
- std::stringstream in;
- in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
- StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc =
- xml::Inflate(&in, &diag, Source("test.xml"));
- CHECK(doc != nullptr) << "failed to parse inline XML string";
- return doc;
-}
-
-inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(
- IAaptContext* context, const android::StringPiece& str) {
- std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
- doc->file.name.package = context->GetCompilationPackage();
- return doc;
-}
+std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str);
+std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
+ const android::StringPiece& str);
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index d7b46caf8c94..e6b38c0007b4 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -142,10 +142,97 @@ MATCHER_P(StrEq, a,
return android::StringPiece16(arg) == a;
}
-MATCHER_P(ValueEq, a,
- std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
- return arg.Equals(&a);
-}
+class ValueEq {
+ public:
+ template <typename arg_type>
+ class BaseImpl : public ::testing::MatcherInterface<arg_type> {
+ BaseImpl(const BaseImpl&) = default;
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "is equal to " << *expected_;
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "is not equal to " << *expected_;
+ }
+
+ protected:
+ BaseImpl(const Value* expected) : expected_(expected) {
+ }
+
+ const Value* expected_;
+ };
+
+ template <typename T, bool>
+ class Impl {};
+
+ template <typename T>
+ class Impl<T, false> : public ::testing::MatcherInterface<T> {
+ public:
+ explicit Impl(const Value* expected) : expected_(expected) {
+ }
+
+ bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
+ return expected_->Equals(&x);
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "is equal to " << *expected_;
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "is not equal to " << *expected_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Impl);
+
+ const Value* expected_;
+ };
+
+ template <typename T>
+ class Impl<T, true> : public ::testing::MatcherInterface<T> {
+ public:
+ explicit Impl(const Value* expected) : expected_(expected) {
+ }
+
+ bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
+ return expected_->Equals(x);
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "is equal to " << *expected_;
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "is not equal to " << *expected_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Impl);
+
+ const Value* expected_;
+ };
+
+ ValueEq(const Value& expected) : expected_(&expected) {
+ }
+ ValueEq(const Value* expected) : expected_(expected) {
+ }
+ ValueEq(const ValueEq&) = default;
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::Matcher<T>(new Impl<T, std::is_pointer<T>::value>(expected_));
+ }
+
+ private:
+ const Value* expected_;
+};
+
+// MATCHER_P(ValueEq, a,
+// std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
+// return arg.Equals(&a);
+//}
MATCHER_P(StrValueEq, a,
std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 728d1f4207c4..892aee6fadcb 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -544,7 +544,7 @@ std::unique_ptr<Array> BinaryResourceParser::ParseArray(
const ResTable_map_entry* map) {
std::unique_ptr<Array> array = util::make_unique<Array>();
for (const ResTable_map& map_entry : map) {
- array->items.push_back(ParseValue(name, config, map_entry.value));
+ array->elements.push_back(ParseValue(name, config, map_entry.value));
}
return array;
}
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 1bf25947ea93..bf8dc4d727f7 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -27,6 +27,8 @@
#include "android-base/errors.h"
#include "android-base/file.h"
#include "android-base/logging.h"
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
#include "util/Util.h"
@@ -35,14 +37,32 @@
#include <direct.h>
#endif
-using android::StringPiece;
+using ::android::FileMap;
+using ::android::StringPiece;
+using ::android::base::ReadFileToString;
+using ::android::base::SystemErrorCodeToString;
+using ::android::base::unique_fd;
namespace aapt {
namespace file {
-FileType GetFileType(const StringPiece& path) {
+FileType GetFileType(const std::string& path) {
+// TODO(adamlesinski): I'd like to move this to ::android::base::utf8 but Windows does some macro
+// trickery with 'stat' and things don't override very well.
+#ifdef _WIN32
+ std::wstring path_utf16;
+ if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
+ return FileType::kNonexistant;
+ }
+
+ struct _stat64 sb;
+ int result = _wstat64(path_utf16.c_str(), &sb);
+#else
struct stat sb;
- if (stat(path.data(), &sb) < 0) {
+ int result = stat(path.c_str(), &sb);
+#endif
+
+ if (result == -1) {
if (errno == ENOENT || errno == ENOTDIR) {
return FileType::kNonexistant;
}
@@ -72,27 +92,20 @@ FileType GetFileType(const StringPiece& path) {
}
}
-inline static int MkdirImpl(const StringPiece& path) {
-#ifdef _WIN32
- return _mkdir(path.to_string().c_str());
-#else
- return mkdir(path.to_string().c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
-#endif
-}
-
-bool mkdirs(const StringPiece& path) {
- const char* start = path.begin();
- const char* end = path.end();
- for (const char* current = start; current != end; ++current) {
- if (*current == sDirSep && current != start) {
- StringPiece parent_path(start, current - start);
- int result = MkdirImpl(parent_path);
- if (result < 0 && errno != EEXIST) {
- return false;
- }
+bool mkdirs(const std::string& path) {
+ constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
+ // Start after the first character so that we don't consume the root '/'.
+ // This is safe to do with unicode because '/' will never match with a continuation character.
+ size_t current_pos = 1u;
+ while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) {
+ std::string parent_path = path.substr(0, current_pos);
+ int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode);
+ if (result < 0 && errno != EEXIST) {
+ return false;
}
+ current_pos += 1;
}
- return MkdirImpl(path) == 0 || errno == EEXIST;
+ return ::android::base::utf8::mkdir(path.c_str(), mode) == 0 || errno == EEXIST;
}
StringPiece GetStem(const StringPiece& path) {
@@ -129,10 +142,8 @@ StringPiece GetExtension(const StringPiece& path) {
void AppendPath(std::string* base, StringPiece part) {
CHECK(base != nullptr);
- const bool base_has_trailing_sep =
- (!base->empty() && *(base->end() - 1) == sDirSep);
- const bool part_has_leading_sep =
- (!part.empty() && *(part.begin()) == sDirSep);
+ const bool base_has_trailing_sep = (!base->empty() && *(base->end() - 1) == sDirSep);
+ const bool part_has_leading_sep = (!part.empty() && *(part.begin()) == sDirSep);
if (base_has_trailing_sep && part_has_leading_sep) {
// Remove the part's leading sep
part = part.substr(1, part.size() - 1);
@@ -151,31 +162,34 @@ std::string PackageToPath(const StringPiece& package) {
return out_path;
}
-Maybe<android::FileMap> MmapPath(const StringPiece& path,
- std::string* out_error) {
- std::unique_ptr<FILE, decltype(fclose)*> f = {fopen(path.data(), "rb"),
- fclose};
- if (!f) {
- if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) {
+ int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
+ unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
+ if (fd == -1) {
+ if (out_error) {
+ *out_error = SystemErrorCodeToString(errno);
+ }
return {};
}
- int fd = fileno(f.get());
-
struct stat filestats = {};
if (fstat(fd, &filestats) != 0) {
- if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+ if (out_error) {
+ *out_error = SystemErrorCodeToString(errno);
+ }
return {};
}
- android::FileMap filemap;
+ FileMap filemap;
if (filestats.st_size == 0) {
// mmap doesn't like a length of 0. Instead we return an empty FileMap.
return std::move(filemap);
}
- if (!filemap.create(path.data(), fd, 0, filestats.st_size, true)) {
- if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+ if (!filemap.create(path.c_str(), fd, 0, filestats.st_size, true)) {
+ if (out_error) {
+ *out_error = SystemErrorCodeToString(errno);
+ }
return {};
}
return std::move(filemap);
@@ -184,7 +198,7 @@ Maybe<android::FileMap> MmapPath(const StringPiece& path,
bool AppendArgsFromFile(const StringPiece& path, std::vector<std::string>* out_arglist,
std::string* out_error) {
std::string contents;
- if (!android::base::ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
+ if (!ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
if (out_error) {
*out_error = "failed to read argument-list file";
}
@@ -270,7 +284,7 @@ Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDia
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
- diag->Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+ diag->Error(DiagMessage() << SystemErrorCodeToString(errno));
return {};
}
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index b3b1e484d27b..b26e4fa26de6 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -34,8 +34,10 @@ namespace file {
#ifdef _WIN32
constexpr const char sDirSep = '\\';
+constexpr const char sPathSep = ';';
#else
constexpr const char sDirSep = '/';
+constexpr const char sPathSep = ':';
#endif
enum class FileType {
@@ -50,79 +52,56 @@ enum class FileType {
kSocket,
};
-FileType GetFileType(const android::StringPiece& path);
+FileType GetFileType(const std::string& path);
-/*
- * Appends a path to `base`, separated by the directory separator.
- */
+// Appends a path to `base`, separated by the directory separator.
void AppendPath(std::string* base, android::StringPiece part);
-/*
- * Makes all the directories in `path`. The last element in the path
- * is interpreted as a directory.
- */
-bool mkdirs(const android::StringPiece& path);
+// Makes all the directories in `path`. The last element in the path is interpreted as a directory.
+bool mkdirs(const std::string& path);
-/**
- * Returns all but the last part of the path.
- */
+// Returns all but the last part of the path.
android::StringPiece GetStem(const android::StringPiece& path);
-/**
- * Returns the last part of the path with extension.
- */
+// Returns the last part of the path with extension.
android::StringPiece GetFilename(const android::StringPiece& path);
-/**
- * Returns the extension of the path. This is the entire string after
- * the first '.' of the last part of the path.
- */
+// Returns the extension of the path. This is the entire string after the first '.' of the last part
+// of the path.
android::StringPiece GetExtension(const android::StringPiece& path);
-/**
- * Converts a package name (com.android.app) to a path: com/android/app
- */
+// Converts a package name (com.android.app) to a path: com/android/app
std::string PackageToPath(const android::StringPiece& package);
-/**
- * Creates a FileMap for the file at path.
- */
-Maybe<android::FileMap> MmapPath(const android::StringPiece& path, std::string* out_error);
+// Creates a FileMap for the file at path.
+Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
-/**
- * Reads the file at path and appends each line to the outArgList vector.
- */
+// Reads the file at path and appends each line to the outArgList vector.
bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
std::string* out_error);
-/*
- * Filter that determines which resource files/directories are
- * processed by AAPT. Takes a pattern string supplied by the user.
- * Pattern format is specified in the FileFilter::SetPattern() method.
- */
+// Filter that determines which resource files/directories are
+// processed by AAPT. Takes a pattern string supplied by the user.
+// Pattern format is specified in the FileFilter::SetPattern() method.
class FileFilter {
public:
explicit FileFilter(IDiagnostics* diag) : diag_(diag) {}
- /*
- * Patterns syntax:
- * - Delimiter is :
- * - Entry can start with the flag ! to avoid printing a warning
- * about the file being ignored.
- * - Entry can have the flag "<dir>" to match only directories
- * or <file> to match only files. Default is to match both.
- * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
- * where prefix/suffix must have at least 1 character (so that
- * we don't match a '*' catch-all pattern.)
- * - The special filenames "." and ".." are always ignored.
- * - Otherwise the full string is matched.
- * - match is not case-sensitive.
- */
+ // Patterns syntax:
+ // - Delimiter is :
+ // - Entry can start with the flag ! to avoid printing a warning
+ // about the file being ignored.
+ // - Entry can have the flag "<dir>" to match only directories
+ // or <file> to match only files. Default is to match both.
+ // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+ // where prefix/suffix must have at least 1 character (so that
+ // we don't match a '*' catch-all pattern.)
+ // - The special filenames "." and ".." are always ignored.
+ // - Otherwise the full string is matched.
+ // - match is not case-sensitive.
bool SetPattern(const android::StringPiece& pattern);
- /**
- * Applies the filter, returning true for pass, false for fail.
- */
+ // Applies the filter, returning true for pass, false for fail.
bool operator()(const std::string& filename, FileType type) const;
private:
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index ad3989ea430f..8f021ab8cb8a 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -228,6 +228,12 @@ class Tokenizer {
public:
class iterator {
public:
+ using reference = android::StringPiece&;
+ using value_type = android::StringPiece;
+ using difference_type = size_t;
+ using pointer = android::StringPiece*;
+ using iterator_category = std::forward_iterator_tag;
+
iterator(const iterator&) = default;
iterator& operator=(const iterator&) = default;
@@ -250,9 +256,13 @@ class Tokenizer {
Tokenizer(android::StringPiece str, char sep);
- iterator begin() { return begin_; }
+ iterator begin() const {
+ return begin_;
+ }
- iterator end() { return end_; }
+ iterator end() const {
+ return end_;
+ }
private:
const iterator begin_;
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
index 352a93361633..cc664a5de722 100644
--- a/tools/aapt2/xml/XmlActionExecutor.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -78,7 +78,7 @@ bool XmlActionExecutor::Execute(XmlActionExecutorPolicy policy, IDiagnostics* di
XmlResource* doc) const {
SourcePathDiagnostics source_diag(doc->file.source, diag);
- Element* el = FindRootElement(doc);
+ Element* el = doc->root.get();
if (!el) {
if (policy == XmlActionExecutorPolicy::kWhitelist) {
source_diag.Error(DiagMessage() << "no root XML tag found");
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 885ab3e33fed..cbb652ed9a1a 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -29,8 +29,9 @@
#include "XmlPullParser.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::aapt::io::InputStream;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
namespace xml {
@@ -38,17 +39,15 @@ namespace xml {
constexpr char kXmlNamespaceSep = 1;
struct Stack {
- std::unique_ptr<xml::Node> root;
- std::stack<xml::Node*> node_stack;
+ std::unique_ptr<xml::Element> root;
+ std::stack<xml::Element*> node_stack;
+ std::unique_ptr<xml::Element> pending_element;
std::string pending_comment;
std::unique_ptr<xml::Text> last_text_node;
};
-/**
- * Extracts the namespace and name of an expanded element or attribute name.
- */
-static void SplitName(const char* name, std::string* out_ns,
- std::string* out_name) {
+// Extracts the namespace and name of an expanded element or attribute name.
+static void SplitName(const char* name, std::string* out_ns, std::string* out_name) {
const char* p = name;
while (*p != 0 && *p != kXmlNamespaceSep) {
p++;
@@ -66,6 +65,7 @@ static void SplitName(const char* name, std::string* out_ns,
static void FinishPendingText(Stack* stack) {
if (stack->last_text_node != nullptr) {
if (!stack->last_text_node->text.empty()) {
+ CHECK(!stack->node_stack.empty());
stack->node_stack.top()->AppendChild(std::move(stack->last_text_node));
} else {
// Drop an empty text node.
@@ -74,48 +74,27 @@ static void FinishPendingText(Stack* stack) {
}
}
-static void AddToStack(Stack* stack, XML_Parser parser,
- std::unique_ptr<Node> node) {
- node->line_number = XML_GetCurrentLineNumber(parser);
- node->column_number = XML_GetCurrentColumnNumber(parser);
-
- Node* this_node = node.get();
- if (!stack->node_stack.empty()) {
- stack->node_stack.top()->AppendChild(std::move(node));
- } else {
- stack->root = std::move(node);
- }
-
- if (!NodeCast<Text>(this_node)) {
- stack->node_stack.push(this_node);
- }
-}
-
-static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
- const char* uri) {
+static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix, const char* uri) {
XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
FinishPendingText(stack);
- std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
- if (prefix) {
- ns->namespace_prefix = prefix;
- }
+ NamespaceDecl decl;
+ decl.line_number = XML_GetCurrentLineNumber(parser);
+ decl.column_number = XML_GetCurrentColumnNumber(parser);
+ decl.prefix = prefix ? prefix : "";
+ decl.uri = uri ? uri : "";
- if (uri) {
- ns->namespace_uri = uri;
+ if (stack->pending_element == nullptr) {
+ stack->pending_element = util::make_unique<Element>();
}
-
- AddToStack(stack, parser, std::move(ns));
+ stack->pending_element->namespace_decls.push_back(std::move(decl));
}
-static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix) {
+static void XMLCALL EndNamespaceHandler(void* user_data, const char* /*prefix*/) {
XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
FinishPendingText(stack);
-
- CHECK(!stack->node_stack.empty());
- stack->node_stack.pop();
}
static bool less_attribute(const Attribute& lhs, const Attribute& rhs) {
@@ -123,28 +102,42 @@ static bool less_attribute(const Attribute& lhs, const Attribute& rhs) {
std::tie(rhs.namespace_uri, rhs.name, rhs.value);
}
-static void XMLCALL StartElementHandler(void* user_data, const char* name,
- const char** attrs) {
+static void XMLCALL StartElementHandler(void* user_data, const char* name, const char** attrs) {
XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
FinishPendingText(stack);
- std::unique_ptr<Element> el = util::make_unique<Element>();
+ std::unique_ptr<Element> el;
+ if (stack->pending_element != nullptr) {
+ el = std::move(stack->pending_element);
+ } else {
+ el = util::make_unique<Element>();
+ }
+
+ el->line_number = XML_GetCurrentLineNumber(parser);
+ el->column_number = XML_GetCurrentColumnNumber(parser);
+ el->comment = std::move(stack->pending_comment);
+
SplitName(name, &el->namespace_uri, &el->name);
while (*attrs) {
Attribute attribute;
SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
attribute.value = *attrs++;
-
- // Insert in sorted order.
- auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
- less_attribute);
- el->attributes.insert(iter, std::move(attribute));
+ el->attributes.push_back(std::move(attribute));
}
- el->comment = std::move(stack->pending_comment);
- AddToStack(stack, parser, std::move(el));
+ // Sort the attributes.
+ std::sort(el->attributes.begin(), el->attributes.end(), less_attribute);
+
+ // Add to the stack.
+ Element* this_el = el.get();
+ if (!stack->node_stack.empty()) {
+ stack->node_stack.top()->AppendChild(std::move(el));
+ } else {
+ stack->root = std::move(el);
+ }
+ stack->node_stack.push(this_el);
}
static void XMLCALL EndElementHandler(void* user_data, const char* name) {
@@ -189,40 +182,41 @@ static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {
stack->pending_comment += comment;
}
-std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
+std::unique_ptr<XmlResource> Inflate(InputStream* in, IDiagnostics* diag, const Source& source) {
Stack stack;
- XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
- XML_SetUserData(parser, &stack);
- XML_UseParserAsHandlerArg(parser);
- XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
- XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler, EndNamespaceHandler);
- XML_SetCharacterDataHandler(parser, CharacterDataHandler);
- XML_SetCommentHandler(parser, CommentDataHandler);
-
- char buffer[1024];
- while (!in->eof()) {
- in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
- if (in->bad() && !in->eof()) {
- stack.root = {};
- diag->Error(DiagMessage(source) << strerror(errno));
- break;
- }
-
- if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
- stack.root = {};
- diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser)))
- << XML_ErrorString(XML_GetErrorCode(parser)));
- break;
+ std::unique_ptr<std::remove_pointer<XML_Parser>::type, decltype(XML_ParserFree)*> parser = {
+ XML_ParserCreateNS(nullptr, kXmlNamespaceSep), XML_ParserFree};
+ XML_SetUserData(parser.get(), &stack);
+ XML_UseParserAsHandlerArg(parser.get());
+ XML_SetElementHandler(parser.get(), StartElementHandler, EndElementHandler);
+ XML_SetNamespaceDeclHandler(parser.get(), StartNamespaceHandler, EndNamespaceHandler);
+ XML_SetCharacterDataHandler(parser.get(), CharacterDataHandler);
+ XML_SetCommentHandler(parser.get(), CommentDataHandler);
+
+ const char* buffer = nullptr;
+ size_t buffer_size = 0;
+ while (in->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) {
+ if (XML_Parse(parser.get(), buffer, buffer_size, false) == XML_STATUS_ERROR) {
+ diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get())))
+ << XML_ErrorString(XML_GetErrorCode(parser.get())));
+ return {};
}
}
- XML_ParserFree(parser);
- if (stack.root) {
- return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
- std::move(stack.root));
+ if (in->HadError()) {
+ diag->Error(DiagMessage(source) << in->GetError());
+ return {};
+ } else {
+ // Finish off the parsing.
+ if (XML_Parse(parser.get(), nullptr, 0u, true) == XML_STATUS_ERROR) {
+ diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get())))
+ << XML_ErrorString(XML_GetErrorCode(parser.get())));
+ return {};
+ }
}
- return {};
+ return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
+ std::move(stack.root));
}
static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) {
@@ -261,13 +255,13 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo
std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag,
const Source& source) {
// We import the android namespace because on Windows NO_ERROR is a macro, not
- // an enum, which
- // causes errors when qualifying it with android::
+ // an enum, which causes errors when qualifying it with android::
using namespace android;
StringPool string_pool;
- std::unique_ptr<Node> root;
- std::stack<Node*> node_stack;
+ std::unique_ptr<Element> root;
+ std::stack<Element*> node_stack;
+ std::unique_ptr<Element> pending_element;
ResXMLTree tree;
if (tree.setTo(data, data_len) != NO_ERROR) {
@@ -275,57 +269,76 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
}
ResXMLParser::event_code_t code;
- while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
- code != ResXMLParser::END_DOCUMENT) {
+ while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT && code != ResXMLParser::END_DOCUMENT) {
std::unique_ptr<Node> new_node;
switch (code) {
case ResXMLParser::START_NAMESPACE: {
- std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
+ NamespaceDecl decl;
size_t len;
const char16_t* str16 = tree.getNamespacePrefix(&len);
if (str16) {
- node->namespace_prefix = util::Utf16ToUtf8(StringPiece16(str16, len));
+ decl.prefix = util::Utf16ToUtf8(StringPiece16(str16, len));
}
str16 = tree.getNamespaceUri(&len);
if (str16) {
- node->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
+ decl.uri = util::Utf16ToUtf8(StringPiece16(str16, len));
+ }
+
+ if (pending_element == nullptr) {
+ pending_element = util::make_unique<Element>();
}
- new_node = std::move(node);
break;
}
case ResXMLParser::START_TAG: {
- std::unique_ptr<Element> node = util::make_unique<Element>();
+ std::unique_ptr<Element> el;
+ if (pending_element != nullptr) {
+ el = std::move(pending_element);
+ } else {
+ el = util::make_unique<Element>();
+ ;
+ }
+
size_t len;
const char16_t* str16 = tree.getElementNamespace(&len);
if (str16) {
- node->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
+ el->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
}
str16 = tree.getElementName(&len);
if (str16) {
- node->name = util::Utf16ToUtf8(StringPiece16(str16, len));
+ el->name = util::Utf16ToUtf8(StringPiece16(str16, len));
}
- CopyAttributes(node.get(), &tree, &string_pool);
+ Element* this_el = el.get();
+ CopyAttributes(el.get(), &tree, &string_pool);
- new_node = std::move(node);
+ if (!node_stack.empty()) {
+ node_stack.top()->AppendChild(std::move(el));
+ } else {
+ root = std::move(el);
+ }
+ node_stack.push(this_el);
break;
}
case ResXMLParser::TEXT: {
- std::unique_ptr<Text> node = util::make_unique<Text>();
+ std::unique_ptr<Text> text = util::make_unique<Text>();
+ text->line_number = tree.getLineNumber();
size_t len;
const char16_t* str16 = tree.getText(&len);
if (str16) {
- node->text = util::Utf16ToUtf8(StringPiece16(str16, len));
+ text->text = util::Utf16ToUtf8(StringPiece16(str16, len));
}
- new_node = std::move(node);
+ CHECK(!node_stack.empty());
+ node_stack.top()->AppendChild(std::move(text));
break;
}
case ResXMLParser::END_NAMESPACE:
+ break;
+
case ResXMLParser::END_TAG:
CHECK(!node_stack.empty());
node_stack.pop();
@@ -335,74 +348,32 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
LOG(FATAL) << "unhandled XML chunk type";
break;
}
-
- if (new_node) {
- new_node->line_number = tree.getLineNumber();
-
- Node* this_node = new_node.get();
- if (!root) {
- CHECK(node_stack.empty()) << "node stack should be empty";
- root = std::move(new_node);
- } else {
- CHECK(!node_stack.empty()) << "node stack should not be empty";
- node_stack.top()->AppendChild(std::move(new_node));
- }
-
- if (!NodeCast<Text>(this_node)) {
- node_stack.push(this_node);
- }
- }
}
return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
}
-std::unique_ptr<Node> Namespace::Clone(const ElementCloneFunc& el_cloner) {
- auto ns = util::make_unique<Namespace>();
- ns->comment = comment;
- ns->line_number = line_number;
- ns->column_number = column_number;
- ns->namespace_prefix = namespace_prefix;
- ns->namespace_uri = namespace_uri;
- ns->children.reserve(children.size());
- for (const std::unique_ptr<xml::Node>& child : children) {
- ns->AppendChild(child->Clone(el_cloner));
- }
- return std::move(ns);
-}
-
-Element* FindRootElement(XmlResource* doc) {
- return FindRootElement(doc->root.get());
-}
-
Element* FindRootElement(Node* node) {
- if (!node) {
+ if (node == nullptr) {
return nullptr;
}
- Element* el = nullptr;
- while ((el = NodeCast<Element>(node)) == nullptr) {
- if (node->children.empty()) {
- return nullptr;
- }
- // We are looking for the first element, and namespaces can only have one
- // child.
- node = node->children.front().get();
+ while (node->parent != nullptr) {
+ node = node->parent;
}
- return el;
+ return NodeCast<Element>(node);
}
-void Node::AppendChild(std::unique_ptr<Node> child) {
+void Element::AppendChild(std::unique_ptr<Node> child) {
child->parent = this;
children.push_back(std::move(child));
}
-void Node::InsertChild(size_t index, std::unique_ptr<Node> child) {
+void Element::InsertChild(size_t index, std::unique_ptr<Node> child) {
child->parent = this;
children.insert(children.begin() + index, std::move(child));
}
-Attribute* Element::FindAttribute(const StringPiece& ns,
- const StringPiece& name) {
+Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) {
for (auto& attr : attributes) {
if (ns == attr.namespace_uri && name == attr.name) {
return &attr;
@@ -424,21 +395,11 @@ Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
return FindChildWithAttribute(ns, name, {}, {}, {});
}
-Element* Element::FindChildWithAttribute(const StringPiece& ns,
- const StringPiece& name,
- const StringPiece& attr_ns,
- const StringPiece& attr_name,
+Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name,
+ const StringPiece& attr_ns, const StringPiece& attr_name,
const StringPiece& attr_value) {
- for (auto& child_node : children) {
- Node* child = child_node.get();
- while (NodeCast<Namespace>(child)) {
- if (child->children.empty()) {
- break;
- }
- child = child->children[0].get();
- }
-
- if (Element* el = NodeCast<Element>(child)) {
+ for (auto& child : children) {
+ if (Element* el = NodeCast<Element>(child.get())) {
if (ns == el->namespace_uri && name == el->name) {
if (attr_ns.empty() && attr_name.empty()) {
return el;
@@ -457,23 +418,16 @@ Element* Element::FindChildWithAttribute(const StringPiece& ns,
std::vector<Element*> Element::GetChildElements() {
std::vector<Element*> elements;
for (auto& child_node : children) {
- Node* child = child_node.get();
- while (NodeCast<Namespace>(child)) {
- if (child->children.empty()) {
- break;
- }
- child = child->children[0].get();
- }
-
- if (Element* el = NodeCast<Element>(child)) {
- elements.push_back(el);
+ if (Element* child = NodeCast<Element>(child_node.get())) {
+ elements.push_back(child);
}
}
return elements;
}
-std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) {
+std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) const {
auto el = util::make_unique<Element>();
+ el->namespace_decls = namespace_decls;
el->comment = comment;
el->line_number = line_number;
el->column_number = column_number;
@@ -488,7 +442,17 @@ std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) {
return std::move(el);
}
-std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) {
+std::unique_ptr<Element> Element::CloneElement(const ElementCloneFunc& el_cloner) const {
+ return std::unique_ptr<Element>(static_cast<Element*>(Clone(el_cloner).release()));
+}
+
+void Element::Accept(Visitor* visitor) {
+ visitor->BeforeVisitElement(this);
+ visitor->Visit(this);
+ visitor->AfterVisitElement(this);
+}
+
+std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) const {
auto t = util::make_unique<Text>();
t->comment = comment;
t->line_number = line_number;
@@ -497,21 +461,22 @@ std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) {
return std::move(t);
}
-void PackageAwareVisitor::Visit(Namespace* ns) {
- bool added = false;
- if (Maybe<ExtractedPackage> maybe_package =
- ExtractPackageFromNamespace(ns->namespace_uri)) {
- ExtractedPackage& package = maybe_package.value();
- package_decls_.push_back(
- PackageDecl{ns->namespace_prefix, std::move(package)});
- added = true;
- }
-
- Visitor::Visit(ns);
+void Text::Accept(Visitor* visitor) {
+ visitor->Visit(this);
+}
- if (added) {
- package_decls_.pop_back();
+void PackageAwareVisitor::BeforeVisitElement(Element* el) {
+ std::vector<PackageDecl> decls;
+ for (const NamespaceDecl& decl : el->namespace_decls) {
+ if (Maybe<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
+ decls.push_back(PackageDecl{decl.prefix, std::move(maybe_package.value())});
+ }
}
+ package_decls_.push_back(std::move(decls));
+}
+
+void PackageAwareVisitor::AfterVisitElement(Element* el) {
+ package_decls_.pop_back();
}
Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
@@ -522,11 +487,16 @@ Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
const auto rend = package_decls_.rend();
for (auto iter = package_decls_.rbegin(); iter != rend; ++iter) {
- if (alias == iter->prefix) {
- if (iter->package.package.empty()) {
- return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
+ const std::vector<PackageDecl>& decls = *iter;
+ const auto rend2 = decls.rend();
+ for (auto iter2 = decls.rbegin(); iter2 != rend2; ++iter2) {
+ const PackageDecl& decl = *iter2;
+ if (alias == decl.prefix) {
+ if (decl.package.package.empty()) {
+ return ExtractedPackage{local_package.to_string(), decl.package.private_namespace};
+ }
+ return decl.package;
}
- return iter->package;
}
}
return {};
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 2dc99d693148..154224381626 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -17,7 +17,6 @@
#ifndef AAPT_XML_DOM_H
#define AAPT_XML_DOM_H
-#include <istream>
#include <memory>
#include <string>
#include <vector>
@@ -27,58 +26,40 @@
#include "Diagnostics.h"
#include "Resource.h"
#include "ResourceValues.h"
+#include "io/Io.h"
#include "util/Util.h"
#include "xml/XmlUtil.h"
namespace aapt {
namespace xml {
-class RawVisitor;
-
class Element;
+class Visitor;
-/**
- * Base class for all XML nodes.
- */
+// Base class for all XML nodes.
class Node {
public:
- Node* parent = nullptr;
- size_t line_number = 0;
- size_t column_number = 0;
- std::string comment;
- std::vector<std::unique_ptr<Node>> children;
-
virtual ~Node() = default;
- void AppendChild(std::unique_ptr<Node> child);
- void InsertChild(size_t index, std::unique_ptr<Node> child);
- virtual void Accept(RawVisitor* visitor) = 0;
+ Element* parent = nullptr;
+ size_t line_number = 0u;
+ size_t column_number = 0u;
+ std::string comment;
+
+ virtual void Accept(Visitor* visitor) = 0;
using ElementCloneFunc = std::function<void(const Element&, Element*)>;
// Clones the Node subtree, using the given function to decide how to clone an Element.
- virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) = 0;
+ virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const = 0;
};
-/**
- * Base class that implements the visitor methods for a
- * subclass of Node.
- */
-template <typename Derived>
-class BaseNode : public Node {
- public:
- virtual void Accept(RawVisitor* visitor) override;
-};
-
-/**
- * A Namespace XML node. Can only have one child.
- */
-class Namespace : public BaseNode<Namespace> {
- public:
- std::string namespace_prefix;
- std::string namespace_uri;
-
- std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
+// A namespace declaration (xmlns:prefix="uri").
+struct NamespaceDecl {
+ std::string prefix;
+ std::string uri;
+ size_t line_number = 0u;
+ size_t column_number = 0u;
};
struct AaptAttribute {
@@ -90,9 +71,7 @@ struct AaptAttribute {
Maybe<ResourceId> id;
};
-/**
- * An XML attribute.
- */
+// An XML attribute.
struct Attribute {
std::string namespace_uri;
std::string name;
@@ -102,41 +81,50 @@ struct Attribute {
std::unique_ptr<Item> compiled_value;
};
-/**
- * An Element XML node.
- */
-class Element : public BaseNode<Element> {
+// An Element XML node.
+class Element : public Node {
public:
+ // Ordered namespace prefix declarations.
+ std::vector<NamespaceDecl> namespace_decls;
+
std::string namespace_uri;
std::string name;
std::vector<Attribute> attributes;
+ std::vector<std::unique_ptr<Node>> children;
+
+ void AppendChild(std::unique_ptr<Node> child);
+ void InsertChild(size_t index, std::unique_ptr<Node> child);
Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
const Attribute* FindAttribute(const android::StringPiece& ns,
const android::StringPiece& name) const;
- xml::Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
- xml::Element* FindChildWithAttribute(const android::StringPiece& ns,
- const android::StringPiece& name,
- const android::StringPiece& attr_ns,
- const android::StringPiece& attr_name,
- const android::StringPiece& attr_value);
- std::vector<xml::Element*> GetChildElements();
- std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
+ Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
+ Element* FindChildWithAttribute(const android::StringPiece& ns, const android::StringPiece& name,
+ const android::StringPiece& attr_ns,
+ const android::StringPiece& attr_name,
+ const android::StringPiece& attr_value);
+ std::vector<Element*> GetChildElements();
+
+ // Due to overriding of subtypes not working with unique_ptr, define a convenience Clone method
+ // that knows cloning an element returns an element.
+ std::unique_ptr<Element> CloneElement(const ElementCloneFunc& el_cloner) const;
+
+ std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
+
+ void Accept(Visitor* visitor) override;
};
-/**
- * A Text (CDATA) XML node. Can not have any children.
- */
-class Text : public BaseNode<Text> {
+// A Text (CDATA) XML node. Can not have any children.
+class Text : public Node {
public:
std::string text;
- std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
+ std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
+
+ void Accept(Visitor* visitor) override;
};
-/**
- * An XML resource with a source, name, and XML tree.
- */
+// An XML resource with a source, name, and XML tree.
class XmlResource {
public:
ResourceFile file;
@@ -146,99 +134,121 @@ class XmlResource {
// is destroyed.
StringPool string_pool;
- std::unique_ptr<xml::Node> root;
+ std::unique_ptr<xml::Element> root;
};
-/**
- * Inflates an XML DOM from a text stream, logging errors to the logger.
- * Returns the root node on success, or nullptr on failure.
- */
-std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source);
+// Inflates an XML DOM from an InputStream, logging errors to the logger.
+// Returns the root node on success, or nullptr on failure.
+std::unique_ptr<XmlResource> Inflate(io::InputStream* in, IDiagnostics* diag, const Source& source);
-/**
- * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
- * Returns the root node on success, or nullptr on failure.
- */
+// Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
+// Returns the root node on success, or nullptr on failure.
std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag,
const Source& source);
-Element* FindRootElement(XmlResource* doc);
Element* FindRootElement(Node* node);
-/**
- * A visitor interface for the different XML Node subtypes. This will not
- * traverse into
- * children. Use Visitor for that.
- */
-class RawVisitor {
- public:
- virtual ~RawVisitor() = default;
-
- virtual void Visit(Namespace* node) {}
- virtual void Visit(Element* node) {}
- virtual void Visit(Text* text) {}
-};
-
-/**
- * Visitor whose default implementation visits the children nodes of any node.
- */
-class Visitor : public RawVisitor {
+// Visitor whose default implementation visits the children nodes of any node.
+class Visitor {
public:
- using RawVisitor::Visit;
+ virtual ~Visitor() = default;
- void Visit(Namespace* node) override { VisitChildren(node); }
+ virtual void Visit(Element* el) {
+ VisitChildren(el);
+ }
- void Visit(Element* node) override { VisitChildren(node); }
+ virtual void Visit(Text* text) {
+ }
- void Visit(Text* text) override { VisitChildren(text); }
+ protected:
+ Visitor() = default;
- void VisitChildren(Node* node) {
- for (auto& child : node->children) {
+ void VisitChildren(Element* el) {
+ for (auto& child : el->children) {
child->Accept(this);
}
}
+
+ virtual void BeforeVisitElement(Element* el) {
+ }
+ virtual void AfterVisitElement(Element* el) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+
+ friend class Element;
};
-/**
- * An XML DOM visitor that will record the package name for a namespace prefix.
- */
+// An XML DOM visitor that will record the package name for a namespace prefix.
class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
- void Visit(Namespace* ns) override;
Maybe<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias, const android::StringPiece& local_package) const override;
+ protected:
+ PackageAwareVisitor() = default;
+
+ void BeforeVisitElement(Element* el) override;
+ void AfterVisitElement(Element* el) override;
+
private:
+ DISALLOW_COPY_AND_ASSIGN(PackageAwareVisitor);
+
struct PackageDecl {
std::string prefix;
ExtractedPackage package;
};
- std::vector<PackageDecl> package_decls_;
+ std::vector<std::vector<PackageDecl>> package_decls_;
};
-// Implementations
+namespace internal {
-template <typename Derived>
-void BaseNode<Derived>::Accept(RawVisitor* visitor) {
- visitor->Visit(static_cast<Derived*>(this));
-}
+// Base class that overrides the default behaviour and does not descend into child nodes.
+class NodeCastBase : public Visitor {
+ public:
+ void Visit(Element* el) override {
+ }
+ void Visit(Text* el) override {
+ }
+
+ protected:
+ NodeCastBase() = default;
+
+ void BeforeVisitElement(Element* el) override {
+ }
+ void AfterVisitElement(Element* el) override {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NodeCastBase);
+};
template <typename T>
-class NodeCastImpl : public RawVisitor {
+class NodeCastImpl : public NodeCastBase {
public:
- using RawVisitor::Visit;
+ using NodeCastBase::Visit;
+
+ NodeCastImpl() = default;
T* value = nullptr;
- void Visit(T* v) override { value = v; }
+ void Visit(T* v) override {
+ value = v;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NodeCastImpl);
};
+} // namespace internal
+
template <typename T>
T* NodeCast(Node* node) {
- NodeCastImpl<T> visitor;
+ internal::NodeCastImpl<T> visitor;
node->Accept(&visitor);
return visitor.value;
}
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index f0122e8c617a..6ed2d616f782 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -16,23 +16,22 @@
#include "xml/XmlDom.h"
-#include <sstream>
#include <string>
+#include "io/StringInputStream.h"
#include "test/Test.h"
+using ::aapt::io::StringInputStream;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::SizeIs;
+using ::testing::StrEq;
namespace aapt {
-
-constexpr const char* kXmlPreamble =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+namespace xml {
TEST(XmlDomTest, Inflate) {
- std::stringstream in(kXmlPreamble);
- in << R"(
+ std::string input = R"(<?xml version="1.0" encoding="utf-8"?>
<Layout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -41,26 +40,25 @@ TEST(XmlDomTest, Inflate) {
android:layout_height="wrap_content" />
</Layout>)";
- const Source source("test.xml");
StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);
+ StringInputStream in(input);
+ std::unique_ptr<XmlResource> doc = Inflate(&in, &diag, Source("test.xml"));
ASSERT_THAT(doc, NotNull());
- xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());
- ASSERT_THAT(ns, NotNull());
- EXPECT_THAT(ns->namespace_uri, Eq(xml::kSchemaAndroid));
- EXPECT_THAT(ns->namespace_prefix, Eq("android"));
+ Element* el = doc->root.get();
+ EXPECT_THAT(el->namespace_decls, SizeIs(1u));
+ EXPECT_THAT(el->namespace_decls[0].uri, StrEq(xml::kSchemaAndroid));
+ EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android"));
}
// Escaping is handled after parsing of the values for resource-specific values.
TEST(XmlDomTest, ForwardEscapes) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)");
- xml::Element* el = xml::FindRootElement(doc.get());
- ASSERT_THAT(el, NotNull());
+ Element* el = doc->root.get();
- xml::Attribute* attr = el->FindAttribute({}, "pattern");
+ Attribute* attr = el->FindAttribute({}, "pattern");
ASSERT_THAT(attr, NotNull());
EXPECT_THAT(attr->value, Eq("\\\\d{5}"));
@@ -70,21 +68,54 @@ TEST(XmlDomTest, ForwardEscapes) {
ASSERT_THAT(el->children, SizeIs(1u));
- xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
+ Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
ASSERT_THAT(text, NotNull());
EXPECT_THAT(text->text, Eq("\\\\d{5}"));
}
TEST(XmlDomTest, XmlEscapeSequencesAreParsed) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element value="&quot;" />)");
-
- xml::Element* el = xml::FindRootElement(doc.get());
- ASSERT_THAT(el, NotNull());
-
- xml::Attribute* attr = el->FindAttribute({}, "value");
+ std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(<element value="&quot;" />)");
+ Attribute* attr = doc->root->FindAttribute({}, "value");
ASSERT_THAT(attr, NotNull());
-
EXPECT_THAT(attr->value, Eq("\""));
}
+class TestVisitor : public PackageAwareVisitor {
+ public:
+ using PackageAwareVisitor::Visit;
+
+ void Visit(Element* el) override {
+ if (el->name == "View1") {
+ EXPECT_THAT(TransformPackageAlias("one", "local"),
+ Eq(make_value(ExtractedPackage{"com.one", false})));
+ } else if (el->name == "View2") {
+ EXPECT_THAT(TransformPackageAlias("one", "local"),
+ Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two", "local"),
+ Eq(make_value(ExtractedPackage{"com.two", false})));
+ } else if (el->name == "View3") {
+ EXPECT_THAT(TransformPackageAlias("one", "local"),
+ Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("two", "local"),
+ Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("three", "local"),
+ Eq(make_value(ExtractedPackage{"com.three", false})));
+ }
+ }
+};
+
+TEST(XmlDomTest, PackageAwareXmlVisitor) {
+ std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
+ <View1 xmlns:one="http://schemas.android.com/apk/res/com.one">
+ <View2 xmlns:two="http://schemas.android.com/apk/res/com.two">
+ <View3 xmlns:three="http://schemas.android.com/apk/res/com.three" />
+ </View2>
+ </View1>)");
+
+ Debug::DumpXml(doc.get());
+ TestVisitor visitor;
+ doc->root->Accept(&visitor);
+}
+
+} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index c2a9c8283a6d..30bdc507303b 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -22,14 +22,15 @@
#include "xml/XmlPullParser.h"
#include "xml/XmlUtil.h"
-using android::StringPiece;
+using ::aapt::io::InputStream;
+using ::android::StringPiece;
namespace aapt {
namespace xml {
constexpr char kXmlNamespaceSep = 1;
-XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) {
+XmlPullParser::XmlPullParser(InputStream* in) : in_(in), empty_(), depth_(0) {
parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
XML_SetUserData(parser_, this);
XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler);
@@ -40,30 +41,35 @@ XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) {
event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
}
-XmlPullParser::~XmlPullParser() { XML_ParserFree(parser_); }
+XmlPullParser::~XmlPullParser() {
+ XML_ParserFree(parser_);
+}
XmlPullParser::Event XmlPullParser::Next() {
const Event currentEvent = event();
- if (currentEvent == Event::kBadDocument ||
- currentEvent == Event::kEndDocument) {
+ if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
return currentEvent;
}
event_queue_.pop();
while (event_queue_.empty()) {
- in_.read(buffer_, sizeof(buffer_) / sizeof(*buffer_));
+ const char* buffer = nullptr;
+ size_t buffer_size = 0;
+ bool done = false;
+ if (!in_->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) {
+ if (in_->HadError()) {
+ error_ = in_->GetError();
+ event_queue_.push(EventData{Event::kBadDocument});
+ break;
+ }
- const bool done = in_.eof();
- if (in_.bad() && !done) {
- error_ = strerror(errno);
- event_queue_.push(EventData{Event::kBadDocument});
- continue;
+ done = true;
}
- if (XML_Parse(parser_, buffer_, in_.gcount(), done) == XML_STATUS_ERROR) {
+ if (XML_Parse(parser_, buffer, buffer_size, done) == XML_STATUS_ERROR) {
error_ = XML_ErrorString(XML_GetErrorCode(parser_));
event_queue_.push(EventData{Event::kBadDocument});
- continue;
+ break;
}
if (done) {
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index cdeeefd13976..a00caa139061 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -31,6 +31,7 @@
#include "androidfw/StringPiece.h"
#include "Resource.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "util/Maybe.h"
#include "xml/XmlUtil.h"
@@ -64,7 +65,7 @@ class XmlPullParser : public IPackageDeclStack {
static bool SkipCurrentElement(XmlPullParser* parser);
static bool IsGoodEvent(Event event);
- explicit XmlPullParser(std::istream& in);
+ explicit XmlPullParser(io::InputStream* in);
~XmlPullParser();
/**
@@ -169,9 +170,8 @@ class XmlPullParser : public IPackageDeclStack {
std::vector<Attribute> attributes;
};
- std::istream& in_;
+ io::InputStream* in_;
XML_Parser parser_;
- char buffer_[16384];
std::queue<EventData> event_queue_;
std::string error_;
const std::string empty_;
@@ -228,18 +228,15 @@ inline ::std::ostream& operator<<(::std::ostream& out,
return out;
}
-inline bool XmlPullParser::NextChildNode(XmlPullParser* parser,
- size_t start_depth) {
+inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) {
Event event;
// First get back to the start depth.
- while (IsGoodEvent(event = parser->Next()) &&
- parser->depth() > start_depth + 1) {
+ while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) {
}
// Now look for the first good node.
- while ((event != Event::kEndElement || parser->depth() > start_depth) &&
- IsGoodEvent(event)) {
+ while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) {
switch (event) {
case Event::kText:
case Event::kComment:
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
index 1cce4850cac5..681d9d48173f 100644
--- a/tools/aapt2/xml/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -16,21 +16,22 @@
#include "xml/XmlPullParser.h"
-#include <sstream>
-
#include "androidfw/StringPiece.h"
+#include "io/StringInputStream.h"
#include "test/Test.h"
-using android::StringPiece;
+using ::aapt::io::StringInputStream;
+using ::android::StringPiece;
namespace aapt {
TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
- std::stringstream str;
- str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
- xml::XmlPullParser parser(str);
+ std::string str =
+ R"(<?xml version="1.0" encoding="utf-8"?>
+ <a><b><c xmlns:a="http://schema.org"><d/></c><e/></b></a>)";
+ StringInputStream input(str);
+ xml::XmlPullParser parser(&input);
const size_t depth_outer = parser.depth();
ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 1650ac2124ac..866b6dcd7a88 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -26,79 +26,56 @@ namespace aapt {
namespace xml {
constexpr const char* kSchemaAuto = "http://schemas.android.com/apk/res-auto";
-constexpr const char* kSchemaPublicPrefix =
- "http://schemas.android.com/apk/res/";
-constexpr const char* kSchemaPrivatePrefix =
- "http://schemas.android.com/apk/prv/res/";
-constexpr const char* kSchemaAndroid =
- "http://schemas.android.com/apk/res/android";
+constexpr const char* kSchemaPublicPrefix = "http://schemas.android.com/apk/res/";
+constexpr const char* kSchemaPrivatePrefix = "http://schemas.android.com/apk/prv/res/";
+constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android";
constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
-/**
- * Result of extracting a package name from a namespace URI declaration.
- */
+// Result of extracting a package name from a namespace URI declaration.
struct ExtractedPackage {
- /**
- * The name of the package. This can be the empty string, which means that the
- * package
- * should be assumed to be the package being compiled.
- */
+ // The name of the package. This can be the empty string, which means that the package
+ // should be assumed to be the package being compiled.
std::string package;
- /**
- * True if the package's private namespace was declared. This means that
- * private resources
- * are made visible.
- */
+ // True if the package's private namespace was declared. This means that private resources
+ // are made visible.
bool private_namespace;
+
+ friend inline bool operator==(const ExtractedPackage& a, const ExtractedPackage& b) {
+ return a.package == b.package && a.private_namespace == b.private_namespace;
+ }
};
-/**
- * Returns an ExtractedPackage struct if the namespace URI is of the form:
- * http://schemas.android.com/apk/res/<package> or
- * http://schemas.android.com/apk/prv/res/<package>
- *
- * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
- * returns an empty package name.
- */
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(
- const std::string& namespace_uri);
+// Returns an ExtractedPackage struct if the namespace URI is of the form:
+// http://schemas.android.com/apk/res/<package> or
+// http://schemas.android.com/apk/prv/res/<package>
+//
+// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
+// returns an empty package name.
+Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
-/**
- * Returns an XML Android namespace for the given package of the form:
- *
- * http://schemas.android.com/apk/res/<package>
- *
- * If privateReference == true, the package will be of the form:
- *
- * http://schemas.android.com/apk/prv/res/<package>
- */
+// Returns an XML Android namespace for the given package of the form:
+// http://schemas.android.com/apk/res/<package>
+//
+// If privateReference == true, the package will be of the form:
+// http://schemas.android.com/apk/prv/res/<package>
std::string BuildPackageNamespace(const android::StringPiece& package,
bool private_reference = false);
-/**
- * Interface representing a stack of XML namespace declarations. When looking up
- * the package
- * for a namespace prefix, the stack is checked from top to bottom.
- */
+// Interface representing a stack of XML namespace declarations. When looking up the package
+// for a namespace prefix, the stack is checked from top to bottom.
struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
- /**
- * Returns an ExtractedPackage struct if the alias given corresponds with a
- * package declaration.
- */
+ // Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
virtual Maybe<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
};
-/**
- * Helper function for transforming the original Reference inRef to a fully
- * qualified reference
- * via the IPackageDeclStack. This will also mark the Reference as private if
- * the namespace of the package declaration was private.
- */
+// Helper function for transforming the original Reference inRef to a fully qualified reference
+// via the IPackageDeclStack. This will also mark the Reference as private if the namespace of the
+// package declaration was private.
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
const android::StringPiece& local_package, Reference* in_ref);