summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/AaptXml.cpp42
-rw-r--r--tools/aapt/SdkConstants.h3
-rw-r--r--tools/aapt/pseudolocalize.cpp12
-rw-r--r--tools/aapt/tests/Pseudolocales_test.cpp4
-rw-r--r--tools/aapt2/Android.bp9
-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/LoadedApk.cpp13
-rw-r--r--tools/aapt2/LoadedApk.h9
-rw-r--r--tools/aapt2/Main.cpp172
-rw-r--r--tools/aapt2/OWNERS2
-rw-r--r--tools/aapt2/Resource.cpp3
-rw-r--r--tools/aapt2/Resource.h1
-rw-r--r--tools/aapt2/ResourceParser.cpp16
-rw-r--r--tools/aapt2/ResourceParser_test.cpp782
-rw-r--r--tools/aapt2/ResourceTable.cpp27
-rw-r--r--tools/aapt2/ResourceTable_test.cpp27
-rw-r--r--tools/aapt2/ResourceUtils.cpp6
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp150
-rw-r--r--tools/aapt2/ResourceValues.cpp134
-rw-r--r--tools/aapt2/ResourceValues.h4
-rw-r--r--tools/aapt2/ResourceValues_test.cpp66
-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/SdkConstants.h1
-rw-r--r--tools/aapt2/StringPool.cpp324
-rw-r--r--tools/aapt2/StringPool.h100
-rw-r--r--tools/aapt2/StringPool_test.cpp245
-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.cpp28
-rw-r--r--tools/aapt2/cmd/Optimize.cpp91
-rw-r--r--tools/aapt2/cmd/Util.cpp30
-rw-r--r--tools/aapt2/compile/IdAssigner_test.cpp12
-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/compile/PseudolocaleGenerator.cpp2
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator_test.cpp14
-rw-r--r--tools/aapt2/compile/Pseudolocalizer.cpp12
-rw-r--r--tools/aapt2/compile/Pseudolocalizer_test.cpp5
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp542
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.h37
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp102
-rw-r--r--tools/aapt2/configuration/example/config.xml2
-rw-r--r--tools/aapt2/filter/AbiFilter.cpp51
-rw-r--r--tools/aapt2/filter/AbiFilter.h54
-rw-r--r--tools/aapt2/filter/AbiFilter_test.cpp66
-rw-r--r--tools/aapt2/filter/Filter.h77
-rw-r--r--tools/aapt2/filter/Filter_test.cpp54
-rw-r--r--tools/aapt2/flatten/Archive.cpp16
-rw-r--r--tools/aapt2/flatten/TableFlattener.cpp22
-rw-r--r--tools/aapt2/flatten/TableFlattener_test.cpp32
-rw-r--r--tools/aapt2/flatten/XmlFlattener.cpp82
-rw-r--r--tools/aapt2/integration-tests/AppOne/res/navigation/home.xml2
-rw-r--r--tools/aapt2/integration-tests/AppOne/res/values/styles.xml1
-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/AnnotationProcessor.cpp19
-rw-r--r--tools/aapt2/java/AnnotationProcessor.h2
-rw-r--r--tools/aapt2/java/AnnotationProcessor_test.cpp24
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp11
-rw-r--r--tools/aapt2/java/ClassDefinition.h63
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp20
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp5
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp162
-rw-r--r--tools/aapt2/java/ProguardRules.cpp97
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp119
-rw-r--r--tools/aapt2/link/AutoVersioner_test.cpp93
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp3
-rw-r--r--tools/aapt2/link/ManifestFixer_test.cpp39
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp11
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp42
-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.cpp203
-rw-r--r--tools/aapt2/proto/ProtoHelpers.cpp69
-rw-r--r--tools/aapt2/proto/ProtoHelpers.h18
-rw-r--r--tools/aapt2/proto/ProtoSerialize.h16
-rw-r--r--tools/aapt2/proto/TableProtoDeserializer.cpp230
-rw-r--r--tools/aapt2/proto/TableProtoSerializer.cpp150
-rw-r--r--tools/aapt2/proto/TableProtoSerializer_test.cpp91
-rw-r--r--tools/aapt2/readme.md40
-rw-r--r--tools/aapt2/test/Builders.cpp216
-rw-r--r--tools/aapt2/test/Builders.h200
-rw-r--r--tools/aapt2/test/Common.h104
-rw-r--r--tools/aapt2/text/Unicode.cpp125
-rw-r--r--tools/aapt2/text/Unicode.h58
-rw-r--r--tools/aapt2/text/Unicode_data.cpp629
-rw-r--r--tools/aapt2/text/Unicode_test.cpp68
-rw-r--r--tools/aapt2/text/Utf8Iterator.cpp65
-rw-r--r--tools/aapt2/text/Utf8Iterator.h54
-rw-r--r--tools/aapt2/text/Utf8Iterator_test.cpp95
-rw-r--r--tools/aapt2/tools/extract_unicode_properties.py102
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp30
-rw-r--r--tools/aapt2/util/BigBuffer_test.cpp16
-rw-r--r--tools/aapt2/util/Files.cpp94
-rw-r--r--tools/aapt2/util/Files.h81
-rw-r--r--tools/aapt2/util/Maybe_test.cpp10
-rw-r--r--tools/aapt2/util/Util.cpp171
-rw-r--r--tools/aapt2/util/Util.h30
-rw-r--r--tools/aapt2/util/Util_test.cpp72
-rw-r--r--tools/aapt2/xml/XmlActionExecutor.cpp2
-rw-r--r--tools/aapt2/xml/XmlActionExecutor_test.cpp12
-rw-r--r--tools/aapt2/xml/XmlDom.cpp346
-rw-r--r--tools/aapt2/xml/XmlDom.h224
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp102
-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
-rw-r--r--tools/aapt2/xml/XmlUtil_test.cpp31
-rw-r--r--tools/bit/adb.cpp17
-rw-r--r--tools/bit/main.cpp59
-rw-r--r--tools/incident_report/Android.bp3
-rw-r--r--tools/incident_section_gen/Android.bp2
127 files changed, 6506 insertions, 3532 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/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index d92de062bcf0..bf56ec024699 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -41,7 +41,8 @@ enum {
SDK_MNC = 23,
SDK_NOUGAT = 24,
SDK_NOUGAT_MR1 = 25,
- SDK_O = 26, // STOPSHIP replace with real version
+ SDK_O = 26,
+ SDK_O_MR1 = 27,
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index c7fee2c19342..5c47e0fa8a16 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -360,9 +360,15 @@ String16 PseudoMethodBidi::text(const String16& source)
String16 result;
bool lastspace = true;
bool space = true;
+ bool escape = false;
+ const char16_t ESCAPE_CHAR = '\\';
for (size_t i=0; i<source.size(); i++) {
char16_t c = s[i];
- space = is_space(c);
+ if (!escape && c == ESCAPE_CHAR) {
+ escape = true;
+ continue;
+ }
+ space = (!escape && is_space(c)) || (escape && (c == 'n' || c == 't'));
if (lastspace && !space) {
// Word start
result += k_rlm + k_rlo;
@@ -371,6 +377,10 @@ String16 PseudoMethodBidi::text(const String16& source)
result += k_pdf + k_rlm;
}
lastspace = space;
+ if (escape) {
+ result.append(&ESCAPE_CHAR, 1);
+ escape=false;
+ }
result.append(&c, 1);
}
if (!lastspace) {
diff --git a/tools/aapt/tests/Pseudolocales_test.cpp b/tools/aapt/tests/Pseudolocales_test.cpp
index 4670e9fd53ea..a6aed3abd7cf 100644
--- a/tools/aapt/tests/Pseudolocales_test.cpp
+++ b/tools/aapt/tests/Pseudolocales_test.cpp
@@ -87,6 +87,10 @@ TEST(Pseudolocales, PlaintextBidi) {
"\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
" \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
PSEUDO_BIDI);
+ simple_helper("hello\\nworld\\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\\n"
+ "\xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\\n",
+ PSEUDO_BIDI);
}
TEST(Pseudolocales, SimpleICU) {
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 046de469c7cc..c6cfd3032762 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -81,13 +81,16 @@ cc_library_host_static {
"compile/Pseudolocalizer.cpp",
"compile/XmlIdCollector.cpp",
"configuration/ConfigurationParser.cpp",
+ "filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
"flatten/Archive.cpp",
"flatten/TableFlattener.cpp",
"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",
@@ -106,6 +109,8 @@ cc_library_host_static {
"proto/TableProtoDeserializer.cpp",
"proto/TableProtoSerializer.cpp",
"split/TableSplitter.cpp",
+ "text/Unicode.cpp",
+ "text/Utf8Iterator.cpp",
"unflatten/BinaryResourceParser.cpp",
"unflatten/ResChunkPullParser.cpp",
"util/BigBuffer.cpp",
@@ -133,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,
@@ -157,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/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 8a8f8be205e7..7e5efa15f61b 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -57,6 +57,12 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
IArchiveWriter* writer) {
+ FilterChain empty;
+ return WriteToArchive(context, options, &empty, writer);
+}
+
+bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
+ FilterChain* filters, IArchiveWriter* writer) {
std::set<std::string> referenced_resources;
// List the files being referenced in the resource table.
for (auto& pkg : table_->packages) {
@@ -89,6 +95,13 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
continue;
}
+ if (!filters->Keep(path)) {
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK.");
+ }
+ continue;
+ }
+
// The resource table needs to be re-serialized since it might have changed.
if (path == "resources.arsc") {
BigBuffer buffer(4096);
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 59eb8161a868..8aa9674aa2ed 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -20,6 +20,7 @@
#include "androidfw/StringPiece.h"
#include "ResourceTable.h"
+#include "filter/Filter.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
#include "io/ZipArchive.h"
@@ -49,6 +50,14 @@ class LoadedApk {
bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
IArchiveWriter* writer);
+ /**
+ * Writes the APK on disk at the given path, while also removing the resource
+ * files that are not referenced in the resource table. The provided filter
+ * chain is applied to each entry in the APK file.
+ */
+ bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
+ FilterChain* filters, IArchiveWriter* writer);
+
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path);
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 1d2e3a4f2df0..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 = "16";
+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/OWNERS b/tools/aapt2/OWNERS
new file mode 100644
index 000000000000..d76233ec78ba
--- /dev/null
+++ b/tools/aapt2/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+adamlesinski@google.com
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 9a37913f0edc..1c3ac2ad4f17 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -268,8 +268,7 @@ bool ResourceParser::Parse(xml::XmlPullParser* parser) {
continue;
}
- if (!parser->element_namespace().empty() ||
- parser->element_name() != "resources") {
+ if (!parser->element_namespace().empty() || parser->element_name() != "resources") {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
<< "root element must be <resources>");
return false;
@@ -328,8 +327,7 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
parsed_resource.comment = std::move(comment);
// Extract the product name if it exists.
- if (Maybe<StringPiece> maybe_product =
- xml::FindNonEmptyAttribute(parser, "product")) {
+ if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
parsed_resource.product = maybe_product.value().to_string();
}
@@ -348,10 +346,8 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
for (const ResourceName& stripped_resource : stripped_resources) {
if (!table_->FindResource(stripped_resource)) {
// Failed to find the resource.
- diag_->Error(DiagMessage(source_)
- << "resource '" << stripped_resource
- << "' "
- "was filtered out but no product variant remains");
+ diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource
+ << "' was filtered out but no product variant remains");
error = true;
}
}
@@ -589,7 +585,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
// This can only be a StyledString.
std::unique_ptr<StyledString> styled_string =
util::make_unique<StyledString>(table_->string_pool.MakeRef(
- style_string, StringPool::Context(StringPool::Context::kStylePriority, config_)));
+ style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
styled_string->untranslatable_sections = std::move(untranslatable_sections);
return std::move(styled_string);
}
@@ -1223,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 c6382b177f42..144ebd22e105 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -22,14 +22,22 @@
#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;
+using ::android::Res_value;
using ::android::StringPiece;
using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Pointee;
+using ::testing::SizeIs;
namespace aapt {
@@ -37,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));
}
@@ -56,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();
}
@@ -74,31 +88,31 @@ class ResourceParserTest : public ::testing::Test {
};
TEST_F(ResourceParserTest, ParseQuotedString) {
- std::string input = "<string name=\"foo\"> \" hey there \" </string>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<string name="foo"> " hey there " </string>)"));
String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" hey there "), *str->value);
- EXPECT_TRUE(str->untranslatable_sections.empty());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq(" hey there "));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
}
TEST_F(ResourceParserTest, ParseEscapedString) {
- std::string input = "<string name=\"foo\">\\?123</string>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<string name="foo">\?123</string>)"));
String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("?123"), *str->value);
- EXPECT_TRUE(str->untranslatable_sections.empty());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("?123"));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
+
+ ASSERT_TRUE(TestParse(R"(<string name="bar">This isn\’t a bad string</string>)"));
+ str = test::GetValue<String>(&table_, "string/bar");
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("This isn’t a bad string"));
}
TEST_F(ResourceParserTest, ParseFormattedString) {
- std::string input = "<string name=\"foo\">%d %s</string>";
- ASSERT_FALSE(TestParse(input));
-
- input = "<string name=\"foo\">%1$d %2$s</string>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_FALSE(TestParse(R"(<string name="foo">%d %s</string>)"));
+ ASSERT_TRUE(TestParse(R"(<string name="foo">%1$d %2$s</string>)"));
}
TEST_F(ResourceParserTest, ParseStyledString) {
@@ -109,98 +123,93 @@ TEST_F(ResourceParserTest, ParseStyledString) {
ASSERT_TRUE(TestParse(input));
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
+ ASSERT_THAT(str, NotNull());
- const std::string expected_str = "This is my aunt\u2019s fickle string";
- EXPECT_EQ(expected_str, *str->value->str);
- EXPECT_EQ(2u, str->value->spans.size());
- EXPECT_TRUE(str->untranslatable_sections.empty());
+ EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string"));
+ EXPECT_THAT(str->value->spans, SizeIs(2));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
- EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
- EXPECT_EQ(17u, str->value->spans[0].first_char);
- EXPECT_EQ(30u, str->value->spans[0].last_char);
+ EXPECT_THAT(*str->value->spans[0].name, Eq("b"));
+ EXPECT_THAT(str->value->spans[0].first_char, Eq(17u));
+ EXPECT_THAT(str->value->spans[0].last_char, Eq(30u));
- EXPECT_EQ(std::string("small"), *str->value->spans[1].name);
- EXPECT_EQ(24u, str->value->spans[1].first_char);
- EXPECT_EQ(30u, str->value->spans[1].last_char);
+ EXPECT_THAT(*str->value->spans[1].name, Eq("small"));
+ EXPECT_THAT(str->value->spans[1].first_char, Eq(24u));
+ EXPECT_THAT(str->value->spans[1].last_char, Eq(30u));
}
TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
- std::string input = "<string name=\"foo\"> This is what I think </string>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<string name="foo"> This is what I think </string>)"));
String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("This is what I think"), *str->value);
- EXPECT_TRUE(str->untranslatable_sections.empty());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str->value, Eq("This is what I think"));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
- input = "<string name=\"foo2\">\" This is what I think \"</string>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<string name="foo2">" This is what I think "</string>)"));
str = test::GetValue<String>(&table_, "string/foo2");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" This is what I think "), *str->value);
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq(" This is what I think "));
}
TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
- std::string input = R"EOF(
+ std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- There are <xliff:source>no</xliff:source> apples</string>)EOF";
+ There are <xliff:source>no</xliff:source> apples</string>)";
ASSERT_TRUE(TestParse(input));
String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(StringPiece("There are no apples"), StringPiece(*str->value));
- EXPECT_TRUE(str->untranslatable_sections.empty());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("There are no apples"));
+ EXPECT_THAT(str->untranslatable_sections, IsEmpty());
}
TEST_F(ResourceParserTest, NestedXliffGTagsAreIllegal) {
- std::string input = R"EOF(
+ std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)EOF";
+ Do not <xliff:g>translate <xliff:g>this</xliff:g></xliff:g></string>)";
EXPECT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInString) {
- std::string input = R"EOF(
+ std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- There are <xliff:g id="count">%1$d</xliff:g> apples</string>)EOF";
+ There are <xliff:g id="count">%1$d</xliff:g> apples</string>)";
ASSERT_TRUE(TestParse(input));
String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
-
- ASSERT_EQ(1u, str->untranslatable_sections.size());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("There are %1$d apples"));
+ ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
// We expect indices and lengths that span to include the whitespace
// before %1$d. This is due to how the StringBuilder withholds whitespace unless
// needed (to deal with line breaks, etc.).
- EXPECT_EQ(9u, str->untranslatable_sections[0].start);
- EXPECT_EQ(14u, str->untranslatable_sections[0].end);
+ EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
+ EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
}
TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) {
- std::string input = R"EOF(
+ std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)EOF";
+ There are <b><xliff:g id="count">%1$d</xliff:g></b> apples</string>)";
ASSERT_TRUE(TestParse(input));
StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value->str));
-
- ASSERT_EQ(1u, str->untranslatable_sections.size());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(str->value->value, Eq("There are %1$d apples"));
+ ASSERT_THAT(str->untranslatable_sections, SizeIs(1));
// We expect indices and lengths that span to include the whitespace
// before %1$d. This is due to how the StringBuilder withholds whitespace unless
// needed (to deal with line breaks, etc.).
- EXPECT_EQ(9u, str->untranslatable_sections[0].start);
- EXPECT_EQ(14u, str->untranslatable_sections[0].end);
+ EXPECT_THAT(str->untranslatable_sections[0].start, Eq(9u));
+ EXPECT_THAT(str->untranslatable_sections[0].end, Eq(14u));
}
TEST_F(ResourceParserTest, ParseNull) {
- std::string input = "<integer name=\"foo\">@null</integer>";
+ std::string input = R"(<integer name="foo">@null</integer>)";
ASSERT_TRUE(TestParse(input));
// The Android runtime treats a value of android::Res_value::TYPE_NULL as
@@ -211,38 +220,36 @@ TEST_F(ResourceParserTest, ParseNull) {
ASSERT_THAT(null_ref, NotNull());
EXPECT_FALSE(null_ref->name);
EXPECT_FALSE(null_ref->id);
- EXPECT_EQ(Reference::Type::kResource, null_ref->reference_type);
+ EXPECT_THAT(null_ref->reference_type, Eq(Reference::Type::kResource));
}
TEST_F(ResourceParserTest, ParseEmpty) {
- std::string input = "<integer name=\"foo\">@empty</integer>";
+ std::string input = R"(<integer name="foo">@empty</integer>)";
ASSERT_TRUE(TestParse(input));
BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
- ASSERT_NE(nullptr, integer);
- EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
- EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
+ ASSERT_THAT(integer, NotNull());
+ EXPECT_THAT(integer->value.dataType, Eq(Res_value::TYPE_NULL));
+ EXPECT_THAT(integer->value.data, Eq(Res_value::DATA_NULL_EMPTY));
}
TEST_F(ResourceParserTest, ParseAttr) {
- std::string input =
- "<attr name=\"foo\" format=\"string\"/>\n"
- "<attr name=\"bar\"/>";
+ std::string input = R"(
+ <attr name="foo" format="string"/>
+ <attr name="bar"/>)";
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_STRING));
attr = test::GetValue<Attribute>(&table_, "attr/bar");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
}
-// Old AAPT allowed attributes to be defined under different configurations, but
-// ultimately
-// stored them with the default configuration. Check that we have the same
-// behavior.
+// Old AAPT allowed attributes to be defined under different configurations, but ultimately
+// stored them with the default configuration. Check that we have the same behavior.
TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
std::string input = R"(
@@ -252,583 +259,519 @@ TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoC
</declare-styleable>)";
ASSERT_TRUE(TestParse(input, watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config));
+ EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config), IsNull());
+ EXPECT_THAT(test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config), IsNull());
+ EXPECT_THAT(test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config), IsNull());
- EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
- EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
- EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar"));
+ EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/foo"), NotNull());
+ EXPECT_THAT(test::GetValue<Attribute>(&table_, "attr/baz"), NotNull());
+ EXPECT_THAT(test::GetValue<Styleable>(&table_, "styleable/bar"), NotNull());
}
TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
- std::string input =
- "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
+ std::string input = R"(<attr name="foo" min="10" max="23" format="integer"/>)";
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask);
- EXPECT_EQ(10, attr->min_int);
- EXPECT_EQ(23, attr->max_int);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_INTEGER));
+ EXPECT_THAT(attr->min_int, Eq(10));
+ EXPECT_THAT(attr->max_int, Eq(23));
}
TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
- std::string input =
- "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
- ASSERT_FALSE(TestParse(input));
+ ASSERT_FALSE(TestParse(R"(<attr name="foo" min="10" max="23" format="string"/>)"));
}
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
- std::string input =
- "<declare-styleable name=\"Styleable\">\n"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<attr name=\"foo\" format=\"string\"/>";
+ std::string input = R"(
+ <declare-styleable name="Styleable">
+ <attr name="foo" />
+ </declare-styleable>
+ <attr name="foo" format="string"/>)";
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_STRING));
}
TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
- std::string input =
- "<declare-styleable name=\"Theme\">"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<declare-styleable name=\"Window\">\n"
- " <attr name=\"foo\" format=\"boolean\"/>\n"
- "</declare-styleable>";
+ std::string input = R"(
+ <declare-styleable name="Theme">
+ <attr name="foo" />
+ </declare-styleable>
+ <declare-styleable name="Window">
+ <attr name="foo" format="boolean"/>
+ </declare-styleable>)";
ASSERT_TRUE(TestParse(input));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_BOOLEAN));
}
TEST_F(ResourceParserTest, ParseEnumAttr) {
- std::string input =
- "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"baz\" value=\"2\"/>\n"
- "</attr>";
+ std::string input = R"(
+ <attr name="foo">
+ <enum name="bar" value="0"/>
+ <enum name="bat" value="1"/>
+ <enum name="baz" value="2"/>
+ </attr>)";
ASSERT_TRUE(TestParse(input));
Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(enum_attr, nullptr);
- EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM);
- ASSERT_EQ(enum_attr->symbols.size(), 3u);
+ ASSERT_THAT(enum_attr, NotNull());
+ EXPECT_THAT(enum_attr->type_mask, Eq(ResTable_map::TYPE_ENUM));
+ ASSERT_THAT(enum_attr->symbols, SizeIs(3));
- AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
- EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar");
- EXPECT_EQ(enum_attr->symbols[0].value, 0u);
+ ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
+ EXPECT_THAT(enum_attr->symbols[0].symbol.name.value().entry, Eq("bar"));
+ EXPECT_THAT(enum_attr->symbols[0].value, Eq(0u));
- AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
- EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat");
- EXPECT_EQ(enum_attr->symbols[1].value, 1u);
+ ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
+ EXPECT_THAT(enum_attr->symbols[1].symbol.name.value().entry, Eq("bat"));
+ EXPECT_THAT(enum_attr->symbols[1].value, Eq(1u));
- AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
- EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz");
- EXPECT_EQ(enum_attr->symbols[2].value, 2u);
+ ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
+ EXPECT_THAT(enum_attr->symbols[2].symbol.name.value().entry, Eq("baz"));
+ EXPECT_THAT(enum_attr->symbols[2].value, Eq(2u));
}
TEST_F(ResourceParserTest, ParseFlagAttr) {
- std::string input =
- "<attr name=\"foo\">\n"
- " <flag name=\"bar\" value=\"0\"/>\n"
- " <flag name=\"bat\" value=\"1\"/>\n"
- " <flag name=\"baz\" value=\"2\"/>\n"
- "</attr>";
+ std::string input = R"(
+ <attr name="foo">
+ <flag name="bar" value="0"/>
+ <flag name="bat" value="1"/>
+ <flag name="baz" value="2"/>
+ </attr>)";
ASSERT_TRUE(TestParse(input));
Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, flag_attr);
- EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS);
- ASSERT_EQ(flag_attr->symbols.size(), 3u);
+ ASSERT_THAT(flag_attr, NotNull());
+ EXPECT_THAT(flag_attr->type_mask, Eq(ResTable_map::TYPE_FLAGS));
+ ASSERT_THAT(flag_attr->symbols, SizeIs(3));
- AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
- EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar");
- EXPECT_EQ(flag_attr->symbols[0].value, 0u);
+ ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
+ EXPECT_THAT(flag_attr->symbols[0].symbol.name.value().entry, Eq("bar"));
+ EXPECT_THAT(flag_attr->symbols[0].value, Eq(0u));
- AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
- EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat");
- EXPECT_EQ(flag_attr->symbols[1].value, 1u);
+ ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
+ EXPECT_THAT(flag_attr->symbols[1].symbol.name.value().entry, Eq("bat"));
+ EXPECT_THAT(flag_attr->symbols[1].value, Eq(1u));
- AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
- EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz");
- EXPECT_EQ(flag_attr->symbols[2].value, 2u);
+ ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
+ EXPECT_THAT(flag_attr->symbols[2].symbol.name.value().entry, Eq("baz"));
+ EXPECT_THAT(flag_attr->symbols[2].value, Eq(2u));
std::unique_ptr<BinaryPrimitive> flag_value =
ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
- ASSERT_NE(nullptr, flag_value);
- EXPECT_EQ(flag_value->value.data, 1u | 2u);
+ ASSERT_THAT(flag_value, NotNull());
+ EXPECT_THAT(flag_value->value.data, Eq(1u | 2u));
}
TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
- std::string input =
- "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"bat\" value=\"2\"/>\n"
- "</attr>";
+ std::string input = R"(
+ <attr name="foo">
+ <enum name="bar" value="0"/>
+ <enum name="bat" value="1"/>
+ <enum name="bat" value="2"/>
+ </attr>)";
ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseStyle) {
- std::string input =
- "<style name=\"foo\" parent=\"@style/fu\">\n"
- " <item name=\"bar\">#ffffffff</item>\n"
- " <item name=\"bat\">@string/hey</item>\n"
- " <item name=\"baz\"><b>hey</b></item>\n"
- "</style>";
+ std::string input = R"(
+ <style name="foo" parent="@style/fu">
+ <item name="bar">#ffffffff</item>
+ <item name="bat">@string/hey</item>
+ <item name="baz"><b>hey</b></item>
+ </style>)";
ASSERT_TRUE(TestParse(input));
Style* style = test::GetValue<Style>(&table_, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::ParseNameOrDie("style/fu"),
- style->parent.value().name.value());
- ASSERT_EQ(3u, style->entries.size());
-
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
- style->entries[0].key.name.value());
-
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
- style->entries[1].key.name.value());
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
+ EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu"))));
+ ASSERT_THAT(style->entries, SizeIs(3));
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(test::ParseNameOrDie("attr/baz"),
- style->entries[2].key.name.value());
+ EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
+ EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
+ EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
- std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<style name="foo" parent="com.app:Theme"/>)"));
Style* style = test::GetValue<Style>(&table_, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"),
- style->parent.value().name.value());
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
+ EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme"))));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
- std::string input =
- "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
- " name=\"foo\" parent=\"app:Theme\"/>";
+ std::string input = R"(
+ <style xmlns:app="http://schemas.android.com/apk/res/android"
+ name="foo" parent="app:Theme"/>)";
ASSERT_TRUE(TestParse(input));
Style* style = test::GetValue<Style>(&table_, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"),
- style->parent.value().name.value());
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
+ ASSERT_TRUE(style->parent.value().name);
+ EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme"))));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
- std::string input =
- "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" "
- "name=\"foo\">\n"
- " <item name=\"app:bar\">0</item>\n"
- "</style>";
+ std::string input = R"(
+ <style xmlns:app="http://schemas.android.com/apk/res/android" name="foo">
+ <item name="app:bar">0</item>
+ </style>)";
ASSERT_TRUE(TestParse(input));
Style* style = test::GetValue<Style>(&table_, "style/foo");
- ASSERT_NE(nullptr, style);
- ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"),
- style->entries[0].key.name.value());
+ ASSERT_THAT(style, NotNull());
+ ASSERT_THAT(style->entries, SizeIs(1));
+ EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
}
TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
- std::string input = "<style name=\"foo.bar\"/>";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<style name="foo.bar"/>)"));
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(style->parent.value().name.value(),
- test::ParseNameOrDie("style/foo"));
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
+ EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo"))));
EXPECT_TRUE(style->parent_inferred);
}
-TEST_F(ResourceParserTest,
- ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
- std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
- ASSERT_TRUE(TestParse(input));
+TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
+ ASSERT_TRUE(TestParse(R"(<style name="foo.bar" parent=""/>)"));
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
- ASSERT_NE(nullptr, style);
- AAPT_EXPECT_FALSE(style->parent);
+ ASSERT_THAT(style, NotNull());
+ EXPECT_FALSE(style->parent);
EXPECT_FALSE(style->parent_inferred);
}
TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
- std::string input =
- R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<style name="foo" parent="*android:style/bar" />)"));
Style* style = test::GetValue<Style>(&table_, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
EXPECT_TRUE(style->parent.value().private_reference);
}
TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
- std::string input = "<string name=\"foo\">@+id/bar</string>";
- ASSERT_TRUE(TestParse(input));
-
- Id* id = test::GetValue<Id>(&table_, "id/bar");
- ASSERT_NE(id, nullptr);
+ ASSERT_TRUE(TestParse(R"(<string name="foo">@+id/bar</string>)"));
+ ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull());
}
TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
- std::string input =
- "<declare-styleable name=\"foo\">\n"
- " <attr name=\"bar\" />\n"
- " <attr name=\"bat\" format=\"string|reference\"/>\n"
- " <attr name=\"baz\">\n"
- " <enum name=\"foo\" value=\"1\"/>\n"
- " </attr>\n"
- "</declare-styleable>";
+ std::string input = R"(
+ <declare-styleable name="foo">
+ <attr name="bar" />
+ <attr name="bat" format="string|reference"/>
+ <attr name="baz">
+ <enum name="foo" value="1"/>
+ </attr>
+ </declare-styleable>)";
ASSERT_TRUE(TestParse(input));
Maybe<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("styleable/foo"));
- AAPT_ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
+ ASSERT_TRUE(result);
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
- ASSERT_NE(attr, nullptr);
+ ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
attr = test::GetValue<Attribute>(&table_, "attr/bat");
- ASSERT_NE(attr, nullptr);
+ ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
attr = test::GetValue<Attribute>(&table_, "attr/baz");
- ASSERT_NE(attr, nullptr);
+ ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
- EXPECT_EQ(1u, attr->symbols.size());
+ EXPECT_THAT(attr->symbols, SizeIs(1));
- EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo"));
+ EXPECT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull());
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
- ASSERT_NE(styleable, nullptr);
- ASSERT_EQ(3u, styleable->entries.size());
+ ASSERT_THAT(styleable, NotNull());
+ ASSERT_THAT(styleable->entries, SizeIs(3));
- EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
- styleable->entries[0].name.value());
- EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
- styleable->entries[1].name.value());
+ EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
+ EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
+ EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
}
TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
- std::string input =
- "<declare-styleable name=\"foo\" "
- "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
- " <attr name=\"*android:bar\" />\n"
- " <attr name=\"privAndroid:bat\" />\n"
- "</declare-styleable>";
+ std::string input = R"(
+ <declare-styleable xmlns:privAndroid="http://schemas.android.com/apk/prv/res/android"
+ name="foo">
+ <attr name="*android:bar" />
+ <attr name="privAndroid:bat" />
+ </declare-styleable>)";
ASSERT_TRUE(TestParse(input));
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(2u, styleable->entries.size());
+ ASSERT_THAT(styleable, NotNull());
+ ASSERT_THAT(styleable->entries, SizeIs(2));
EXPECT_TRUE(styleable->entries[0].private_reference);
- AAPT_ASSERT_TRUE(styleable->entries[0].name);
- EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
+ ASSERT_TRUE(styleable->entries[0].name);
+ EXPECT_THAT(styleable->entries[0].name.value().package, Eq("android"));
EXPECT_TRUE(styleable->entries[1].private_reference);
- AAPT_ASSERT_TRUE(styleable->entries[1].name);
- EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
+ ASSERT_TRUE(styleable->entries[1].name);
+ EXPECT_THAT(styleable->entries[1].name.value().package, Eq("android"));
}
TEST_F(ResourceParserTest, ParseArray) {
- std::string input =
- "<array name=\"foo\">\n"
- " <item>@string/ref</item>\n"
- " <item>hey</item>\n"
- " <item>23</item>\n"
- "</array>";
+ std::string input = R"(
+ <array name="foo">
+ <item>@string/ref</item>
+ <item>hey</item>
+ <item>23</item>
+ </array>)";
ASSERT_TRUE(TestParse(input));
Array* array = test::GetValue<Array>(&table_, "array/foo");
- ASSERT_NE(array, nullptr);
- ASSERT_EQ(3u, array->items.size());
+ ASSERT_THAT(array, NotNull());
+ ASSERT_THAT(array->elements, SizeIs(3));
- EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get()));
- EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get()));
- EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get()));
+ 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) {
- std::string input = R"EOF(
+ std::string input = R"(
<string-array name="foo">
<item>"Werk"</item>"
- </string-array>)EOF";
+ </string-array>)";
ASSERT_TRUE(TestParse(input));
- EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo"));
+ EXPECT_THAT(test::GetValue<Array>(&table_, "array/foo"), NotNull());
}
TEST_F(ResourceParserTest, ParseArrayWithFormat) {
- std::string input = R"EOF(
+ std::string input = R"(
<array name="foo" format="string">
<item>100</item>
- </array>)EOF";
+ </array>)";
ASSERT_TRUE(TestParse(input));
Array* array = test::GetValue<Array>(&table_, "array/foo");
- ASSERT_NE(nullptr, array);
+ ASSERT_THAT(array, NotNull());
+ ASSERT_THAT(array->elements, SizeIs(1));
- ASSERT_EQ(1u, array->items.size());
-
- String* str = ValueCast<String>(array->items[0].get());
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("100"), *str->value);
+ String* str = ValueCast<String>(array->elements[0].get());
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str, StrValueEq("100"));
}
TEST_F(ResourceParserTest, ParseArrayWithBadFormat) {
- std::string input = R"EOF(
+ std::string input = R"(
<array name="foo" format="integer">
<item>Hi</item>
- </array>)EOF";
+ </array>)";
ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParsePlural) {
- std::string input =
- "<plurals name=\"foo\">\n"
- " <item quantity=\"other\">apples</item>\n"
- " <item quantity=\"one\">apple</item>\n"
- "</plurals>";
+ std::string input = R"(
+ <plurals name="foo">
+ <item quantity="other">apples</item>
+ <item quantity="one">apple</item>
+ </plurals>)";
ASSERT_TRUE(TestParse(input));
Plural* plural = test::GetValue<Plural>(&table_, "plurals/foo");
- ASSERT_NE(nullptr, plural);
- EXPECT_EQ(nullptr, plural->values[Plural::Zero]);
- EXPECT_EQ(nullptr, plural->values[Plural::Two]);
- EXPECT_EQ(nullptr, plural->values[Plural::Few]);
- EXPECT_EQ(nullptr, plural->values[Plural::Many]);
+ ASSERT_THAT(plural, NotNull());
+ EXPECT_THAT(plural->values[Plural::Zero], IsNull());
+ EXPECT_THAT(plural->values[Plural::Two], IsNull());
+ EXPECT_THAT(plural->values[Plural::Few], IsNull());
+ EXPECT_THAT(plural->values[Plural::Many], IsNull());
- EXPECT_NE(nullptr, plural->values[Plural::One]);
- EXPECT_NE(nullptr, plural->values[Plural::Other]);
+ EXPECT_THAT(plural->values[Plural::One], NotNull());
+ EXPECT_THAT(plural->values[Plural::Other], NotNull());
}
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
- std::string input =
- "<!--This is a comment-->\n"
- "<string name=\"foo\">Hi</string>";
+ std::string input = R"(
+ <!--This is a comment-->
+ <string name="foo">Hi</string>)";
ASSERT_TRUE(TestParse(input));
String* value = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->GetComment(), "This is a comment");
+ ASSERT_THAT(value, NotNull());
+ EXPECT_THAT(value->GetComment(), Eq("This is a comment"));
}
TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
- std::string input =
- "<!--One-->\n"
- "<!--Two-->\n"
- "<string name=\"foo\">Hi</string>";
+ std::string input = R"(
+ <!--One-->
+ <!--Two-->
+ <string name="foo">Hi</string>)";
ASSERT_TRUE(TestParse(input));
String* value = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->GetComment(), "Two");
+ ASSERT_THAT(value, NotNull());
+ EXPECT_THAT(value->GetComment(), Eq("Two"));
}
TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
- std::string input =
- "<!--One-->\n"
- "<string name=\"foo\">\n"
- " Hi\n"
- "<!--Two-->\n"
- "</string>";
-
+ std::string input = R"(
+ <!--One-->
+ <string name="foo">
+ Hi
+ <!--Two-->
+ </string>)";
ASSERT_TRUE(TestParse(input));
String* value = test::GetValue<String>(&table_, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->GetComment(), "One");
+ ASSERT_THAT(value, NotNull());
+ EXPECT_THAT(value->GetComment(), Eq("One"));
}
TEST_F(ResourceParserTest, ParseNestedComments) {
// We only care about declare-styleable and enum/flag attributes because
- // comments
- // from those end up in R.java
- std::string input = R"EOF(
- <declare-styleable name="foo">
- <!-- The name of the bar -->
- <attr name="barName" format="string|reference" />
- </declare-styleable>
-
- <attr name="foo">
- <!-- The very first -->
- <enum name="one" value="1" />
- </attr>)EOF";
+ // comments from those end up in R.java
+ std::string input = R"(
+ <declare-styleable name="foo">
+ <!-- The name of the bar -->
+ <attr name="barName" format="string|reference" />
+ </declare-styleable>
+
+ <attr name="foo">
+ <!-- The very first -->
+ <enum name="one" value="1" />
+ </attr>)";
ASSERT_TRUE(TestParse(input));
Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(1u, styleable->entries.size());
-
- EXPECT_EQ(StringPiece("The name of the bar"),
- styleable->entries.front().GetComment());
+ ASSERT_THAT(styleable, NotNull());
+ ASSERT_THAT(styleable->entries, SizeIs(1));
+ EXPECT_THAT(styleable->entries[0].GetComment(), Eq("The name of the bar"));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
- ASSERT_NE(nullptr, attr);
- ASSERT_EQ(1u, attr->symbols.size());
-
- EXPECT_EQ(StringPiece("The very first"),
- attr->symbols.front().symbol.GetComment());
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_THAT(attr->symbols, SizeIs(1));
+ EXPECT_THAT(attr->symbols[0].symbol.GetComment(), Eq("The very first"));
}
-/*
- * Declaring an ID as public should not require a separate definition
- * (as an ID has no value).
- */
+// Declaring an ID as public should not require a separate definition (as an ID has no value).
TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
- std::string input = "<public type=\"id\" name=\"foo\"/>";
- ASSERT_TRUE(TestParse(input));
-
- Id* id = test::GetValue<Id>(&table_, "id/foo");
- ASSERT_NE(nullptr, id);
+ ASSERT_TRUE(TestParse(R"(<public type="id" name="foo"/>)"));
+ ASSERT_THAT(test::GetValue<Id>(&table_, "id/foo"), NotNull());
}
TEST_F(ResourceParserTest, KeepAllProducts) {
- std::string input = R"EOF(
- <string name="foo" product="phone">hi</string>
- <string name="foo" product="no-sdcard">ho</string>
- <string name="bar" product="">wee</string>
- <string name="baz">woo</string>
- <string name="bit" product="phablet">hoot</string>
- <string name="bot" product="default">yes</string>
- )EOF";
+ std::string input = R"(
+ <string name="foo" product="phone">hi</string>
+ <string name="foo" product="no-sdcard">ho</string>
+ <string name="bar" product="">wee</string>
+ <string name="baz">woo</string>
+ <string name="bit" product="phablet">hoot</string>
+ <string name="bot" product="default">yes</string>)";
ASSERT_TRUE(TestParse(input));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
- &table_, "string/foo",
- ConfigDescription::DefaultConfig(), "phone"));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
- &table_, "string/foo",
- ConfigDescription::DefaultConfig(), "no-sdcard"));
- EXPECT_NE(nullptr,
- test::GetValueForConfigAndProduct<String>(
- &table_, "string/bar", ConfigDescription::DefaultConfig(), ""));
- EXPECT_NE(nullptr,
- test::GetValueForConfigAndProduct<String>(
- &table_, "string/baz", ConfigDescription::DefaultConfig(), ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
- &table_, "string/bit",
- ConfigDescription::DefaultConfig(), "phablet"));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
- &table_, "string/bot",
- ConfigDescription::DefaultConfig(), "default"));
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo", ConfigDescription::DefaultConfig(), "phone"), NotNull());
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/foo",ConfigDescription::DefaultConfig(), "no-sdcard"), NotNull());
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bar", ConfigDescription::DefaultConfig(), ""), NotNull());
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/baz", ConfigDescription::DefaultConfig(), ""), NotNull());
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bit", ConfigDescription::DefaultConfig(), "phablet"), NotNull());
+ ASSERT_THAT(test::GetValueForConfigAndProduct<String>(&table_, "string/bot", ConfigDescription::DefaultConfig(), "default"), NotNull());
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
- std::string input = R"EOF(
- <public-group type="attr" first-id="0x01010040">
- <public name="foo" />
- <public name="bar" />
- </public-group>)EOF";
+ std::string input = R"(
+ <public-group type="attr" first-id="0x01010040">
+ <public name="foo" />
+ <public name="bar" />
+ </public-group>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
- table_.FindResource(test::ParseNameOrDie("attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ ASSERT_TRUE(result);
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
+ ASSERT_TRUE(result.value().package->id);
+ ASSERT_TRUE(result.value().type->id);
+ ASSERT_TRUE(result.value().entry->id);
ResourceId actual_id(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010040), actual_id);
+ EXPECT_THAT(actual_id, Eq(ResourceId(0x01010040)));
result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
- AAPT_ASSERT_TRUE(result);
+ ASSERT_TRUE(result);
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
+ ASSERT_TRUE(result.value().package->id);
+ ASSERT_TRUE(result.value().type->id);
+ ASSERT_TRUE(result.value().entry->id);
actual_id = ResourceId(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010041), actual_id);
+ EXPECT_THAT(actual_id, Eq(ResourceId(0x01010041)));
}
TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
- std::string input =
- R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
- ASSERT_TRUE(TestParse(input));
-
- input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
- ASSERT_FALSE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<item type="layout" name="foo">@layout/bar</item>)"));
+ ASSERT_FALSE(TestParse(R"(<item type="layout" name="bar">"this is a string"</item>)"));
}
TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
- std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)"));
Maybe<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("string/bar"));
- AAPT_ASSERT_TRUE(result);
+ ASSERT_TRUE(result);
const ResourceEntry* entry = result.value().entry;
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
+ ASSERT_THAT(entry, NotNull());
+ EXPECT_THAT(entry->symbol_status.state, Eq(SymbolState::kUndefined));
EXPECT_TRUE(entry->symbol_status.allow_new);
}
TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
- std::string input = R"(<item name="foo" type="integer" format="float">0.3</item>)";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer" format="float">0.3</item>)"));
BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_THAT(val, NotNull());
- EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FLOAT));
+ EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FLOAT));
- input = R"(<item name="bar" type="integer" format="fraction">100</item>)";
- ASSERT_FALSE(TestParse(input));
+ ASSERT_FALSE(TestParse(R"(<item name="bar" type="integer" format="fraction">100</item>)"));
}
// An <item> without a format specifier accepts all types of values.
TEST_F(ResourceParserTest, ParseItemElementWithoutFormat) {
- std::string input = R"(<item name="foo" type="integer">100%p</item>)";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<item name="foo" type="integer">100%p</item>)"));
BinaryPrimitive* val = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_THAT(val, NotNull());
- EXPECT_THAT(val->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
+ EXPECT_THAT(val->value.dataType, Eq(Res_value::TYPE_FRACTION));
}
TEST_F(ResourceParserTest, ParseConfigVaryingItem) {
- std::string input = R"EOF(<item name="foo" type="configVarying">Hey</item>)EOF";
- ASSERT_TRUE(TestParse(input));
- ASSERT_NE(nullptr, test::GetValue<String>(&table_, "configVarying/foo"));
+ ASSERT_TRUE(TestParse(R"(<item name="foo" type="configVarying">Hey</item>)"));
+ ASSERT_THAT(test::GetValue<String>(&table_, "configVarying/foo"), NotNull());
}
TEST_F(ResourceParserTest, ParseBagElement) {
- std::string input =
- R"EOF(<bag name="bag" type="configVarying"><item name="test">Hello!</item></bag>)EOF";
+ std::string input = R"(
+ <bag name="bag" type="configVarying">
+ <item name="test">Hello!</item>
+ </bag>)";
ASSERT_TRUE(TestParse(input));
Style* val = test::GetValue<Style>(&table_, "configVarying/bag");
- ASSERT_NE(nullptr, val);
+ ASSERT_THAT(val, NotNull());
+ ASSERT_THAT(val->entries, SizeIs(1));
- ASSERT_EQ(1u, val->entries.size());
- EXPECT_EQ(Reference(test::ParseNameOrDie("attr/test")), val->entries[0].key);
- EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get()));
+ EXPECT_THAT(val->entries[0].key, Eq(Reference(test::ParseNameOrDie("attr/test"))));
+ EXPECT_THAT(ValueCast<RawString>(val->entries[0].value.get()), NotNull());
}
TEST_F(ResourceParserTest, ParseElementWithNoValue) {
@@ -840,12 +783,11 @@ TEST_F(ResourceParserTest, ParseElementWithNoValue) {
String* str = test::GetValue<String>(&table_, "string/foo");
ASSERT_THAT(str, NotNull());
- EXPECT_THAT(*str->value, Eq(""));
+ EXPECT_THAT(*str, StrValueEq(""));
}
TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
- std::string input = R"(<string name="foo">%1$s %n %2$s</string>)";
- ASSERT_TRUE(TestParse(input));
+ ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 168004f0b721..ab59560d33a3 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -15,20 +15,24 @@
*/
#include "ResourceTable.h"
-#include "ConfigDescription.h"
-#include "NameMangler.h"
-#include "ResourceValues.h"
-#include "ValueVisitor.h"
-#include "util/Util.h"
-#include <android-base/logging.h>
-#include <androidfw/ResourceTypes.h>
#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
-using android::StringPiece;
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "ConfigDescription.h"
+#include "NameMangler.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "text/Unicode.h"
+#include "util/Util.h"
+
+using ::aapt::text::IsValidResourceEntryName;
+using ::android::StringPiece;
namespace aapt {
@@ -283,12 +287,9 @@ ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* exist
return CollisionResult::kConflict;
}
-static constexpr const char* kValidNameChars = "._-";
-
static StringPiece ValidateName(const StringPiece& name) {
- auto iter = util::FindNonAlphaNumericAndNotInSet(name, kValidNameChars);
- if (iter != name.end()) {
- return StringPiece(iter, 1);
+ if (!IsValidResourceEntryName(name)) {
+ return name;
}
return {};
}
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index e2b37be994ff..2a3c131f7b4b 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -24,6 +24,8 @@
#include <ostream>
#include <string>
+using ::testing::NotNull;
+
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
@@ -56,7 +58,7 @@ TEST(ResourceTableTest, AddOneResource) {
test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(),
test::GetDiagnostics()));
- ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
+ EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
}
TEST(ResourceTableTest, AddMultipleResources) {
@@ -88,11 +90,10 @@ TEST(ResourceTableTest, AddMultipleResources) {
.Build(),
test::GetDiagnostics()));
- ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/layout_width"));
- ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
- ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:string/ok"));
- ASSERT_NE(nullptr, test::GetValueForConfig<BinaryPrimitive>(
- &table, "android:string/ok", language_config));
+ EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/layout_width"), NotNull());
+ EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
+ EXPECT_THAT(test::GetValue<Id>(&table, "android:string/ok"), NotNull());
+ EXPECT_THAT(test::GetValueForConfig<BinaryPrimitive>(&table, "android:string/ok", language_config), NotNull());
}
TEST(ResourceTableTest, OverrideWeakResourceValue) {
@@ -103,7 +104,7 @@ TEST(ResourceTableTest, OverrideWeakResourceValue) {
util::make_unique<Attribute>(true), test::GetDiagnostics()));
Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
+ ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
ASSERT_TRUE(table.AddResource(
@@ -111,7 +112,7 @@ TEST(ResourceTableTest, OverrideWeakResourceValue) {
util::make_unique<Attribute>(false), test::GetDiagnostics()));
attr = test::GetValue<Attribute>(&table, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
+ ASSERT_THAT(attr, NotNull());
EXPECT_FALSE(attr->IsWeak());
}
@@ -127,16 +128,12 @@ TEST(ResourceTableTest, ProductVaryingValues) {
util::make_unique<Id>(),
test::GetDiagnostics()));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/foo",
- test::ParseConfigOrDie("land"), "tablet"));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/foo",
- test::ParseConfigOrDie("land"), "phone"));
+ EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
+ EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
Maybe<ResourceTable::SearchResult> sr =
table.FindResource(test::ParseNameOrDie("android:string/foo"));
- AAPT_ASSERT_TRUE(sr);
+ ASSERT_TRUE(sr);
std::vector<ResourceConfigValue*> values =
sr.value().entry->FindAllValues(test::ParseConfigOrDie("land"));
ASSERT_EQ(2u, values.size());
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index deeef6ebbcb7..f193fe0c6593 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -512,7 +512,7 @@ std::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
}
std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
- std::u16string str16 = util::Utf8ToUtf16(str);
+ std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
android::Res_value value;
if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
return {};
@@ -521,7 +521,7 @@ std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
}
std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
- std::u16string str16 = util::Utf8ToUtf16(str);
+ std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
android::Res_value value;
if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
return {};
@@ -700,7 +700,7 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
spans++;
}
return util::make_unique<StyledString>(dst_pool->MakeRef(
- style_str, StringPool::Context(StringPool::Context::kStylePriority, config)));
+ style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
} else {
if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
// This must be a FileReference.
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index cdc34f1ec752..e637c3ee2f3c 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -20,96 +20,90 @@
#include "test/Test.h"
using ::aapt::test::ValueEq;
+using ::android::Res_value;
+using ::android::ResTable_map;
+using ::testing::Eq;
+using ::testing::NotNull;
using ::testing::Pointee;
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("true"));
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("TRUE"));
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::ParseBool("True"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("false"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("FALSE"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::ParseBool("False"));
+ EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(Maybe<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(Maybe<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(Maybe<bool>(true)));
+
+ EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(Maybe<bool>(false)));
+
+ EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(Maybe<bool>(false)));
}
TEST(ResourceUtilsTest, ParseResourceName) {
ResourceNameRef actual;
bool actual_priv = false;
- EXPECT_TRUE(ResourceUtils::ParseResourceName("android:color/foo", &actual,
- &actual_priv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(ResourceUtils::ParseResourceName("android:color/foo", &actual, &actual_priv));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kColor, "foo")));
EXPECT_FALSE(actual_priv);
- EXPECT_TRUE(
- ResourceUtils::ParseResourceName("color/foo", &actual, &actual_priv));
- EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(ResourceUtils::ParseResourceName("color/foo", &actual, &actual_priv));
+ EXPECT_THAT(actual, Eq(ResourceNameRef({}, ResourceType::kColor, "foo")));
EXPECT_FALSE(actual_priv);
- EXPECT_TRUE(ResourceUtils::ParseResourceName("*android:color/foo", &actual,
- &actual_priv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(ResourceUtils::ParseResourceName("*android:color/foo", &actual, &actual_priv));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kColor, "foo")));
EXPECT_TRUE(actual_priv);
EXPECT_FALSE(ResourceUtils::ParseResourceName(android::StringPiece(), &actual, &actual_priv));
}
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
- ResourceNameRef expected({}, ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool private_ref = false;
- EXPECT_TRUE(ResourceUtils::ParseReference("@color/foo", &actual, &create,
- &private_ref));
- EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(ResourceUtils::ParseReference("@color/foo", &actual, &create, &private_ref));
+ EXPECT_THAT(actual, Eq(ResourceNameRef({}, ResourceType::kColor, "foo")));
EXPECT_FALSE(create);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool private_ref = false;
- EXPECT_TRUE(ResourceUtils::ParseReference("@android:color/foo", &actual,
- &create, &private_ref));
- EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(ResourceUtils::ParseReference("@android:color/foo", &actual, &create, &private_ref));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kColor, "foo")));
EXPECT_FALSE(create);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
ResourceNameRef actual;
bool create = false;
bool private_ref = false;
- EXPECT_TRUE(ResourceUtils::ParseReference("\t @android:color/foo\n \n\t",
- &actual, &create, &private_ref));
- EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(ResourceUtils::ParseReference("\t @android:color/foo\n \n\t", &actual, &create, &private_ref));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kColor, "foo")));
EXPECT_FALSE(create);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
ResourceNameRef actual;
bool create = false;
bool private_ref = false;
- EXPECT_TRUE(ResourceUtils::ParseReference("@+android:id/foo", &actual,
- &create, &private_ref));
- EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(ResourceUtils::ParseReference("@+android:id/foo", &actual, &create, &private_ref));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kId, "foo")));
EXPECT_TRUE(create);
EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParsePrivateReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
ResourceNameRef actual;
bool create = false;
bool private_ref = false;
- EXPECT_TRUE(ResourceUtils::ParseReference("@*android:id/foo", &actual,
- &create, &private_ref));
- EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(ResourceUtils::ParseReference("@*android:id/foo", &actual, &create, &private_ref));
+ EXPECT_THAT(actual, Eq(ResourceNameRef("android", ResourceType::kId, "foo")));
EXPECT_FALSE(create);
EXPECT_TRUE(private_ref);
}
@@ -118,8 +112,7 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
bool create = false;
bool private_ref = false;
ResourceNameRef actual;
- EXPECT_FALSE(ResourceUtils::ParseReference("@+android:color/foo", &actual,
- &create, &private_ref));
+ EXPECT_FALSE(ResourceUtils::ParseReference("@+android:color/foo", &actual, &create, &private_ref));
}
TEST(ResourceUtilsTest, ParseAttributeReferences) {
@@ -143,82 +136,81 @@ TEST(ResourceUtilsTest, FailParseIncompleteReference) {
}
TEST(ResourceUtilsTest, ParseStyleParentReference) {
- const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle,
- "foo");
+ const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle, "foo");
const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
std::string err_str;
- Maybe<Reference> ref =
- ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ Maybe<Reference> ref = ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
- ref =
- ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
- ref =
- ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
+ ASSERT_TRUE(ref);
+ EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
EXPECT_TRUE(ref.value().private_reference);
}
TEST(ResourceUtilsTest, ParseEmptyFlag) {
std::unique_ptr<Attribute> attr =
test::AttributeBuilder(false)
- .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
+ .SetTypeMask(ResTable_map::TYPE_FLAGS)
.AddItem("one", 0x01)
.AddItem("two", 0x02)
.Build();
- std::unique_ptr<BinaryPrimitive> result =
- ResourceUtils::TryParseFlagSymbol(attr.get(), "");
- ASSERT_NE(nullptr, result);
- EXPECT_EQ(0u, result->value.data);
+ std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseFlagSymbol(attr.get(), "");
+ ASSERT_THAT(result, NotNull());
+ EXPECT_THAT(result->value.data, Eq(0u));
}
TEST(ResourceUtilsTest, NullIsEmptyReference) {
- auto null_value = ResourceUtils::MakeNull();
- ASSERT_THAT(null_value, Pointee(ValueEq(Reference())));
-
- auto value = ResourceUtils::TryParseNullOrEmpty("@null");
- ASSERT_THAT(value, Pointee(ValueEq(Reference())));
+ ASSERT_THAT(ResourceUtils::MakeNull(), Pointee(ValueEq(Reference())));
+ ASSERT_THAT(ResourceUtils::TryParseNullOrEmpty("@null"), Pointee(ValueEq(Reference())));
}
TEST(ResourceUtilsTest, EmptyIsBinaryPrimitive) {
- auto empty_value = ResourceUtils::MakeEmpty();
- ASSERT_THAT(empty_value, Pointee(ValueEq(BinaryPrimitive(android::Res_value::TYPE_NULL,
- android::Res_value::DATA_NULL_EMPTY))));
+ ASSERT_THAT(ResourceUtils::MakeEmpty(), Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_NULL, Res_value::DATA_NULL_EMPTY))));
+ ASSERT_THAT(ResourceUtils::TryParseNullOrEmpty("@empty"), Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_NULL, Res_value::DATA_NULL_EMPTY))));
+}
+
+TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) {
+ EXPECT_THAT(ResourceUtils::TryParseItemForAttribute(" 12\n ", ResTable_map::TYPE_INTEGER),
+ Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_INT_DEC, 12u))));
+ EXPECT_THAT(ResourceUtils::TryParseItemForAttribute(" true\n ", ResTable_map::TYPE_BOOLEAN),
+ Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_INT_BOOLEAN, 0xffffffffu))));
- auto value = ResourceUtils::TryParseNullOrEmpty("@empty");
- ASSERT_THAT(value, Pointee(ValueEq(BinaryPrimitive(android::Res_value::TYPE_NULL,
- android::Res_value::DATA_NULL_EMPTY))));
+ const float expected_float = 12.0f;
+ const uint32_t expected_float_flattened = *(uint32_t*)&expected_float;
+ EXPECT_THAT(ResourceUtils::TryParseItemForAttribute(" 12.0\n ", ResTable_map::TYPE_FLOAT),
+ Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index e808984c75f5..1cba19462839 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -253,10 +253,9 @@ StyledString* StyledString::Clone(StringPool* new_pool) const {
}
void StyledString::Print(std::ostream* out) const {
- *out << "(styled string) \"" << *value->str << "\"";
+ *out << "(styled string) \"" << value->value << "\"";
for (const StringPool::Span& span : value->spans) {
- *out << " " << *span.name << ":" << span.first_char << ","
- << span.last_char;
+ *out << " " << *span.name << ":" << span.first_char << "," << span.last_char;
}
}
@@ -533,75 +532,119 @@ void Attribute::Print(std::ostream* out) const {
}
}
-static void BuildAttributeMismatchMessage(DiagMessage* msg,
- const Attribute* attr,
- const Item* value) {
- *msg << "expected";
- if (attr->type_mask & android::ResTable_map::TYPE_BOOLEAN) {
- *msg << " boolean";
+static void BuildAttributeMismatchMessage(const Attribute& attr, const Item& value,
+ DiagMessage* out_msg) {
+ *out_msg << "expected";
+ if (attr.type_mask & android::ResTable_map::TYPE_BOOLEAN) {
+ *out_msg << " boolean";
}
- if (attr->type_mask & android::ResTable_map::TYPE_COLOR) {
- *msg << " color";
+ if (attr.type_mask & android::ResTable_map::TYPE_COLOR) {
+ *out_msg << " color";
}
- if (attr->type_mask & android::ResTable_map::TYPE_DIMENSION) {
- *msg << " dimension";
+ if (attr.type_mask & android::ResTable_map::TYPE_DIMENSION) {
+ *out_msg << " dimension";
}
- if (attr->type_mask & android::ResTable_map::TYPE_ENUM) {
- *msg << " enum";
+ if (attr.type_mask & android::ResTable_map::TYPE_ENUM) {
+ *out_msg << " enum";
}
- if (attr->type_mask & android::ResTable_map::TYPE_FLAGS) {
- *msg << " flags";
+ if (attr.type_mask & android::ResTable_map::TYPE_FLAGS) {
+ *out_msg << " flags";
}
- if (attr->type_mask & android::ResTable_map::TYPE_FLOAT) {
- *msg << " float";
+ if (attr.type_mask & android::ResTable_map::TYPE_FLOAT) {
+ *out_msg << " float";
}
- if (attr->type_mask & android::ResTable_map::TYPE_FRACTION) {
- *msg << " fraction";
+ if (attr.type_mask & android::ResTable_map::TYPE_FRACTION) {
+ *out_msg << " fraction";
}
- if (attr->type_mask & android::ResTable_map::TYPE_INTEGER) {
- *msg << " integer";
+ if (attr.type_mask & android::ResTable_map::TYPE_INTEGER) {
+ *out_msg << " integer";
}
- if (attr->type_mask & android::ResTable_map::TYPE_REFERENCE) {
- *msg << " reference";
+ if (attr.type_mask & android::ResTable_map::TYPE_REFERENCE) {
+ *out_msg << " reference";
}
- if (attr->type_mask & android::ResTable_map::TYPE_STRING) {
- *msg << " string";
+ if (attr.type_mask & android::ResTable_map::TYPE_STRING) {
+ *out_msg << " string";
}
- *msg << " but got " << *value;
+ *out_msg << " but got " << value;
}
-bool Attribute::Matches(const Item* item, DiagMessage* out_msg) const {
+bool Attribute::Matches(const Item& item, DiagMessage* out_msg) const {
+ constexpr const uint32_t TYPE_ENUM = android::ResTable_map::TYPE_ENUM;
+ constexpr const uint32_t TYPE_FLAGS = android::ResTable_map::TYPE_FLAGS;
+ constexpr const uint32_t TYPE_INTEGER = android::ResTable_map::TYPE_INTEGER;
+ constexpr const uint32_t TYPE_REFERENCE = android::ResTable_map::TYPE_REFERENCE;
+
android::Res_value val = {};
- item->Flatten(&val);
+ item.Flatten(&val);
+
+ const uint32_t flattened_data = util::DeviceToHost32(val.data);
// Always allow references.
- const uint32_t mask = type_mask | android::ResTable_map::TYPE_REFERENCE;
- if (!(mask & ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType))) {
+ const uint32_t actual_type = ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType);
+
+ // Only one type must match between the actual and expected.
+ if ((actual_type & (type_mask | TYPE_REFERENCE)) == 0) {
if (out_msg) {
- BuildAttributeMismatchMessage(out_msg, this, item);
+ BuildAttributeMismatchMessage(*this, item, out_msg);
}
return false;
+ }
+
+ // Enums and flags are encoded as integers, so check them first before doing any range checks.
+ if ((type_mask & TYPE_ENUM) != 0 && (actual_type & TYPE_ENUM) != 0) {
+ for (const Symbol& s : symbols) {
+ if (flattened_data == s.value) {
+ return true;
+ }
+ }
+
+ // If the attribute accepts integers, we can't fail here.
+ if ((type_mask & TYPE_INTEGER) == 0) {
+ if (out_msg) {
+ *out_msg << item << " is not a valid enum";
+ }
+ return false;
+ }
+ }
+
+ if ((type_mask & TYPE_FLAGS) != 0 && (actual_type & TYPE_FLAGS) != 0) {
+ uint32_t mask = 0u;
+ for (const Symbol& s : symbols) {
+ mask |= s.value;
+ }
+
+ // Check if the flattened data is covered by the flag bit mask.
+ // If the attribute accepts integers, we can't fail here.
+ if ((mask & flattened_data) == flattened_data) {
+ return true;
+ } else if ((type_mask & TYPE_INTEGER) == 0) {
+ if (out_msg) {
+ *out_msg << item << " is not a valid flag";
+ }
+ return false;
+ }
+ }
- } else if (ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType) &
- android::ResTable_map::TYPE_INTEGER) {
- if (static_cast<int32_t>(util::DeviceToHost32(val.data)) < min_int) {
+ // Finally check the integer range of the value.
+ if ((type_mask & TYPE_INTEGER) != 0 && (actual_type & TYPE_INTEGER) != 0) {
+ if (static_cast<int32_t>(flattened_data) < min_int) {
if (out_msg) {
- *out_msg << *item << " is less than minimum integer " << min_int;
+ *out_msg << item << " is less than minimum integer " << min_int;
}
return false;
- } else if (static_cast<int32_t>(util::DeviceToHost32(val.data)) > max_int) {
+ } else if (static_cast<int32_t>(flattened_data) > max_int) {
if (out_msg) {
- *out_msg << *item << " is greater than maximum integer " << max_int;
+ *out_msg << item << " is greater than maximum integer " << max_int;
}
return false;
}
@@ -762,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());
});
}
@@ -777,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 ac5795fb9774..275864bbcd3e 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -264,7 +264,7 @@ struct Attribute : public BaseValue<Attribute> {
Attribute* Clone(StringPool* new_pool) const override;
void PrintMask(std::ostream* out) const;
void Print(std::ostream* out) const override;
- bool Matches(const Item* item, DiagMessage* out_msg) const;
+ bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
};
struct Style : public BaseValue<Style> {
@@ -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 69f8e67530f6..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()));
@@ -190,4 +190,52 @@ TEST(ResourcesValuesTest, EmptyReferenceFlattens) {
EXPECT_EQ(0x0u, value.data);
}
+TEST(ResourcesValuesTest, AttributeMatches) {
+ constexpr const uint32_t TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
+ constexpr const uint32_t TYPE_ENUM = android::ResTable_map::TYPE_ENUM;
+ constexpr const uint32_t TYPE_FLAGS = android::ResTable_map::TYPE_FLAGS;
+ constexpr const uint32_t TYPE_INTEGER = android::ResTable_map::TYPE_INTEGER;
+ constexpr const uint8_t TYPE_INT_DEC = android::Res_value::TYPE_INT_DEC;
+
+ Attribute attr1(false /*weak*/, TYPE_DIMENSION);
+ EXPECT_FALSE(attr1.Matches(*ResourceUtils::TryParseColor("#7fff00")));
+ EXPECT_TRUE(attr1.Matches(*ResourceUtils::TryParseFloat("23dp")));
+ EXPECT_TRUE(attr1.Matches(*ResourceUtils::TryParseReference("@android:string/foo")));
+
+ Attribute attr2(false /*weak*/, TYPE_INTEGER | TYPE_ENUM);
+ attr2.min_int = 0;
+ attr2.symbols.push_back(Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")),
+ static_cast<uint32_t>(-1)});
+ EXPECT_FALSE(attr2.Matches(*ResourceUtils::TryParseColor("#7fff00")));
+ EXPECT_TRUE(attr2.Matches(BinaryPrimitive(TYPE_INT_DEC, static_cast<uint32_t>(-1))));
+ EXPECT_TRUE(attr2.Matches(BinaryPrimitive(TYPE_INT_DEC, 1u)));
+ EXPECT_FALSE(attr2.Matches(BinaryPrimitive(TYPE_INT_DEC, static_cast<uint32_t>(-2))));
+
+ Attribute attr3(false /*weak*/, TYPE_INTEGER | TYPE_FLAGS);
+ attr3.max_int = 100;
+ attr3.symbols.push_back(
+ Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
+ attr3.symbols.push_back(
+ Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x02u});
+ attr3.symbols.push_back(
+ Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/baz")), 0x04u});
+ attr3.symbols.push_back(
+ Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bat")), 0x80u});
+ EXPECT_FALSE(attr3.Matches(*ResourceUtils::TryParseColor("#7fff00")));
+ EXPECT_TRUE(attr3.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x01u | 0x02u)));
+ EXPECT_TRUE(attr3.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x01u | 0x02u | 0x80u)));
+
+ // Not a flag, but a value less than max_int.
+ EXPECT_TRUE(attr3.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x08u)));
+
+ // Not a flag and greater than max_int.
+ EXPECT_FALSE(attr3.Matches(BinaryPrimitive(TYPE_INT_DEC, 127u)));
+
+ Attribute attr4(false /*weak*/, TYPE_ENUM);
+ attr4.symbols.push_back(
+ Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
+ EXPECT_TRUE(attr4.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x01u)));
+ EXPECT_FALSE(attr4.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x02u)));
+}
+
} // namespace aapt
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/SdkConstants.h b/tools/aapt2/SdkConstants.h
index e3745e8ce0da..5c32ed4fd849 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -52,6 +52,7 @@ enum : ApiVersion {
SDK_NOUGAT = 24,
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
+ SDK_O_MR1 = 27,
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 57da5f01dcd1..705b1ab052af 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -27,7 +27,7 @@
#include "util/BigBuffer.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -75,9 +75,14 @@ const std::string* StringPool::Ref::operator->() const {
return &entry_->value;
}
-const std::string& StringPool::Ref::operator*() const { return entry_->value; }
+const std::string& StringPool::Ref::operator*() const {
+ return entry_->value;
+}
-size_t StringPool::Ref::index() const { return entry_->index; }
+size_t StringPool::Ref::index() const {
+ // Account for the styles, which *always* come first.
+ return entry_->pool_->styles_.size() + entry_->index_;
+}
const StringPool::Context& StringPool::Ref::GetContext() const {
return entry_->context;
@@ -104,8 +109,7 @@ StringPool::StyleRef::~StyleRef() {
}
}
-StringPool::StyleRef& StringPool::StyleRef::operator=(
- const StringPool::StyleRef& rhs) {
+StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
if (rhs.entry_ != nullptr) {
rhs.entry_->ref_++;
}
@@ -118,7 +122,7 @@ StringPool::StyleRef& StringPool::StyleRef::operator=(
}
bool StringPool::StyleRef::operator==(const StyleRef& rhs) const {
- if (entry_->str != rhs.entry_->str) {
+ if (entry_->value != rhs.entry_->value) {
return false;
}
@@ -137,7 +141,9 @@ bool StringPool::StyleRef::operator==(const StyleRef& rhs) const {
return true;
}
-bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const { return !operator==(rhs); }
+bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const {
+ return !operator==(rhs);
+}
const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
return entry_;
@@ -147,23 +153,24 @@ const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
return *entry_;
}
-size_t StringPool::StyleRef::index() const { return entry_->str.index(); }
+size_t StringPool::StyleRef::index() const {
+ return entry_->index_;
+}
const StringPool::Context& StringPool::StyleRef::GetContext() const {
- return entry_->str.GetContext();
+ return entry_->context;
}
StringPool::Ref StringPool::MakeRef(const StringPiece& str) {
return MakeRefImpl(str, Context{}, true);
}
-StringPool::Ref StringPool::MakeRef(const StringPiece& str,
- const Context& context) {
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
return MakeRefImpl(str, context, true);
}
-StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str,
- const Context& context, bool unique) {
+StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
+ bool unique) {
if (unique) {
auto iter = indexed_strings_.find(str);
if (iter != std::end(indexed_strings_)) {
@@ -171,82 +178,87 @@ StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str,
}
}
- Entry* entry = new Entry();
+ std::unique_ptr<Entry> entry(new Entry());
entry->value = str.to_string();
entry->context = context;
- entry->index = strings_.size();
+ entry->index_ = strings_.size();
entry->ref_ = 0;
- strings_.emplace_back(entry);
- indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
- return Ref(entry);
+ entry->pool_ = this;
+
+ Entry* borrow = entry.get();
+ strings_.emplace_back(std::move(entry));
+ indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
+ return Ref(borrow);
}
StringPool::StyleRef StringPool::MakeRef(const StyleString& str) {
return MakeRef(str, Context{});
}
-StringPool::StyleRef StringPool::MakeRef(const StyleString& str,
- const Context& context) {
- Entry* entry = new Entry();
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str, const Context& context) {
+ std::unique_ptr<StyleEntry> entry(new StyleEntry());
entry->value = str.str;
entry->context = context;
- entry->index = strings_.size();
+ entry->index_ = styles_.size();
entry->ref_ = 0;
- strings_.emplace_back(entry);
- indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
-
- StyleEntry* style_entry = new StyleEntry();
- style_entry->str = Ref(entry);
for (const aapt::Span& span : str.spans) {
- style_entry->spans.emplace_back(
- Span{MakeRef(span.name), span.first_char, span.last_char});
+ entry->spans.emplace_back(Span{MakeRef(span.name), span.first_char, span.last_char});
}
- style_entry->ref_ = 0;
- styles_.emplace_back(style_entry);
- return StyleRef(style_entry);
+
+ StyleEntry* borrow = entry.get();
+ styles_.emplace_back(std::move(entry));
+ return StyleRef(borrow);
}
StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) {
- Entry* entry = new Entry();
- entry->value = *ref.entry_->str;
- entry->context = ref.entry_->str.entry_->context;
- entry->index = strings_.size();
+ std::unique_ptr<StyleEntry> entry(new StyleEntry());
+ entry->value = ref.entry_->value;
+ entry->context = ref.entry_->context;
+ entry->index_ = styles_.size();
entry->ref_ = 0;
- strings_.emplace_back(entry);
- indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
-
- StyleEntry* style_entry = new StyleEntry();
- style_entry->str = Ref(entry);
for (const Span& span : ref.entry_->spans) {
- style_entry->spans.emplace_back(
- Span{MakeRef(*span.name), span.first_char, span.last_char});
+ entry->spans.emplace_back(Span{MakeRef(*span.name), span.first_char, span.last_char});
+ }
+
+ StyleEntry* borrow = entry.get();
+ styles_.emplace_back(std::move(entry));
+ return StyleRef(borrow);
+}
+
+void StringPool::ReAssignIndices() {
+ // Assign the style indices.
+ const size_t style_len = styles_.size();
+ for (size_t index = 0; index < style_len; index++) {
+ styles_[index]->index_ = index;
+ }
+
+ // Assign the string indices.
+ const size_t string_len = strings_.size();
+ for (size_t index = 0; index < string_len; index++) {
+ strings_[index]->index_ = index;
}
- style_entry->ref_ = 0;
- styles_.emplace_back(style_entry);
- return StyleRef(style_entry);
}
void StringPool::Merge(StringPool&& pool) {
- indexed_strings_.insert(pool.indexed_strings_.begin(),
- pool.indexed_strings_.end());
- pool.indexed_strings_.clear();
- std::move(pool.strings_.begin(), pool.strings_.end(),
- std::back_inserter(strings_));
- pool.strings_.clear();
- std::move(pool.styles_.begin(), pool.styles_.end(),
- std::back_inserter(styles_));
+ // First, change the owning pool for the incoming strings.
+ for (std::unique_ptr<Entry>& entry : pool.strings_) {
+ entry->pool_ = this;
+ }
+
+ // Now move the styles, strings, and indices over.
+ std::move(pool.styles_.begin(), pool.styles_.end(), std::back_inserter(styles_));
pool.styles_.clear();
+ std::move(pool.strings_.begin(), pool.strings_.end(), std::back_inserter(strings_));
+ pool.strings_.clear();
+ indexed_strings_.insert(pool.indexed_strings_.begin(), pool.indexed_strings_.end());
+ pool.indexed_strings_.clear();
- // Assign the indices.
- const size_t len = strings_.size();
- for (size_t index = 0; index < len; index++) {
- strings_[index]->index = index;
- }
+ ReAssignIndices();
}
-void StringPool::HintWillAdd(size_t stringCount, size_t styleCount) {
- strings_.reserve(strings_.size() + stringCount);
- styles_.reserve(styles_.size() + styleCount);
+void StringPool::HintWillAdd(size_t string_count, size_t style_count) {
+ strings_.reserve(strings_.size() + string_count);
+ styles_.reserve(styles_.size() + style_count);
}
void StringPool::Prune() {
@@ -262,47 +274,42 @@ void StringPool::Prune() {
auto end_iter2 =
std::remove_if(strings_.begin(), strings_.end(),
- [](const std::unique_ptr<Entry>& entry) -> bool {
- return entry->ref_ <= 0;
- });
-
- auto end_iter3 =
- std::remove_if(styles_.begin(), styles_.end(),
- [](const std::unique_ptr<StyleEntry>& entry) -> bool {
- return entry->ref_ <= 0;
- });
-
- // Remove the entries at the end or else we'll be accessing
- // a deleted string from the StyleEntry.
+ [](const std::unique_ptr<Entry>& entry) -> bool { return entry->ref_ <= 0; });
+ auto end_iter3 = std::remove_if(
+ styles_.begin(), styles_.end(),
+ [](const std::unique_ptr<StyleEntry>& entry) -> bool { return entry->ref_ <= 0; });
+
+ // Remove the entries at the end or else we'll be accessing a deleted string from the StyleEntry.
strings_.erase(end_iter2, strings_.end());
styles_.erase(end_iter3, styles_.end());
- // Reassign the indices.
- const size_t len = strings_.size();
- for (size_t index = 0; index < len; index++) {
- strings_[index]->index = index;
- }
+ ReAssignIndices();
}
-void StringPool::Sort(
- const std::function<bool(const Entry&, const Entry&)>& cmp) {
- std::sort(
- strings_.begin(), strings_.end(),
- [&cmp](const std::unique_ptr<Entry>& a,
- const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); });
-
- // Assign the indices.
- const size_t len = strings_.size();
- for (size_t index = 0; index < len; index++) {
- strings_[index]->index = index;
+template <typename E>
+static void SortEntries(
+ std::vector<std::unique_ptr<E>>& entries,
+ const std::function<int(const StringPool::Context&, const StringPool::Context&)>& cmp) {
+ using UEntry = std::unique_ptr<E>;
+
+ if (cmp != nullptr) {
+ std::sort(entries.begin(), entries.end(), [&cmp](const UEntry& a, const UEntry& b) -> bool {
+ int r = cmp(a->context, b->context);
+ if (r == 0) {
+ r = a->value.compare(b->value);
+ }
+ return r < 0;
+ });
+ } else {
+ std::sort(entries.begin(), entries.end(),
+ [](const UEntry& a, const UEntry& b) -> bool { return a->value < b->value; });
}
+}
- // Reorder the styles.
- std::sort(styles_.begin(), styles_.end(),
- [](const std::unique_ptr<StyleEntry>& lhs,
- const std::unique_ptr<StyleEntry>& rhs) -> bool {
- return lhs->str.index() < rhs->str.index();
- });
+void StringPool::Sort(const std::function<int(const Context&, const Context&)>& cmp) {
+ SortEntries(styles_, cmp);
+ SortEntries(strings_, cmp);
+ ReAssignIndices();
}
template <typename T>
@@ -327,60 +334,31 @@ static size_t EncodedLengthUnits(size_t length) {
return length > kMaxSize ? 2 : 1;
}
-bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
- const size_t start_index = out->size();
- android::ResStringPool_header* header =
- out->NextBlock<android::ResStringPool_header>();
- header->header.type = android::RES_STRING_POOL_TYPE;
- header->header.headerSize = sizeof(*header);
- header->stringCount = pool.size();
+static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out) {
if (utf8) {
- header->flags |= android::ResStringPool_header::UTF8_FLAG;
- }
-
- uint32_t* indices =
- pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
-
- uint32_t* style_indices = nullptr;
- if (!pool.styles_.empty()) {
- header->styleCount = pool.styles_.back()->str.index() + 1;
- style_indices = out->NextBlock<uint32_t>(header->styleCount);
- }
-
- const size_t before_strings_index = out->size();
- header->stringsStart = before_strings_index - start_index;
-
- for (const auto& entry : pool) {
- *indices = out->size() - before_strings_index;
- indices++;
+ const std::string& encoded = str;
+ const ssize_t utf16_length =
+ utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size());
+ CHECK(utf16_length >= 0);
- if (utf8) {
- const std::string& encoded = entry->value;
- const ssize_t utf16_length = utf8_to_utf16_length(
- reinterpret_cast<const uint8_t*>(entry->value.data()),
- entry->value.size());
- CHECK(utf16_length >= 0);
+ const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
+ EncodedLengthUnits<char>(encoded.length()) + encoded.size() + 1;
- const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
- EncodedLengthUnits<char>(encoded.length()) +
- encoded.size() + 1;
+ char* data = out->NextBlock<char>(total_size);
- char* data = out->NextBlock<char>(total_size);
-
- // First encode the UTF16 string length.
- data = EncodeLength(data, utf16_length);
+ // First encode the UTF16 string length.
+ data = EncodeLength(data, utf16_length);
- // Now encode the size of the real UTF8 string.
- data = EncodeLength(data, encoded.length());
- strncpy(data, encoded.data(), encoded.size());
+ // Now encode the size of the real UTF8 string.
+ data = EncodeLength(data, encoded.length());
+ strncpy(data, encoded.data(), encoded.size());
} else {
- const std::u16string encoded = util::Utf8ToUtf16(entry->value);
+ const std::u16string encoded = util::Utf8ToUtf16(str);
const ssize_t utf16_length = encoded.size();
// Total number of 16-bit words to write.
- const size_t total_size =
- EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
+ const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
char16_t* data = out->NextBlock<char16_t>(total_size);
@@ -395,31 +373,55 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
// The null-terminating character is already here due to the block of data
// being set to 0s on allocation.
}
+}
+
+bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
+ const size_t start_index = out->size();
+ android::ResStringPool_header* header = out->NextBlock<android::ResStringPool_header>();
+ header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE);
+ header->header.headerSize = util::HostToDevice16(sizeof(*header));
+ header->stringCount = util::HostToDevice32(pool.size());
+ header->styleCount = util::HostToDevice32(pool.styles_.size());
+ if (utf8) {
+ header->flags |= android::ResStringPool_header::UTF8_FLAG;
}
- out->Align4();
+ uint32_t* indices = pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+ uint32_t* style_indices =
+ pool.styles_.size() != 0 ? out->NextBlock<uint32_t>(pool.styles_.size()) : nullptr;
- if (!pool.styles_.empty()) {
- const size_t before_styles_index = out->size();
- header->stylesStart = before_styles_index - start_index;
+ const size_t before_strings_index = out->size();
+ header->stringsStart = before_strings_index - start_index;
- size_t current_index = 0;
- for (const auto& entry : pool.styles_) {
- while (entry->str.index() > current_index) {
- style_indices[current_index++] = out->size() - before_styles_index;
+ // Styles always come first.
+ for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+ *indices++ = out->size() - before_strings_index;
+ EncodeString(entry->value, utf8, out);
+ }
- uint32_t* span_offset = out->NextBlock<uint32_t>();
- *span_offset = android::ResStringPool_span::END;
- }
- style_indices[current_index++] = out->size() - before_styles_index;
-
- android::ResStringPool_span* span =
- out->NextBlock<android::ResStringPool_span>(entry->spans.size());
- for (const auto& s : entry->spans) {
- span->name.index = s.name.index();
- span->firstChar = s.first_char;
- span->lastChar = s.last_char;
- span++;
+ for (const std::unique_ptr<Entry>& entry : pool.strings_) {
+ *indices++ = out->size() - before_strings_index;
+ EncodeString(entry->value, utf8, out);
+ }
+
+ out->Align4();
+
+ if (style_indices != nullptr) {
+ const size_t before_styles_index = out->size();
+ header->stylesStart = util::HostToDevice32(before_styles_index - start_index);
+
+ for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+ *style_indices++ = out->size() - before_styles_index;
+
+ if (!entry->spans.empty()) {
+ android::ResStringPool_span* span =
+ out->NextBlock<android::ResStringPool_span>(entry->spans.size());
+ for (const Span& s : entry->spans) {
+ span->name.index = util::HostToDevice32(s.name.index());
+ span->firstChar = util::HostToDevice32(s.first_char);
+ span->lastChar = util::HostToDevice32(s.last_char);
+ span++;
+ }
}
uint32_t* spanEnd = out->NextBlock<uint32_t>();
@@ -436,7 +438,7 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
memset(padding, 0xff, padding_length);
out->Align4();
}
- header->header.size = out->size() - start_index;
+ header->header.size = util::HostToDevice32(out->size() - start_index);
return true;
}
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index d1232a29b5aa..8350d0d09108 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -42,12 +42,16 @@ struct StyleString {
std::vector<Span> spans;
};
+// A StringPool for storing the value of String and StyledString resources.
+// Styles and Strings are stored separately, since the runtime variant of this
+// class -- ResStringPool -- requires that styled strings *always* appear first, since their
+// style data is stored as an array indexed by the same indices as the main string pool array.
+// Otherwise, the style data array would have to be sparse and take up more space.
class StringPool {
public:
class Context {
public:
enum : uint32_t {
- kStylePriority = 0u,
kHighPriority = 1u,
kNormalPriority = 0x7fffffffu,
kLowPriority = 0xffffffffu,
@@ -58,8 +62,8 @@ class StringPool {
Context() = default;
Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {}
explicit Context(uint32_t p) : priority(p) {}
- explicit Context(const ConfigDescription& c)
- : priority(kNormalPriority), config(c) {}
+ explicit Context(const ConfigDescription& c) : priority(kNormalPriority), config(c) {
+ }
};
class Entry;
@@ -116,13 +120,14 @@ class StringPool {
public:
std::string value;
Context context;
- size_t index;
private:
friend class StringPool;
friend class Ref;
+ size_t index_;
int ref_;
+ const StringPool* pool_;
};
struct Span {
@@ -133,18 +138,18 @@ class StringPool {
class StyleEntry {
public:
- Ref str;
+ std::string value;
+ Context context;
std::vector<Span> spans;
private:
friend class StringPool;
friend class StyleRef;
+ size_t index_;
int ref_;
};
- using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
-
static bool FlattenUtf8(BigBuffer* out, const StringPool& pool);
static bool FlattenUtf16(BigBuffer* out, const StringPool& pool);
@@ -152,92 +157,61 @@ class StringPool {
StringPool(StringPool&&) = default;
StringPool& operator=(StringPool&&) = default;
- /**
- * Adds a string to the pool, unless it already exists. Returns
- * a reference to the string in the pool.
- */
+ // Adds a string to the pool, unless it already exists. Returns a reference to the string in the
+ // pool.
Ref MakeRef(const android::StringPiece& str);
- /**
- * Adds a string to the pool, unless it already exists, with a context
- * object that can be used when sorting the string pool. Returns
- * a reference to the string in the pool.
- */
+ // Adds a string to the pool, unless it already exists, with a context object that can be used
+ // when sorting the string pool. Returns a reference to the string in the pool.
Ref MakeRef(const android::StringPiece& str, const Context& context);
- /**
- * Adds a style to the string pool and returns a reference to it.
- */
+ // Adds a style to the string pool and returns a reference to it.
StyleRef MakeRef(const StyleString& str);
- /**
- * Adds a style to the string pool with a context object that
- * can be used when sorting the string pool. Returns a reference
- * to the style in the string pool.
- */
+ // Adds a style to the string pool with a context object that can be used when sorting the string
+ // pool. Returns a reference to the style in the string pool.
StyleRef MakeRef(const StyleString& str, const Context& context);
- /**
- * Adds a style from another string pool. Returns a reference to the
- * style in the string pool.
- */
+ // Adds a style from another string pool. Returns a reference to the style in the string pool.
StyleRef MakeRef(const StyleRef& ref);
- /**
- * Moves pool into this one without coalescing strings. When this
- * function returns, pool will be empty.
- */
+ // Moves pool into this one without coalescing strings. When this function returns, pool will be
+ // empty.
void Merge(StringPool&& pool);
- /**
- * Returns the number of strings in the table.
- */
- inline size_t size() const;
+ inline const std::vector<std::unique_ptr<Entry>>& strings() const {
+ return strings_;
+ }
- /**
- * Reserves space for strings and styles as an optimization.
- */
+ // Returns the number of strings in the table.
+ inline size_t size() const {
+ return styles_.size() + strings_.size();
+ }
+
+ // Reserves space for strings and styles as an optimization.
void HintWillAdd(size_t string_count, size_t style_count);
- /**
- * Sorts the strings according to some comparison function.
- */
- void Sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+ // Sorts the strings according to their Context using some comparison function.
+ // Equal Contexts are further sorted by string value, lexicographically.
+ // If no comparison function is provided, values are only sorted lexicographically.
+ void Sort(const std::function<int(const Context&, const Context&)>& cmp = nullptr);
- /**
- * Removes any strings that have no references.
- */
+ // Removes any strings that have no references.
void Prune();
private:
DISALLOW_COPY_AND_ASSIGN(StringPool);
- friend const_iterator begin(const StringPool& pool);
- friend const_iterator end(const StringPool& pool);
-
static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
+ void ReAssignIndices();
std::vector<std::unique_ptr<Entry>> strings_;
std::vector<std::unique_ptr<StyleEntry>> styles_;
std::unordered_multimap<android::StringPiece, Entry*> indexed_strings_;
};
-//
-// Inline implementation
-//
-
-inline size_t StringPool::size() const { return strings_.size(); }
-
-inline StringPool::const_iterator begin(const StringPool& pool) {
- return pool.strings_.begin();
-}
-
-inline StringPool::const_iterator end(const StringPool& pool) {
- return pool.strings_.end();
-}
-
} // namespace aapt
#endif // AAPT_STRING_POOL_H
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index f64a8cf20928..b1e5ce2e28a8 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -23,8 +23,12 @@
#include "test/Test.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::NotNull;
+using ::testing::Pointee;
namespace aapt {
@@ -32,129 +36,127 @@ TEST(StringPoolTest, InsertOneString) {
StringPool pool;
StringPool::Ref ref = pool.MakeRef("wut");
- EXPECT_EQ(*ref, "wut");
+ EXPECT_THAT(*ref, Eq("wut"));
}
TEST(StringPoolTest, InsertTwoUniqueStrings) {
StringPool pool;
- StringPool::Ref ref = pool.MakeRef("wut");
- StringPool::Ref ref2 = pool.MakeRef("hey");
+ StringPool::Ref ref_a = pool.MakeRef("wut");
+ StringPool::Ref ref_b = pool.MakeRef("hey");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "hey");
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("hey"));
}
TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
StringPool pool;
- StringPool::Ref ref = pool.MakeRef("wut");
- StringPool::Ref ref2 = pool.MakeRef("wut");
+ StringPool::Ref ref_a = pool.MakeRef("wut");
+ StringPool::Ref ref_b = pool.MakeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "wut");
- EXPECT_EQ(1u, pool.size());
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(1u));
}
TEST(StringPoolTest, MaintainInsertionOrderIndex) {
StringPool pool;
- StringPool::Ref ref = pool.MakeRef("z");
- StringPool::Ref ref2 = pool.MakeRef("a");
- StringPool::Ref ref3 = pool.MakeRef("m");
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
- EXPECT_EQ(0u, ref.index());
- EXPECT_EQ(1u, ref2.index());
- EXPECT_EQ(2u, ref3.index());
+ EXPECT_THAT(ref_a.index(), Eq(0u));
+ EXPECT_THAT(ref_b.index(), Eq(1u));
+ EXPECT_THAT(ref_c.index(), Eq(2u));
}
TEST(StringPoolTest, PruneStringsWithNoReferences) {
StringPool pool;
- StringPool::Ref refA = pool.MakeRef("foo");
+ StringPool::Ref ref_a = pool.MakeRef("foo");
+
+ {
+ StringPool::Ref ref_b = pool.MakeRef("wut");
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(2u));
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(2u));
+ }
+ EXPECT_THAT(pool.size(), Eq(2u));
+
{
- StringPool::Ref ref = pool.MakeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(2u, pool.size());
+ StringPool::Ref ref_c = pool.MakeRef("bar");
+ EXPECT_THAT(pool.size(), Eq(3u));
+
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(2u));
}
- StringPool::Ref refB = pool.MakeRef("bar");
+ EXPECT_THAT(pool.size(), Eq(2u));
- EXPECT_EQ(3u, pool.size());
pool.Prune();
- EXPECT_EQ(2u, pool.size());
- StringPool::const_iterator iter = begin(pool);
- EXPECT_EQ((*iter)->value, "foo");
- EXPECT_LT((*iter)->index, 2u);
- ++iter;
- EXPECT_EQ((*iter)->value, "bar");
- EXPECT_LT((*iter)->index, 2u);
+ EXPECT_THAT(pool.size(), Eq(1u));
}
-TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
+TEST(StringPoolTest, SortAndMaintainIndexesInStringReferences) {
StringPool pool;
- StringPool::Ref ref = pool.MakeRef("z");
- StringPool::StyleRef ref2 = pool.MakeRef(StyleString{{"a"}});
- StringPool::Ref ref3 = pool.MakeRef("m");
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(0u, ref.index());
+ EXPECT_THAT(*ref_a, Eq("z"));
+ EXPECT_THAT(ref_a.index(), Eq(0u));
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(1u, ref2.index());
+ EXPECT_THAT(*ref_b, Eq("a"));
+ EXPECT_THAT(ref_b.index(), Eq(1u));
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(2u, ref3.index());
+ EXPECT_THAT(*ref_c, Eq("m"));
+ EXPECT_THAT(ref_c.index(), Eq(2u));
- pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.value < b.value;
- });
+ pool.Sort();
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(2u, ref.index());
+ EXPECT_THAT(*ref_a, Eq("z"));
+ EXPECT_THAT(ref_a.index(), Eq(2u));
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(0u, ref2.index());
+ EXPECT_THAT(*ref_b, Eq("a"));
+ EXPECT_THAT(ref_b.index(), Eq(0u));
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(1u, ref3.index());
+ EXPECT_THAT(*ref_c, Eq("m"));
+ EXPECT_THAT(ref_c.index(), Eq(1u));
}
TEST(StringPoolTest, SortAndStillDedupe) {
StringPool pool;
- StringPool::Ref ref = pool.MakeRef("z");
- StringPool::Ref ref2 = pool.MakeRef("a");
- StringPool::Ref ref3 = pool.MakeRef("m");
+ StringPool::Ref ref_a = pool.MakeRef("z");
+ StringPool::Ref ref_b = pool.MakeRef("a");
+ StringPool::Ref ref_c = pool.MakeRef("m");
- pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.value < b.value;
- });
+ pool.Sort();
- StringPool::Ref ref4 = pool.MakeRef("z");
- StringPool::Ref ref5 = pool.MakeRef("a");
- StringPool::Ref ref6 = pool.MakeRef("m");
+ StringPool::Ref ref_d = pool.MakeRef("z");
+ StringPool::Ref ref_e = pool.MakeRef("a");
+ StringPool::Ref ref_f = pool.MakeRef("m");
- EXPECT_EQ(ref4.index(), ref.index());
- EXPECT_EQ(ref5.index(), ref2.index());
- EXPECT_EQ(ref6.index(), ref3.index());
+ EXPECT_THAT(ref_d.index(), Eq(ref_a.index()));
+ EXPECT_THAT(ref_e.index(), Eq(ref_b.index()));
+ EXPECT_THAT(ref_f.index(), Eq(ref_c.index()));
}
TEST(StringPoolTest, AddStyles) {
StringPool pool;
- StyleString str{{"android"}, {Span{{"b"}, 2, 6}}};
-
- StringPool::StyleRef ref = pool.MakeRef(str);
-
- EXPECT_EQ(0u, ref.index());
- EXPECT_EQ(std::string("android"), *(ref->str));
- ASSERT_EQ(1u, ref->spans.size());
+ StringPool::StyleRef ref = pool.MakeRef(StyleString{{"android"}, {Span{{"b"}, 2, 6}}});
+ EXPECT_THAT(ref.index(), Eq(0u));
+ EXPECT_THAT(ref->value, Eq("android"));
+ ASSERT_THAT(ref->spans.size(), Eq(1u));
const StringPool::Span& span = ref->spans.front();
- EXPECT_EQ(*(span.name), "b");
- EXPECT_EQ(2u, span.first_char);
- EXPECT_EQ(6u, span.last_char);
+ EXPECT_THAT(*span.name, Eq("b"));
+ EXPECT_THAT(span.first_char, Eq(2u));
+ EXPECT_THAT(span.last_char, Eq(6u));
}
TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
@@ -163,9 +165,25 @@ TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
StringPool::Ref ref = pool.MakeRef("android");
StyleString str{{"android"}};
- StringPool::StyleRef styleRef = pool.MakeRef(str);
+ StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}});
+
+ EXPECT_THAT(ref.index(), Ne(style_ref.index()));
+}
+
+TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) {
+ StringPool pool;
+
+ StringPool::StyleRef ref_a = pool.MakeRef(StyleString{{"beta"}});
+ StringPool::Ref ref_b = pool.MakeRef("alpha");
+ StringPool::StyleRef ref_c = pool.MakeRef(StyleString{{"alpha"}});
+
+ EXPECT_THAT(ref_b.index(), Ne(ref_c.index()));
- EXPECT_NE(ref.index(), styleRef.index());
+ pool.Sort();
+
+ EXPECT_THAT(ref_c.index(), Eq(0u));
+ EXPECT_THAT(ref_a.index(), Eq(1u));
+ EXPECT_THAT(ref_b.index(), Eq(2u));
}
TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
@@ -177,7 +195,7 @@ TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ ASSERT_THAT(test.setTo(data.get(), buffer.size()), Eq(NO_ERROR));
}
TEST(StringPoolTest, FlattenOddCharactersUtf16) {
@@ -193,9 +211,9 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) {
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
size_t len = 0;
const char16_t* str = test.stringAt(0, &len);
- EXPECT_EQ(1u, len);
- EXPECT_EQ(u'\u093f', *str);
- EXPECT_EQ(0u, str[1]);
+ EXPECT_THAT(len, Eq(1u));
+ EXPECT_THAT(str, Pointee(Eq(u'\u093f')));
+ EXPECT_THAT(str[1], Eq(0u));
}
constexpr const char* sLongString =
@@ -210,18 +228,20 @@ TEST(StringPoolTest, Flatten) {
StringPool pool;
- StringPool::Ref ref1 = pool.MakeRef("hello");
- StringPool::Ref ref2 = pool.MakeRef("goodbye");
- StringPool::Ref ref3 = pool.MakeRef(sLongString);
- StringPool::Ref ref4 = pool.MakeRef("");
- StringPool::StyleRef ref5 = pool.MakeRef(
- StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
+ StringPool::Ref ref_a = pool.MakeRef("hello");
+ StringPool::Ref ref_b = pool.MakeRef("goodbye");
+ StringPool::Ref ref_c = pool.MakeRef(sLongString);
+ StringPool::Ref ref_d = pool.MakeRef("");
+ StringPool::StyleRef ref_e =
+ pool.MakeRef(StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
+
+ // Styles are always first.
+ EXPECT_THAT(ref_e.index(), Eq(0u));
- EXPECT_EQ(0u, ref1.index());
- EXPECT_EQ(1u, ref2.index());
- EXPECT_EQ(2u, ref3.index());
- EXPECT_EQ(3u, ref4.index());
- EXPECT_EQ(4u, ref5.index());
+ EXPECT_THAT(ref_a.index(), Eq(1u));
+ EXPECT_THAT(ref_b.index(), Eq(2u));
+ EXPECT_THAT(ref_c.index(), Eq(3u));
+ EXPECT_THAT(ref_d.index(), Eq(4u));
BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
StringPool::FlattenUtf8(&buffers[0], pool);
@@ -234,38 +254,37 @@ TEST(StringPoolTest, Flatten) {
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- EXPECT_EQ(std::string("hello"), util::GetString(test, 0));
- EXPECT_EQ(StringPiece16(u"hello"), util::GetString16(test, 0));
+ EXPECT_THAT(util::GetString(test, 1), Eq("hello"));
+ EXPECT_THAT(util::GetString16(test, 1), Eq(u"hello"));
- EXPECT_EQ(std::string("goodbye"), util::GetString(test, 1));
- EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1));
+ EXPECT_THAT(util::GetString(test, 2), Eq("goodbye"));
+ EXPECT_THAT(util::GetString16(test, 2), Eq(u"goodbye"));
- EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2));
- EXPECT_EQ(util::Utf8ToUtf16(sLongString), util::GetString16(test, 2).to_string());
+ EXPECT_THAT(util::GetString(test, 3), Eq(sLongString));
+ EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString)));
size_t len;
- EXPECT_TRUE(test.stringAt(3, &len) != nullptr ||
- test.string8At(3, &len) != nullptr);
-
- EXPECT_EQ(std::string("style"), util::GetString(test, 4));
- EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4));
-
- const ResStringPool_span* span = test.styleAt(4);
- ASSERT_NE(nullptr, span);
- EXPECT_EQ(std::string("b"), util::GetString(test, span->name.index));
- EXPECT_EQ(StringPiece16(u"b"), util::GetString16(test, span->name.index));
- EXPECT_EQ(0u, span->firstChar);
- EXPECT_EQ(1u, span->lastChar);
+ EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr);
+
+ EXPECT_THAT(util::GetString(test, 0), Eq("style"));
+ EXPECT_THAT(util::GetString16(test, 0), Eq(u"style"));
+
+ const ResStringPool_span* span = test.styleAt(0);
+ ASSERT_THAT(span, NotNull());
+ EXPECT_THAT(util::GetString(test, span->name.index), Eq("b"));
+ EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b"));
+ EXPECT_THAT(span->firstChar, Eq(0u));
+ EXPECT_THAT(span->lastChar, Eq(1u));
span++;
- ASSERT_NE(ResStringPool_span::END, span->name.index);
- EXPECT_EQ(std::string("i"), util::GetString(test, span->name.index));
- EXPECT_EQ(StringPiece16(u"i"), util::GetString16(test, span->name.index));
- EXPECT_EQ(2u, span->firstChar);
- EXPECT_EQ(3u, span->lastChar);
+ ASSERT_THAT(span->name.index, Ne(ResStringPool_span::END));
+ EXPECT_THAT(util::GetString(test, span->name.index), Eq("i"));
+ EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"i"));
+ EXPECT_THAT(span->firstChar, Eq(2u));
+ EXPECT_THAT(span->lastChar, Eq(3u));
span++;
- EXPECT_EQ(ResStringPool_span::END, span->name.index);
+ EXPECT_THAT(span->name.index, Eq(ResStringPool_span::END));
}
}
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 740a401f9b57..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))) {
@@ -574,6 +574,11 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
if (file_op.xml_to_flatten) {
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
LinkAndVersionXmlFile(table, &file_op);
+ if (versioned_docs.empty()) {
+ error = true;
+ continue;
+ }
+
for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
std::string dst_path = file_op.dst_path;
if (doc->file.config != file_op.config) {
@@ -989,7 +994,8 @@ class LinkCommand {
manifest_class->GetCommentBuilder()->AppendComment(proper_annotation);
}
- const std::string& package_utf8 = context_->GetCompilationPackage();
+ const std::string package_utf8 =
+ options_.custom_java_package.value_or_default(context_->GetCompilationPackage());
std::string out_path = options_.generate_java_class_path.value();
file::AppendPath(&out_path, file::PackageToPath(package_utf8));
@@ -1289,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/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 194c0c80c2b2..9d71775889d4 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -17,6 +17,7 @@
#include <memory>
#include <vector>
+#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
#include "Diagnostics.h"
@@ -26,6 +27,8 @@
#include "SdkConstants.h"
#include "ValueVisitor.h"
#include "cmd/Util.h"
+#include "configuration/ConfigurationParser.h"
+#include "filter/AbiFilter.h"
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
@@ -33,14 +36,21 @@
#include "optimize/ResourceDeduper.h"
#include "optimize/VersionCollapser.h"
#include "split/TableSplitter.h"
+#include "util/Files.h"
-using android::StringPiece;
+using ::aapt::configuration::Abi;
+using ::aapt::configuration::Artifact;
+using ::aapt::configuration::PostProcessingConfiguration;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
struct OptimizeOptions {
// Path to the output APK.
- std::string output_path;
+ Maybe<std::string> output_path;
+ // Path to the output APK directory for splits.
+ Maybe<std::string> output_dir;
// Details of the app extracted from the AndroidManifest.xml
AppInfo app_info;
@@ -55,6 +65,8 @@ struct OptimizeOptions {
std::vector<SplitConstraints> split_constraints;
TableFlattenerOptions table_flattener_options;
+
+ Maybe<PostProcessingConfiguration> configuration;
};
class OptimizeContext : public IAaptContext {
@@ -175,10 +187,52 @@ class OptimizeCommand {
++split_constraints_iter;
}
- std::unique_ptr<IArchiveWriter> writer =
- CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path);
- if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) {
- return 1;
+ if (options_.configuration && options_.output_dir) {
+ PostProcessingConfiguration& config = options_.configuration.value();
+
+ // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
+ for (const Artifact& artifact : config.artifacts) {
+ if (artifact.abi_group) {
+ const std::string& group = artifact.abi_group.value();
+
+ auto abi_group = config.abi_groups.find(group);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (abi_group == config.abi_groups.end()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage() << "could not find referenced ABI group '" << group << "'");
+ return 1;
+ }
+ FilterChain filters;
+ filters.AddFilter(AbiFilter::FromAbiList(abi_group->second));
+
+ const std::string& path = apk->GetSource().path;
+ const StringPiece ext = file::GetExtension(path);
+ const std::string name = path.substr(0, path.rfind(ext.to_string()));
+
+ // Name is hard coded for now since only one split dimension is supported.
+ // TODO: Incorporate name generation into the configuration objects.
+ const std::string file_name =
+ StringPrintf("%s.%s%s", name.c_str(), group.c_str(), ext.data());
+ std::string out = options_.output_dir.value();
+ file::AppendPath(&out, file_name);
+
+ std::unique_ptr<IArchiveWriter> writer =
+ CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
+
+ if (!apk->WriteToArchive(context_, options_.table_flattener_options, &filters,
+ writer.get())) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (options_.output_path) {
+ std::unique_ptr<IArchiveWriter> writer =
+ CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value());
+ if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) {
+ return 1;
+ }
}
return 0;
@@ -214,8 +268,8 @@ class OptimizeCommand {
if (file_ref->file == nullptr) {
ResourceNameRef name(pkg->name, type->type, entry->name);
context_->GetDiagnostics()->Warn(DiagMessage(file_ref->GetSource())
- << "file for resource " << name << " with config '"
- << config_value->config << "' not found");
+ << "file for resource " << name << " with config '"
+ << config_value->config << "' not found");
continue;
}
@@ -293,13 +347,16 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
int Optimize(const std::vector<StringPiece>& args) {
OptimizeContext context;
OptimizeOptions options;
+ Maybe<std::string> config_path;
Maybe<std::string> target_densities;
std::vector<std::string> configs;
std::vector<std::string> split_args;
bool verbose = false;
Flags flags =
Flags()
- .RequiredFlag("-o", "Path to the output APK.", &options.output_path)
+ .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
+ .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
+ .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
.OptionalFlag(
"--target-densities",
"Comma separated list of the screen densities that the APK will be optimized for.\n"
@@ -369,6 +426,22 @@ int Optimize(const std::vector<StringPiece>& args) {
}
}
+ if (config_path) {
+ if (!options.output_dir) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "Output directory is required when using a configuration file");
+ return 1;
+ }
+ std::string& path = config_path.value();
+ Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
+ if (for_path) {
+ options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse();
+ } else {
+ context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path);
+ return 1;
+ }
+ }
+
if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
return 1;
}
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 8741b7b678ec..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,6 +181,13 @@ 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 (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, ",");
+ manifest_el->attributes.push_back(xml::Attribute{"", "targetConfig", target_config_str.str()});
+
std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>();
application_el->name = "application";
application_el->attributes.push_back(
@@ -186,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;
}
@@ -277,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/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index d465091d224e..5cff0048c062 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -64,12 +64,12 @@ TEST(IdAssignerTest, AssignIdsWithReservedIds) {
// Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
- AAPT_ASSERT_TRUE(maybe_result);
+ ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(2), maybe_result.value().type->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:integer/three"));
- AAPT_ASSERT_TRUE(maybe_result);
+ ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(3), maybe_result.value().type->id);
// Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
@@ -77,17 +77,17 @@ TEST(IdAssignerTest, AssignIdsWithReservedIds) {
maybe_result =
table->FindResource(test::ParseNameOrDie("android:string/five"));
- AAPT_ASSERT_TRUE(maybe_result);
+ ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint8_t>(5), maybe_result.value().type->id);
// Expect to fill in the gaps between 0x01040000 and 0x01040006.
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
- AAPT_ASSERT_TRUE(maybe_result);
+ ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint16_t>(1), maybe_result.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
- AAPT_ASSERT_TRUE(maybe_result);
+ ASSERT_TRUE(maybe_result);
EXPECT_EQ(make_value<uint16_t>(2), maybe_result.value().entry->id);
}
@@ -121,7 +121,7 @@ TEST(IdAssignerTest, AssignIdsWithIdMap) {
ASSERT_TRUE(VerifyIds(table.get()));
Maybe<ResourceTable::SearchResult> result =
table->FindResource(test::ParseNameOrDie("android:attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ ASSERT_TRUE(result);
const ResourceTable::SearchResult& search_result = result.value();
EXPECT_EQ(make_value<uint8_t>(0x01), search_result.package->id);
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/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index a031ea4c31ec..871ed4f01e77 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -120,7 +120,7 @@ std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string,
// All Span indices are UTF-16 based, according to the resources.arsc format expected by the
// runtime. So we will do all our processing in UTF-16, then convert back.
- const std::u16string text16 = util::Utf8ToUtf16(*string->value->str);
+ const std::u16string text16 = util::Utf8ToUtf16(string->value->value);
// Convenient wrapper around the text that allows us to work with StringPieces.
const StringPiece16 text(text16);
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index b08e1dab35a9..711558aa51c1 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -31,7 +31,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
Pseudolocalizer::Method::kNone, &pool);
- EXPECT_EQ(original_style.str, *new_string->value->str);
+ EXPECT_EQ(original_style.str, new_string->value->value);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name);
@@ -52,7 +52,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
Pseudolocalizer::Method::kAccent, &pool);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), new_string->value->value);
ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -79,7 +79,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) {
Pseudolocalizer::Method::kAccent, &pool);
ASSERT_NE(nullptr, new_string);
ASSERT_EQ(2u, new_string->value->spans.size());
- EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+ EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -101,7 +101,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) {
Pseudolocalizer::Method::kAccent, &pool);
ASSERT_NE(nullptr, new_string);
ASSERT_EQ(2u, new_string->value->spans.size());
- EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+ EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value);
EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
@@ -126,7 +126,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) {
ASSERT_EQ(4u, new_string->value->spans.size());
EXPECT_EQ(std::string(
"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"),
- *new_string->value->str);
+ new_string->value->value);
EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char);
@@ -165,7 +165,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) {
ASSERT_NE(nullptr, new_string);
ASSERT_EQ(2u, new_string->value->spans.size());
EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"),
- *new_string->value->str);
+ new_string->value->value);
EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name);
EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char);
@@ -265,7 +265,7 @@ TEST(PseudolocaleGeneratorTest, RespectUntranslateableSections) {
ASSERT_NE(nullptr, new_styled_string);
// "world" should be untranslated.
- EXPECT_NE(std::string::npos, new_styled_string->value->str->find("world"));
+ EXPECT_NE(std::string::npos, new_styled_string->value->value.find("world"));
String* new_string = test::GetValueForConfig<String>(table.get(), "android:string/bar",
test::ParseConfigOrDie("en-rXA"));
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
index 15a3d8c289e2..3a515fad3202 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -445,9 +445,15 @@ std::string PseudoMethodBidi::Text(const StringPiece& source) {
std::string result;
bool lastspace = true;
bool space = true;
+ bool escape = false;
+ const char ESCAPE_CHAR = '\\';
for (size_t i = 0; i < source.size(); i++) {
char c = s[i];
- space = isspace(c);
+ if (!escape && c == ESCAPE_CHAR) {
+ escape = true;
+ continue;
+ }
+ space = (!escape && isspace(c)) || (escape && (c == 'n' || c == 't'));
if (lastspace && !space) {
// Word start
result += kRlm + kRlo;
@@ -456,6 +462,10 @@ std::string PseudoMethodBidi::Text(const StringPiece& source) {
result += kPdf + kRlm;
}
lastspace = space;
+ if (escape) {
+ result.append(&ESCAPE_CHAR, 1);
+ escape=false;
+ }
result.append(&c, 1);
}
if (!lastspace) {
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
index d3b7b02d1abb..65d2472ef2a9 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -97,6 +97,11 @@ TEST(PseudolocalizerTest, PlaintextBidi) {
"\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
" \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(
+ SimpleHelper("hello\\nworld\\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\\n"
+ "\xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\\n",
+ Pseudolocalizer::Method::kBidi));
}
TEST(PseudolocalizerTest, SimpleICU) {
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 89618d3a4676..bdccf8bcae3a 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -18,13 +18,19 @@
#include <algorithm>
#include <functional>
+#include <map>
#include <memory>
#include <utility>
-#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"
#include "xml/XmlDom.h"
@@ -38,27 +44,30 @@ using ::aapt::configuration::Abi;
using ::aapt::configuration::AndroidManifest;
using ::aapt::configuration::AndroidSdk;
using ::aapt::configuration::Artifact;
-using ::aapt::configuration::Configuration;
+using ::aapt::configuration::PostProcessingConfiguration;
using ::aapt::configuration::GlTexture;
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;
using ::aapt::xml::XmlNodeAction;
+using ::android::base::ReadFileToString;
-const std::unordered_map<std::string, Abi> kAbiMap = {
- {"armeabi", Abi::kArmeV6},
- {"armeabi-v7a", Abi::kArmV7a},
- {"arm64-v8a", Abi::kArm64V8a},
- {"x86", Abi::kX86},
- {"x86_64", Abi::kX86_64},
- {"mips", Abi::kMips},
- {"mips64", Abi::kMips64},
- {"universal", Abi::kUniversal},
+const std::unordered_map<std::string, Abi> kStringToAbiMap = {
+ {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
+ {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
+ {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
+};
+const std::map<Abi, std::string> kAbiToStringMap = {
+ {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
+ {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
+ {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
};
constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
@@ -96,21 +105,92 @@ class NamespaceVisitor : public xml::Visitor {
} // namespace
+namespace configuration {
+
+const std::string& AbiToString(Abi abi) {
+ return kAbiToStringMap.find(abi)->second;
+}
+
+/**
+ * Attempts to replace the placeholder in the name string with the provided value. Returns true on
+ * success, or false if the either the placeholder is not found in the name, or the value is not
+ * present and the placeholder was.
+ */
+static bool ReplacePlaceholder(const std::string& placeholder, const Maybe<std::string>& value,
+ std::string* name, IDiagnostics* diag) {
+ size_t offset = name->find(placeholder);
+ if (value) {
+ if (offset == std::string::npos) {
+ diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
+ return false;
+ }
+ name->replace(offset, placeholder.length(), value.value());
+ return true;
+ }
+
+ // Make sure the placeholder was not present if the desired value was not present.
+ bool result = (offset == std::string::npos);
+ if (!result) {
+ diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
+ }
+ return result;
+}
+
+Maybe<std::string> Artifact::ToArtifactName(const std::string& format, IDiagnostics* diag) const {
+ std::string result = format;
+
+ if (!ReplacePlaceholder("{abi}", abi_group, &result, diag)) {
+ return {};
+ }
+
+ if (!ReplacePlaceholder("{density}", screen_density_group, &result, diag)) {
+ return {};
+ }
+
+ if (!ReplacePlaceholder("{locale}", locale_group, &result, diag)) {
+ return {};
+ }
+
+ if (!ReplacePlaceholder("{sdk}", android_sdk_group, &result, diag)) {
+ return {};
+ }
+
+ if (!ReplacePlaceholder("{feature}", device_feature_group, &result, diag)) {
+ return {};
+ }
+
+ if (!ReplacePlaceholder("{gl}", gl_texture_group, &result, diag)) {
+ return {};
+ }
+
+ return result;
+}
+
+} // namespace configuration
+
+/** Returns a ConfigurationParser for the file located at the provided path. */
+Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
+ std::string contents;
+ if (!ReadFileToString(path, &contents, true)) {
+ return {};
+ }
+ return ConfigurationParser(contents);
+}
+
ConfigurationParser::ConfigurationParser(std::string contents)
: contents_(std::move(contents)),
diag_(&noop_) {
}
-Maybe<Configuration> ConfigurationParser::Parse() {
- std::istringstream in(contents_);
-
- auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
+Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
+ 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 {};
@@ -133,10 +213,11 @@ Maybe<Configuration> ConfigurationParser::Parse() {
XmlNodeAction& artifacts_action = root_action["artifacts"];
XmlNodeAction& groups_action = root_action["groups"];
- Configuration config;
+ PostProcessingConfiguration config;
// Helper to bind a static method to an action handler in the DOM executor.
- auto bind_handler = [&config](std::function<bool(Configuration*, Element*, IDiagnostics*)> h)
+ auto bind_handler =
+ [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
-> XmlNodeAction::ActionFuncWithDiag {
return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
};
@@ -158,275 +239,266 @@ Maybe<Configuration> ConfigurationParser::Parse() {
return {};
}
+ // TODO: Validate all references in the configuration are valid. It should be safe to assume from
+ // this point on that any references from one section to another will be present.
+
return {config};
}
ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- Artifact artifact{};
- for (const auto& attr : root_element->attributes) {
- if (attr.name == "name") {
- artifact.name = attr.value;
- } else if (attr.name == "abi-group") {
- artifact.abi_group = {attr.value};
- } else if (attr.name == "screen-density-group") {
- artifact.screen_density_group = {attr.value};
- } else if (attr.name == "locale-group") {
- artifact.locale_group = {attr.value};
- } else if (attr.name == "android-sdk-group") {
- artifact.android_sdk_group = {attr.value};
- } else if (attr.name == "gl-texture-group") {
- artifact.gl_texture_group = {attr.value};
- } else if (attr.name == "device-feature-group") {
- artifact.device_feature_group = {attr.value};
- } else {
- diag->Note(
- DiagMessage() << "Unknown artifact attribute: " << attr.name << " = " << attr.value);
- }
- }
- config->artifacts[artifact.name] = artifact;
- return true;
- };
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ Artifact artifact{};
+ for (const auto& attr : root_element->attributes) {
+ if (attr.name == "name") {
+ artifact.name = attr.value;
+ } else if (attr.name == "abi-group") {
+ artifact.abi_group = {attr.value};
+ } else if (attr.name == "screen-density-group") {
+ artifact.screen_density_group = {attr.value};
+ } else if (attr.name == "locale-group") {
+ artifact.locale_group = {attr.value};
+ } else if (attr.name == "android-sdk-group") {
+ artifact.android_sdk_group = {attr.value};
+ } else if (attr.name == "gl-texture-group") {
+ artifact.gl_texture_group = {attr.value};
+ } else if (attr.name == "device-feature-group") {
+ artifact.device_feature_group = {attr.value};
+ } else {
+ diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
+ << attr.value);
+ }
+ }
+ config->artifacts.push_back(artifact);
+ return true;
+};
ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- for (auto& node : root_element->children) {
- xml::Text* t;
- if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- config->artifact_format = TrimWhitespace(t->text).to_string();
- break;
- }
- }
- return true;
- };
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ for (auto& node : root_element->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ config->artifact_format = TrimWhitespace(t->text).to_string();
+ break;
+ }
+ }
+ return true;
+};
ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- auto& group = config->abi_groups[label];
- bool valid = true;
+ auto& group = config->abi_groups[label];
+ bool valid = true;
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "abi") {
- diag->Error(
- DiagMessage() << "Unexpected element in ABI group: " << child->name);
- valid = false;
- } else {
- for (auto& node : child->children) {
- xml::Text* t;
- if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- group.push_back(kAbiMap.at(TrimWhitespace(t->text).to_string()));
- break;
- }
- }
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "abi") {
+ diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
+ valid = false;
+ } else {
+ for (auto& node : child->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
+ break;
}
}
+ }
+ }
- return valid;
- };
+ return valid;
+};
ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- auto& group = config->screen_density_groups[label];
- bool valid = true;
+ auto& group = config->screen_density_groups[label];
+ bool valid = true;
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "screen-density") {
- diag->Error(
- DiagMessage() << "Unexpected root_element in screen density group: "
- << child->name);
- valid = false;
- } else {
- for (auto& node : child->children) {
- xml::Text* t;
- if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- ConfigDescription config_descriptor;
- const android::StringPiece& text = TrimWhitespace(t->text);
- if (ConfigDescription::Parse(text, &config_descriptor)) {
- // Copy the density with the minimum SDK version stripped out.
- group.push_back(config_descriptor.CopyWithoutSdkVersion());
- } else {
- diag->Error(
- DiagMessage() << "Could not parse config descriptor for screen-density: "
- << text);
- valid = false;
- }
- break;
- }
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "screen-density") {
+ diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
+ << child->name);
+ valid = false;
+ } else {
+ for (auto& node : child->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ ConfigDescription config_descriptor;
+ const android::StringPiece& text = TrimWhitespace(t->text);
+ if (ConfigDescription::Parse(text, &config_descriptor)) {
+ // Copy the density with the minimum SDK version stripped out.
+ group.push_back(config_descriptor.CopyWithoutSdkVersion());
+ } else {
+ diag->Error(DiagMessage()
+ << "Could not parse config descriptor for screen-density: " << text);
+ valid = false;
}
+ break;
}
}
+ }
+ }
- return valid;
- };
+ return valid;
+};
ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- auto& group = config->locale_groups[label];
- bool valid = true;
+ auto& group = config->locale_groups[label];
+ bool valid = true;
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "locale") {
- diag->Error(
- DiagMessage() << "Unexpected root_element in screen density group: "
- << child->name);
- valid = false;
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "locale") {
+ diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
+ << child->name);
+ valid = false;
+ } else {
+ Locale entry;
+ for (const auto& attr : child->attributes) {
+ if (attr.name == "lang") {
+ entry.lang = {attr.value};
+ } else if (attr.name == "region") {
+ entry.region = {attr.value};
} else {
- Locale entry;
- for (const auto& attr : child->attributes) {
- if (attr.name == "lang") {
- entry.lang = {attr.value};
- } else if (attr.name == "region") {
- entry.region = {attr.value};
- } else {
- diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name
- << " = " << attr.value);
- }
- }
- group.push_back(entry);
+ diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
}
}
+ group.push_back(entry);
+ }
+ }
- return valid;
- };
+ return valid;
+};
ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
-
- auto& group = config->android_sdk_groups[label];
- bool valid = true;
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "android-sdk") {
- diag->Error(
- DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
- valid = false;
+ auto& group = config->android_sdk_groups[label];
+ bool valid = true;
+
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "android-sdk") {
+ diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
+ valid = false;
+ } else {
+ AndroidSdk entry;
+ for (const auto& attr : child->attributes) {
+ if (attr.name == "minSdkVersion") {
+ entry.min_sdk_version = {attr.value};
+ } else if (attr.name == "targetSdkVersion") {
+ entry.target_sdk_version = {attr.value};
+ } else if (attr.name == "maxSdkVersion") {
+ entry.max_sdk_version = {attr.value};
} else {
- AndroidSdk entry;
- for (const auto& attr : child->attributes) {
- if (attr.name == "minSdkVersion") {
- entry.min_sdk_version = {attr.value};
- } else if (attr.name == "targetSdkVersion") {
- entry.target_sdk_version = {attr.value};
- } else if (attr.name == "maxSdkVersion") {
- entry.max_sdk_version = {attr.value};
- } else {
- diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name
- << " = " << attr.value);
- }
- }
+ diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
+ }
+ }
- // TODO: Fill in the manifest details when they are finalised.
- for (auto node : child->GetChildElements()) {
- if (node->name == "manifest") {
- if (entry.manifest) {
- diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
- continue;
- }
- entry.manifest = {AndroidManifest()};
- }
+ // TODO: Fill in the manifest details when they are finalised.
+ for (auto node : child->GetChildElements()) {
+ if (node->name == "manifest") {
+ if (entry.manifest) {
+ diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
+ continue;
}
-
- group.push_back(entry);
+ entry.manifest = {AndroidManifest()};
}
}
- return valid;
- };
+ group.push_back(entry);
+ }
+ }
+
+ return valid;
+};
ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- auto& group = config->gl_texture_groups[label];
- bool valid = true;
+ auto& group = config->gl_texture_groups[label];
+ bool valid = true;
- GlTexture result;
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "gl-texture") {
- diag->Error(
- DiagMessage() << "Unexpected element in GL texture group: "
- << child->name);
- valid = false;
- } else {
- for (const auto& attr : child->attributes) {
- if (attr.name == "name") {
- result.name = attr.value;
- break;
- }
- }
+ GlTexture result;
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "gl-texture") {
+ diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
+ valid = false;
+ } else {
+ for (const auto& attr : child->attributes) {
+ if (attr.name == "name") {
+ result.name = attr.value;
+ break;
+ }
+ }
- for (auto* element : child->GetChildElements()) {
- if (element->name != "texture-path") {
- diag->Error(
- DiagMessage() << "Unexpected element in gl-texture element: "
- << child->name);
- valid = false;
- continue;
- }
- for (auto& node : element->children) {
- xml::Text* t;
- if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
- }
- }
+ for (auto* element : child->GetChildElements()) {
+ if (element->name != "texture-path") {
+ diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
+ valid = false;
+ continue;
+ }
+ for (auto& node : element->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
}
}
- group.push_back(result);
}
+ }
+ group.push_back(result);
+ }
- return valid;
- };
+ return valid;
+};
ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
- [](Configuration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
+ [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ std::string label = GetLabel(root_element, diag);
+ if (label.empty()) {
+ return false;
+ }
- auto& group = config->device_feature_groups[label];
- bool valid = true;
+ auto& group = config->device_feature_groups[label];
+ bool valid = true;
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "supports-feature") {
- diag->Error(
- DiagMessage() << "Unexpected root_element in device feature group: "
- << child->name);
- valid = false;
- } else {
- for (auto& node : child->children) {
- xml::Text* t;
- if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- group.push_back(TrimWhitespace(t->text).to_string());
- break;
- }
- }
+ for (auto* child : root_element->GetChildElements()) {
+ if (child->name != "supports-feature") {
+ diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
+ << child->name);
+ valid = false;
+ } else {
+ for (auto& node : child->children) {
+ xml::Text* t;
+ if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
+ group.push_back(TrimWhitespace(t->text).to_string());
+ break;
}
}
+ }
+ }
- return valid;
- };
+ return valid;
+};
} // namespace aapt
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 0fb2f714a76c..28c355e39643 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -20,8 +20,9 @@
#include <string>
#include <unordered_map>
#include <vector>
-#include <ConfigDescription.h>
+#include "ConfigDescription.h"
+#include "Diagnostics.h"
#include "util/Maybe.h"
namespace aapt {
@@ -48,6 +49,9 @@ struct Artifact {
Maybe<std::string> device_feature_group;
/** If present, uses the OpenGL texture group with this name. */
Maybe<std::string> gl_texture_group;
+
+ /** Convert an artifact name template into a name string based on configuration contents. */
+ Maybe<std::string> ToArtifactName(const std::string& format, IDiagnostics* diag) const;
};
/** Enumeration of currently supported ABIs. */
@@ -62,6 +66,9 @@ enum class Abi {
kUniversal
};
+/** Helper method to convert an ABI to a string representing the path within the APK. */
+const std::string& AbiToString(Abi abi);
+
/**
* Represents an individual locale. When a locale is included, it must be
* declared from least specific to most specific, as a region does not make
@@ -114,11 +121,10 @@ struct GlTexture {
}
};
-/**
- * AAPT2 XML configuration binary representation.
- */
-struct Configuration {
- std::unordered_map<std::string, Artifact> artifacts;
+/** AAPT2 XML configuration file binary representation. */
+struct PostProcessingConfiguration {
+ // TODO: Support named artifacts?
+ std::vector<Artifact> artifacts;
Maybe<std::string> artifact_format;
Group<Abi> abi_groups;
@@ -142,18 +148,16 @@ class Element;
*/
class ConfigurationParser {
public:
+
+ /** Returns a ConfigurationParser for the file located at the provided path. */
+ static Maybe<ConfigurationParser> ForPath(const std::string& path);
+
/** Returns a ConfigurationParser for the configuration in the provided file contents. */
static ConfigurationParser ForContents(const std::string& contents) {
ConfigurationParser parser{contents};
return parser;
}
- /** Returns a ConfigurationParser for the file located at the provided path. */
- static ConfigurationParser ForPath(const std::string& path) {
- // TODO: Read XML file into memory.
- return ForContents(path);
- }
-
/** Sets the diagnostics context to use when parsing. */
ConfigurationParser& WithDiagnostics(IDiagnostics* diagnostics) {
diag_ = diagnostics;
@@ -164,7 +168,7 @@ class ConfigurationParser {
* Parses the configuration file and returns the results. If the configuration could not be parsed
* the result is empty and any errors will be displayed with the provided diagnostics context.
*/
- Maybe<configuration::Configuration> Parse();
+ Maybe<configuration::PostProcessingConfiguration> Parse();
protected:
/**
@@ -183,9 +187,8 @@ class ConfigurationParser {
* An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
* element was successfully processed, otherwise returns false.
*/
- using ActionHandler = std::function<bool(configuration::Configuration* config,
- xml::Element* element,
- IDiagnostics* diag)>;
+ using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag)>;
/** Handler for <artifact> tags. */
static ActionHandler artifact_handler_;
@@ -213,4 +216,4 @@ class ConfigurationParser {
} // namespace aapt
-#endif //AAPT2_CONFIGURATION_H
+#endif // AAPT2_CONFIGURATION_H
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 72a97b273cb0..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,15 +26,16 @@
namespace aapt {
namespace {
-using android::ResTable_config;
+using ::android::ResTable_config;
using configuration::Abi;
using configuration::AndroidSdk;
-using configuration::Configuration;
+using configuration::Artifact;
+using configuration::PostProcessingConfiguration;
using configuration::DeviceFeature;
using configuration::GlTexture;
using configuration::Locale;
using configuration::AndroidManifest;
-using testing::ElementsAre;
+using ::testing::ElementsAre;
using xml::Element;
using xml::NodeCast;
@@ -130,11 +128,16 @@ class ConfigurationParserTest : public ConfigurationParser, public ::testing::Te
StdErrDiagnostics diag_;
};
+TEST_F(ConfigurationParserTest, ForPath_NoFile) {
+ auto result = ConfigurationParser::ForPath("./does_not_exist.xml");
+ EXPECT_FALSE(result);
+}
+
TEST_F(ConfigurationParserTest, ValidateFile) {
auto parser = ConfigurationParser::ForContents(kValidConfig).WithDiagnostics(&diag_);
auto result = parser.Parse();
ASSERT_TRUE(result);
- Configuration& value = result.value();
+ PostProcessingConfiguration& value = result.value();
EXPECT_EQ(2ul, value.artifacts.size());
ASSERT_TRUE(value.artifact_format);
EXPECT_EQ(
@@ -185,13 +188,13 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
- bool ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ PostProcessingConfiguration config;
+ bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
ASSERT_TRUE(ok);
EXPECT_EQ(1ul, config.artifacts.size());
- auto& artifact = config.artifacts.begin()->second;
+ auto& artifact = config.artifacts.front();
EXPECT_EQ("", artifact.name); // TODO: make this fail.
EXPECT_EQ("arm", artifact.abi_group.value());
EXPECT_EQ("large", artifact.screen_density_group.value());
@@ -199,6 +202,21 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
EXPECT_EQ("19", artifact.android_sdk_group.value());
EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
EXPECT_EQ("low-latency", artifact.device_feature_group.value());
+
+ // Perform a second action to ensure we get 2 artifacts.
+ static constexpr const char* second = R"xml(
+ <artifact
+ abi-group="other"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml";
+ doc = test::BuildXmlDom(second);
+
+ ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(2ul, config.artifacts.size());
}
TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
@@ -209,7 +227,7 @@ TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok = artifact_format_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
ASSERT_TRUE(config.artifact_format);
@@ -232,7 +250,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok = abi_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -255,7 +273,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok =
screen_density_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -285,7 +303,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok = locale_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -321,7 +339,7 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -353,7 +371,7 @@ TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok = gl_texture_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -382,7 +400,7 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
auto doc = test::BuildXmlDom(xml);
- Configuration config;
+ PostProcessingConfiguration config;
bool ok
= device_feature_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
@@ -397,5 +415,55 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
ASSERT_THAT(out, ElementsAre(low_latency, pro));
}
+TEST(ArtifactTest, Simple) {
+ StdErrDiagnostics diag;
+ Artifact x86;
+ x86.abi_group = {"x86"};
+
+ auto x86_result = x86.ToArtifactName("something.{abi}.apk", &diag);
+ ASSERT_TRUE(x86_result);
+ EXPECT_EQ(x86_result.value(), "something.x86.apk");
+
+ Artifact arm;
+ arm.abi_group = {"armeabi-v7a"};
+
+ auto arm_result = arm.ToArtifactName("app.{abi}.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+}
+
+TEST(ArtifactTest, Complex) {
+ StdErrDiagnostics diag;
+ Artifact artifact;
+ artifact.abi_group = {"mips64"};
+ artifact.screen_density_group = {"ldpi"};
+ artifact.device_feature_group = {"df1"};
+ artifact.gl_texture_group = {"glx1"};
+ artifact.locale_group = {"en-AU"};
+ artifact.android_sdk_group = {"26"};
+
+ auto result =
+ artifact.ToArtifactName("app.{density}_{locale}_{feature}_{gl}.sdk{sdk}.{abi}.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+}
+
+TEST(ArtifactTest, Missing) {
+ StdErrDiagnostics diag;
+ Artifact x86;
+ x86.abi_group = {"x86"};
+
+ EXPECT_FALSE(x86.ToArtifactName("something.{density}.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.apk", &diag));
+}
+
+TEST(ArtifactTest, Empty) {
+ StdErrDiagnostics diag;
+ Artifact artifact;
+
+ EXPECT_FALSE(artifact.ToArtifactName("something.{density}.apk", &diag));
+ EXPECT_TRUE(artifact.ToArtifactName("something.apk", &diag));
+}
+
} // namespace
} // namespace aapt
diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml
index a8360f870b71..ce31e61b2d62 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
-<post-process xmlns="http://schemas.android.com/tools/aapt2">
+<post-process xmlns="http://schemas.android.com/tools/aapt">
<groups>
<abi-group label="arm">
<abi>armeabi-v7a</abi>
diff --git a/tools/aapt2/filter/AbiFilter.cpp b/tools/aapt2/filter/AbiFilter.cpp
new file mode 100644
index 000000000000..cb96235f98f9
--- /dev/null
+++ b/tools/aapt2/filter/AbiFilter.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "AbiFilter.h"
+
+#include <memory>
+
+#include "io/Util.h"
+
+namespace aapt {
+
+std::unique_ptr<AbiFilter> AbiFilter::FromAbiList(const std::vector<configuration::Abi>& abi_list) {
+ std::unordered_set<std::string> abi_set;
+ for (auto& abi : abi_list) {
+ abi_set.insert(configuration::AbiToString(abi));
+ }
+ // Make unique by hand as the constructor is private.
+ return std::unique_ptr<AbiFilter>(new AbiFilter(abi_set));
+}
+
+bool AbiFilter::Keep(const std::string& path) {
+ // We only care about libraries.
+ if (!util::StartsWith(path, kLibPrefix)) {
+ return true;
+ }
+
+ auto abi_end = path.find('/', kLibPrefixLen);
+ if (abi_end == std::string::npos) {
+ // Ignore any files in the top level lib directory.
+ return true;
+ }
+
+ // Strip the lib/ prefix.
+ const std::string& path_abi = path.substr(kLibPrefixLen, abi_end - kLibPrefixLen);
+ return (abis_.find(path_abi) != abis_.end());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/filter/AbiFilter.h b/tools/aapt2/filter/AbiFilter.h
new file mode 100644
index 000000000000..d875cb2b127b
--- /dev/null
+++ b/tools/aapt2/filter/AbiFilter.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AAPT2_ABISPLITTER_H
+#define AAPT2_ABISPLITTER_H
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "configuration/ConfigurationParser.h"
+#include "filter/Filter.h"
+
+namespace aapt {
+
+/**
+ * Filters native library paths by ABI. ABIs present in the filter list are kept and all over
+ * libraries are removed. The filter is only applied to native library paths (this under lib/).
+ */
+class AbiFilter : public IPathFilter {
+ public:
+ /** Factory method to create a filter from a list of configuration::Abi. */
+ static std::unique_ptr<AbiFilter> FromAbiList(const std::vector<configuration::Abi>& abi_list);
+
+ /** Returns true if the path is for a native library in the list of desired ABIs. */
+ bool Keep(const std::string& path) override;
+
+ private:
+ explicit AbiFilter(std::unordered_set<std::string> abis) : abis_(std::move(abis)) {
+ }
+
+ /** The path prefix to where all native libs end up inside an APK file. */
+ static constexpr const char* kLibPrefix = "lib/";
+ static constexpr size_t kLibPrefixLen = 4;
+ const std::unordered_set<std::string> abis_;
+};
+
+} // namespace aapt
+
+#endif // AAPT2_ABISPLITTER_H
diff --git a/tools/aapt2/filter/AbiFilter_test.cpp b/tools/aapt2/filter/AbiFilter_test.cpp
new file mode 100644
index 000000000000..0c8ea3575a29
--- /dev/null
+++ b/tools/aapt2/filter/AbiFilter_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "filter/AbiFilter.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace aapt {
+namespace {
+
+using ::aapt::configuration::Abi;
+
+struct TestData {
+ std::string path;
+ bool kept;
+};
+
+const TestData kTestData[] = {
+ /* Keep. */
+ {"lib/mips/libnative.so", true},
+ {"not/native/file.txt", true},
+ // Not sure if this is a valid use case.
+ {"lib/listing.txt", true},
+ {"lib/mips/foo/bar/baz.so", true},
+ {"lib/mips/x86/foo.so", true},
+ /* Discard. */
+ {"lib/mips_horse/foo.so", false},
+ {"lib/horse_mips/foo.so", false},
+ {"lib/mips64/armeabi-v7a/foo.so", false},
+ {"lib/mips64/x86_64/x86.so", false},
+ {"lib/x86/libnative.so", false},
+ {"lib/x86/foo/bar/baz.so", false},
+ {"lib/x86/x86/foo.so", false},
+ {"lib/x86_horse/foo.so", false},
+ {"lib/horse_x86/foo.so", false},
+ {"lib/x86/armeabi-v7a/foo.so", false},
+ {"lib/x86_64/x86_64/x86.so", false},
+};
+
+class AbiFilterTest : public ::testing::TestWithParam<TestData> {};
+
+TEST_P(AbiFilterTest, Keep) {
+ auto mips = AbiFilter::FromAbiList({Abi::kMips});
+ const TestData& data = GetParam();
+ EXPECT_EQ(mips->Keep(data.path), data.kept);
+}
+
+INSTANTIATE_TEST_CASE_P(NativePaths, AbiFilterTest, ::testing::ValuesIn(kTestData));
+
+} // namespace
+} // namespace aapt
diff --git a/tools/aapt2/filter/Filter.h b/tools/aapt2/filter/Filter.h
new file mode 100644
index 000000000000..d737dc92e87b
--- /dev/null
+++ b/tools/aapt2/filter/Filter.h
@@ -0,0 +1,77 @@
+/*
+ * 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 AAPT2_FILTER_H
+#define AAPT2_FILTER_H
+
+#include <string>
+#include <vector>
+
+#include "util/Util.h"
+
+namespace aapt {
+
+/** A filter to be applied to a path segment. */
+class IPathFilter {
+ public:
+ ~IPathFilter() = default;
+
+ /** Returns true if the path should be kept. */
+ virtual bool Keep(const std::string& path) = 0;
+};
+
+/**
+ * Path filter that keeps anything that matches the provided prefix.
+ */
+class PrefixFilter : public IPathFilter {
+ public:
+ explicit PrefixFilter(std::string prefix) : prefix_(std::move(prefix)) {
+ }
+
+ /** Returns true if the provided path matches the prefix. */
+ bool Keep(const std::string& path) override {
+ return util::StartsWith(path, prefix_);
+ }
+
+ private:
+ const std::string prefix_;
+};
+
+/** Applies a set of IPathFilters to a path and returns true iif all filters keep the path. */
+class FilterChain : public IPathFilter {
+ public:
+ /** Adds a filter to the list to be applied to each path. */
+ void AddFilter(std::unique_ptr<IPathFilter> filter) {
+ filters_.push_back(std::move(filter));
+ }
+
+ /** Returns true if all filters keep the path. */
+ bool Keep(const std::string& path) override {
+ for (auto& filter : filters_) {
+ if (!filter->Keep(path)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ std::vector<std::unique_ptr<IPathFilter>> filters_;
+};
+
+} // namespace aapt
+
+#endif // AAPT2_FILTER_H
diff --git a/tools/aapt2/filter/Filter_test.cpp b/tools/aapt2/filter/Filter_test.cpp
new file mode 100644
index 000000000000..fb75a4b4d7c1
--- /dev/null
+++ b/tools/aapt2/filter/Filter_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "filter/Filter.h"
+
+#include <string>
+
+#include "io/Util.h"
+
+#include "gtest/gtest.h"
+
+namespace aapt {
+namespace {
+
+TEST(FilterChainTest, EmptyChain) {
+ FilterChain chain;
+ ASSERT_TRUE(chain.Keep("some/random/path"));
+}
+
+TEST(FilterChainTest, SingleFilter) {
+ FilterChain chain;
+ chain.AddFilter(util::make_unique<PrefixFilter>("keep/"));
+
+ ASSERT_FALSE(chain.Keep("removed/path"));
+ ASSERT_TRUE(chain.Keep("keep/path/1"));
+ ASSERT_TRUE(chain.Keep("keep/path/2"));
+}
+
+TEST(FilterChainTest, MultipleFilters) {
+ FilterChain chain;
+ chain.AddFilter(util::make_unique<PrefixFilter>("keep/"));
+ chain.AddFilter(util::make_unique<PrefixFilter>("keep/really/"));
+
+ ASSERT_FALSE(chain.Keep("removed/path"));
+ ASSERT_FALSE(chain.Keep("/keep/really/wrong/prefix"));
+ ASSERT_FALSE(chain.Keep("keep/maybe/1"));
+ ASSERT_TRUE(chain.Keep("keep/really/1"));
+}
+
+} // namespace
+} // namespace aapt
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 f4d02262f25c..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));
@@ -557,19 +557,15 @@ class PackageFlattener {
} // namespace
bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may
- // change.
- table->string_pool.Sort(
- [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- int diff = a.context.priority - b.context.priority;
- if (diff < 0) return true;
- if (diff > 0) return false;
- diff = a.context.config.compare(b.context.config);
- if (diff < 0) return true;
- if (diff > 0) return false;
- return a.value < b.value;
- });
+ // We must do this before writing the resources, since the string pool IDs may change.
table->string_pool.Prune();
+ table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+ int diff = util::compare(a.priority, b.priority);
+ if (diff == 0) {
+ diff = a.config.compare(b.config);
+ }
+ return diff;
+ });
// Write the ResTable header.
ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 6d1350d433a4..4fdb2eced59b 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -26,6 +26,9 @@
using namespace android;
+using ::testing::IsNull;
+using ::testing::NotNull;
+
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
@@ -235,13 +238,12 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
ResourceTable result;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &result));
- Attribute* actualAttr =
- test::GetValue<Attribute>(&result, "android:attr/foo");
- ASSERT_NE(nullptr, actualAttr);
- EXPECT_EQ(attr.IsWeak(), actualAttr->IsWeak());
- EXPECT_EQ(attr.type_mask, actualAttr->type_mask);
- EXPECT_EQ(attr.min_int, actualAttr->min_int);
- EXPECT_EQ(attr.max_int, actualAttr->max_int);
+ Attribute* actual_attr = test::GetValue<Attribute>(&result, "android:attr/foo");
+ ASSERT_THAT(actual_attr, NotNull());
+ EXPECT_EQ(attr.IsWeak(), actual_attr->IsWeak());
+ EXPECT_EQ(attr.type_mask, actual_attr->type_mask);
+ EXPECT_EQ(attr.min_int, actual_attr->min_int);
+ EXPECT_EQ(attr.max_int, actual_attr->max_int);
}
static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
@@ -303,15 +305,13 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) {
auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
sparse_config);
- ASSERT_NE(nullptr, value);
+ ASSERT_THAT(value, NotNull());
EXPECT_EQ(0u, value->value.data);
- ASSERT_EQ(nullptr, test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
- sparse_config));
+ ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", sparse_config), IsNull());
- value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
- sparse_config);
- ASSERT_NE(nullptr, value);
+ value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", sparse_config);
+ ASSERT_THAT(value, NotNull());
EXPECT_EQ(4u, value->value.data);
}
@@ -372,7 +372,7 @@ TEST_F(TableFlattenerTest, FlattenSharedLibrary) {
Maybe<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:id/foo"));
- AAPT_ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result);
EXPECT_EQ(0x00u, search_result.value().package->id.value());
auto iter = result.included_packages_.find(0x00);
@@ -398,7 +398,7 @@ TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) {
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
- ASSERT_NE(nullptr, dynamic_ref_table);
+ ASSERT_THAT(dynamic_ref_table, NotNull());
const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
@@ -423,7 +423,7 @@ TEST_F(TableFlattenerTest, PackageWithNonStandardIdHasDynamicRefTable) {
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
- ASSERT_NE(nullptr, dynamic_ref_table);
+ ASSERT_THAT(dynamic_ref_table, NotNull());
const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
ssize_t idx = entries.indexOfKey(android::String16("app"));
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index bfebedef2a1e..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,17 +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);
+ ResXMLTree_namespaceExt* flat_ns = writer.NextBlock<ResXMLTree_namespaceExt>();
+ AddString(decl.prefix, kLowPriority, &flat_ns->prefix);
+ AddString(decl.uri, kLowPriority, &flat_ns->uri);
writer.Finish();
}
@@ -289,8 +283,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
BigBuffer* buffer_;
XmlFlattenerOptions options_;
- // Scratch vector to filter attributes. We avoid allocations
- // making this a member.
+ // Scratch vector to filter attributes. We avoid allocations making this a member.
std::vector<xml::Attribute*> filtered_attrs_;
};
@@ -307,10 +300,9 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
}
// Sort the string pool so that attribute resource IDs show up first.
- visitor.pool.Sort(
- [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.context.priority < b.context.priority;
- });
+ visitor.pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+ return util::compare(a.priority, b.priority);
+ });
// Now we flatten the string pool references into the correct places.
for (const auto& ref_entry : visitor.string_refs) {
@@ -328,15 +320,13 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
// Write the array of resource IDs, indexed by StringPool order.
ChunkWriter res_id_map_writer(buffer_);
res_id_map_writer.StartChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
- for (const auto& str : visitor.pool) {
- ResourceId id = {str->context.priority};
- if (id.id == kLowPriority || !id.is_valid()) {
- // When we see the first non-resource ID,
- // we're done.
+ for (const auto& str : visitor.pool.strings()) {
+ ResourceId id(str->context.priority);
+ if (str->context.priority == kLowPriority || !id.is_valid()) {
+ // When we see the first non-resource ID, we're done.
break;
}
-
- *res_id_map_writer.NextBlock<uint32_t>() = id.id;
+ *res_id_map_writer.NextBlock<uint32_t>() = util::HostToDevice32(id.id);
}
res_id_map_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/integration-tests/AppOne/res/values/styles.xml b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
index f05845cfab28..19d96c0809db 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
@@ -25,6 +25,7 @@
<style name="Pop">
<item name="custom">@android:drawable/btn_default</item>
<item name="android:focusable">true</item>
+ <item name="android:numColumns">auto_fit</item>
</style>
<string name="yo">@string/wow</string>
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/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index a0ef00b1ea1f..1f83fa098d74 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -18,12 +18,29 @@
#include <algorithm>
+#include "text/Unicode.h"
+#include "text/Utf8Iterator.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::aapt::text::Utf8Iterator;
+using ::android::StringPiece;
namespace aapt {
+StringPiece AnnotationProcessor::ExtractFirstSentence(const StringPiece& comment) {
+ Utf8Iterator iter(comment);
+ while (iter.HasNext()) {
+ const char32_t codepoint = iter.Next();
+ if (codepoint == U'.') {
+ const size_t current_position = iter.Position();
+ if (!iter.HasNext() || text::IsWhitespace(iter.Next())) {
+ return comment.substr(0, current_position);
+ }
+ }
+ }
+ return comment;
+}
+
void AnnotationProcessor::AppendCommentLine(std::string& comment) {
static const std::string sDeprecated = "@deprecated";
static const std::string sSystemApi = "@SystemApi";
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index 99cd44fd2cc1..a06eda0f9c5c 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -53,6 +53,8 @@ namespace aapt {
*/
class AnnotationProcessor {
public:
+ static android::StringPiece ExtractFirstSentence(const android::StringPiece& comment);
+
/**
* Adds more comments. Since resources can have various values with different
* configurations,
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 3e43c4295c07..9ccac8888426 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -18,6 +18,10 @@
#include "test/Test.h"
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Not;
+
namespace aapt {
TEST(AnnotationProcessorTest, EmitsDeprecated) {
@@ -33,7 +37,7 @@ TEST(AnnotationProcessorTest, EmitsDeprecated) {
processor.WriteToStream(&result, "");
std::string annotations = result.str();
- EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
+ EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
}
TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
@@ -44,10 +48,20 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
processor.WriteToStream(&result, "");
std::string annotations = result.str();
- EXPECT_NE(std::string::npos,
- annotations.find("@android.annotation.SystemApi"));
- EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
- EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a system API"));
+}
+
+TEST(AnnotationProcessor, ExtractsFirstSentence) {
+ EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence("This is the only sentence"),
+ Eq("This is the only sentence"));
+ EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence(
+ "This is the\n first sentence. This is the rest of the paragraph."),
+ Eq("This is the\n first sentence."));
+ EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence(
+ "This is the first sentence with a {@link android.R.styleable.Theme}."),
+ Eq("This is the first sentence with a {@link android.R.styleable.Theme}."));
}
} // 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/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 2a23aa9e5372..44fa0f19a0e5 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -299,24 +299,16 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
}
const ResourceName& attr_name = entry.attr_ref->name.value();
- styleable_comment << "<tr><td>";
- styleable_comment << "<code>{@link #" << entry.field_name << " "
- << (!attr_name.package.empty()
- ? attr_name.package
- : context_->GetCompilationPackage())
- << ":" << attr_name.entry << "}</code>";
- styleable_comment << "</td>";
-
- styleable_comment << "<td>";
+ styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
+ << (!attr_name.package.empty() ? attr_name.package
+ : context_->GetCompilationPackage())
+ << ":" << attr_name.entry << "}</code></td>";
// Only use the comment up until the first '.'. This is to stay compatible with
// the way old AAPT did it (presumably to keep it short and to avoid including
// annotations like @hide which would affect this Styleable).
- auto iter = std::find(attr_comment_line.begin(), attr_comment_line.end(), '.');
- if (iter != attr_comment_line.end()) {
- attr_comment_line = attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
- }
- styleable_comment << attr_comment_line << "</td></tr>\n";
+ styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
+ << "</td></tr>\n";
}
styleable_comment << "</table>\n";
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 5ebf508807e8..44b6a1ffd5ae 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -18,124 +18,130 @@
#include "test/Test.h"
-namespace aapt {
+using ::testing::HasSubstr;
+using ::testing::Not;
-static ::testing::AssertionResult GetManifestClassText(IAaptContext* context,
- xml::XmlResource* res,
- std::string* out_str) {
- std::unique_ptr<ClassDefinition> manifest_class =
- GenerateManifestClass(context->GetDiagnostics(), res);
- if (!manifest_class) {
- return ::testing::AssertionFailure() << "manifest_class == nullptr";
- }
-
- std::stringstream out;
- if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true,
- &out)) {
- return ::testing::AssertionFailure() << "failed to write java file";
- }
+namespace aapt {
- *out_str = out.str();
- return ::testing::AssertionSuccess();
-}
+static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
+ std::string* out_str);
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"EOF(
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <permission android:name="android.permission.ACCESS_INTERNET" />
- <permission android:name="android.DO_DANGEROUS_THINGS" />
- <permission android:name="com.test.sample.permission.HUH" />
- <permission-group android:name="foo.bar.PERMISSION" />
- </manifest>)EOF");
+ 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="android.DO_DANGEROUS_THINGS" />
+ <permission android:name="com.test.sample.permission.HUH" />
+ <permission-group android:name="foo.bar.PERMISSION" />
+ </manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
- const size_t permission_class_pos =
- actual.find("public static final class permission {");
- const size_t permission_croup_class_pos =
+ ASSERT_THAT(actual, HasSubstr("public static final class permission {"));
+ ASSERT_THAT(actual, HasSubstr("public static final class permission_group {"));
+
+ const size_t permission_start_pos = actual.find("public static final class permission {");
+ const size_t permission_group_start_pos =
actual.find("public static final class permission_group {");
- ASSERT_NE(std::string::npos, permission_class_pos);
- ASSERT_NE(std::string::npos, permission_croup_class_pos);
//
// Make sure these permissions are in the permission class.
//
-
- size_t pos = actual.find(
- "public static final String ACCESS_INTERNET="
- "\"android.permission.ACCESS_INTERNET\";");
- EXPECT_GT(pos, permission_class_pos);
- EXPECT_LT(pos, permission_croup_class_pos);
-
- pos = actual.find(
- "public static final String DO_DANGEROUS_THINGS="
- "\"android.DO_DANGEROUS_THINGS\";");
- EXPECT_GT(pos, permission_class_pos);
- EXPECT_LT(pos, permission_croup_class_pos);
-
- pos = actual.find(
- "public static final String HUH=\"com.test.sample.permission.HUH\";");
- EXPECT_GT(pos, permission_class_pos);
- EXPECT_LT(pos, permission_croup_class_pos);
+ const std::string permission_class =
+ actual.substr(permission_start_pos, permission_group_start_pos - permission_start_pos);
+
+ EXPECT_THAT(
+ permission_class,
+ HasSubstr(
+ "public static final String ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";"));
+ EXPECT_THAT(
+ permission_class,
+ HasSubstr("public static final String DO_DANGEROUS_THINGS=\"android.DO_DANGEROUS_THINGS\";"));
+ EXPECT_THAT(permission_class,
+ HasSubstr("public static final String HUH=\"com.test.sample.permission.HUH\";"));
//
// Make sure these permissions are in the permission_group class
//
+ const std::string permission_group_class = actual.substr(permission_group_start_pos);
- pos = actual.find(
- "public static final String PERMISSION="
- "\"foo.bar.PERMISSION\";");
- EXPECT_GT(pos, permission_croup_class_pos);
- EXPECT_LT(pos, std::string::npos);
+ EXPECT_THAT(permission_group_class,
+ HasSubstr("public static final String PERMISSION=\"foo.bar.PERMISSION\";"));
}
TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"EOF(
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Required to access the internet.
- Added in API 1. -->
- <permission android:name="android.permission.ACCESS_INTERNET" />
- <!-- @deprecated This permission is for playing outside. -->
- <permission android:name="android.permission.PLAY_OUTSIDE" />
- <!-- This is a private permission for system only!
- @hide
- @SystemApi -->
- <permission android:name="android.permission.SECRET" />
- </manifest>)EOF");
+ std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Required to access the internet.
+ Added in API 1. -->
+ <permission android:name="android.permission.ACCESS_INTERNET" />
+ <!-- @deprecated This permission is for playing outside. -->
+ <permission android:name="android.permission.PLAY_OUTSIDE" />
+ <!-- This is a private permission for system only!
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.SECRET" />
+ </manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
- const char* expected_access_internet =
- R"EOF( /**
+ const char* expected_access_internet = R"( /**
* Required to access the internet.
* Added in API 1.
*/
- public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF";
+ public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)";
+ EXPECT_THAT(actual, HasSubstr(expected_access_internet));
- EXPECT_NE(std::string::npos, actual.find(expected_access_internet));
-
- const char* expected_play_outside =
- R"EOF( /**
+ const char* expected_play_outside = R"( /**
* @deprecated This permission is for playing outside.
*/
@Deprecated
- public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF";
-
- EXPECT_NE(std::string::npos, actual.find(expected_play_outside));
+ public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)";
+ EXPECT_THAT(actual, HasSubstr(expected_play_outside));
- const char* expected_secret =
- R"EOF( /**
+ const char* expected_secret = R"( /**
* This is a private permission for system only!
* @hide
*/
@android.annotation.SystemApi
- public static final String SECRET="android.permission.SECRET";)EOF";
+ public static final String SECRET="android.permission.SECRET";)";
+ 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 =
+ GenerateManifestClass(context->GetDiagnostics(), res);
+ if (!manifest_class) {
+ return ::testing::AssertionFailure() << "manifest_class == nullptr";
+ }
+
+ std::stringstream out;
+ if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out)) {
+ return ::testing::AssertionFailure() << "failed to write java file";
+ }
- EXPECT_NE(std::string::npos, actual.find(expected_secret));
+ *out_str = out.str();
+ return ::testing::AssertionSuccess();
}
} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 624a559c4dae..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,14 +72,18 @@ 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()) {
- check_class = node->name == "view" || node->name == "fragment";
+ if (node->name == "view") {
+ check_class = true;
+ } else if (node->name == "fragment") {
+ check_class = check_name = true;
+ }
} else if (node->namespace_uri == xml::kSchemaAndroid) {
check_name = node->name == "fragment";
}
@@ -110,12 +108,38 @@ class LayoutVisitor : public BaseVisitor {
DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
};
+class MenuVisitor : public BaseVisitor {
+ public:
+ MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
+ }
+
+ 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) {
+ if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
+ util::IsJavaClassName(attr.value)) {
+ AddClass(node->line_number, attr.value);
+ } else if (attr.name == "onClick") {
+ AddMethod(node->line_number, attr.value);
+ }
+ }
+ }
+ }
+
+ BaseVisitor::Visit(node);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MenuVisitor);
+};
+
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 =
@@ -139,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)) {
@@ -165,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") {
@@ -175,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;
}
@@ -196,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_;
@@ -212,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());
}
@@ -231,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) {
@@ -242,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;
}
@@ -267,6 +283,12 @@ bool CollectProguardRules(const Source& source, xml::XmlResource* res,
break;
}
+ case ResourceType::kMenu: {
+ MenuVisitor visitor(source, keep_set);
+ res->root->Accept(&visitor);
+ break;
+ }
+
default:
break;
}
@@ -285,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/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
new file mode 100644
index 000000000000..900b07339715
--- /dev/null
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "java/ProguardRules.h"
+
+#include "test/Test.h"
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+namespace aapt {
+
+TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="com.foo.Bar"/>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+
+ std::stringstream out;
+ ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+
+ std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+}
+
+TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout =
+ test::BuildXmlDom(R"(<fragment class="com.foo.Bar"/>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+
+ std::stringstream out;
+ ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+
+ std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+}
+
+TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="com.foo.Baz"
+ class="com.foo.Bar"/>)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+
+ std::stringstream out;
+ ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+
+ std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+ EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
+}
+
+TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:onClick="bar_method" />)");
+ layout->file.name = test::ParseNameOrDie("layout/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules({}, layout.get(), &set));
+
+ std::stringstream out;
+ ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+
+ std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("bar_method"));
+}
+
+TEST(ProguardRulesTest, MenuRulesAreEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
+ <menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:onClick="on_click"
+ android:actionViewClass="com.foo.Bar"
+ android:actionProviderClass="com.foo.Baz"
+ android:name="com.foo.Bat" />
+ </menu>)");
+ menu->file.name = test::ParseNameOrDie("menu/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules({}, menu.get(), &set));
+
+ std::stringstream out;
+ ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+
+ std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("on_click"));
+ EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+ EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
+ EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 755af0a1c6cc..49639f8ad549 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -19,43 +19,34 @@
#include "ConfigDescription.h"
#include "test/Test.h"
+using ::testing::NotNull;
+
namespace aapt {
TEST(AutoVersionerTest, GenerateVersionedResources) {
const ConfigDescription land_config = test::ParseConfigOrDie("land");
- const ConfigDescription sw600dp_land_config =
- test::ParseConfigOrDie("sw600dp-land");
+ const ConfigDescription sw600dp_land_config = test::ParseConfigOrDie("sw600dp-land");
ResourceEntry entry("foo");
- entry.values.push_back(util::make_unique<ResourceConfigValue>(
- ConfigDescription::DefaultConfig(), ""));
- entry.values.push_back(
- util::make_unique<ResourceConfigValue>(land_config, ""));
- entry.values.push_back(
- util::make_unique<ResourceConfigValue>(sw600dp_land_config, ""));
-
- EXPECT_TRUE(ShouldGenerateVersionedResource(
- &entry, ConfigDescription::DefaultConfig(), 17));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(ConfigDescription::DefaultConfig(), ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(land_config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dp_land_config, ""));
+
+ EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 17));
EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, land_config, 17));
}
TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
- const ConfigDescription sw600dp_v13_config =
- test::ParseConfigOrDie("sw600dp-v13");
+ const ConfigDescription sw600dp_v13_config = test::ParseConfigOrDie("sw600dp-v13");
const ConfigDescription v21_config = test::ParseConfigOrDie("v21");
ResourceEntry entry("foo");
- entry.values.push_back(util::make_unique<ResourceConfigValue>(
- ConfigDescription::DefaultConfig(), ""));
- entry.values.push_back(
- util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
- entry.values.push_back(
- util::make_unique<ResourceConfigValue>(v21_config, ""));
-
- EXPECT_TRUE(ShouldGenerateVersionedResource(
- &entry, ConfigDescription::DefaultConfig(), 17));
- EXPECT_FALSE(ShouldGenerateVersionedResource(
- &entry, ConfigDescription::DefaultConfig(), 22));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(ConfigDescription::DefaultConfig(), ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(v21_config, ""));
+
+ EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 17));
+ EXPECT_FALSE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 22));
}
TEST(AutoVersionerTest, VersionStylesForTable) {
@@ -92,46 +83,28 @@ TEST(AutoVersionerTest, VersionStylesForTable) {
AutoVersioner versioner;
ASSERT_TRUE(versioner.Consume(context.get(), table.get()));
- Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
- test::ParseConfigOrDie("v4"));
- ASSERT_NE(style, nullptr);
+ Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v4"));
+ ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 1u);
- AAPT_ASSERT_TRUE(style->entries.front().key.name);
- EXPECT_EQ(style->entries.front().key.name.value(),
- test::ParseNameOrDie("android:attr/onClick"));
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries.front().key.name);
- style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
- test::ParseConfigOrDie("v13"));
- ASSERT_NE(style, nullptr);
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v13"));
+ ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 2u);
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(style->entries[0].key.name.value(),
- test::ParseNameOrDie("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"));
-
- style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
- test::ParseConfigOrDie("v17"));
- ASSERT_NE(style, nullptr);
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")),style->entries[0].key.name);
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
+
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v17"));
+ ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 3u);
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(style->entries[0].key.name.value(),
- test::ParseNameOrDie("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"));
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(style->entries[2].key.name.value(),
- test::ParseNameOrDie("android:attr/paddingStart"));
-
- style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
- test::ParseConfigOrDie("v21"));
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(style->entries.size(), 1u);
- AAPT_ASSERT_TRUE(style->entries.front().key.name);
- EXPECT_EQ(style->entries.front().key.name.value(),
- test::ParseNameOrDie("android:attr/paddingEnd"));
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries[0].key.name);
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingStart")), style->entries[2].key.name);
+
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v21"));
+ ASSERT_THAT(style, NotNull());
+ ASSERT_EQ(1u, style->entries.size());
+ EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingEnd")), style->entries.front().key.name);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 012bb5ecdeca..6fb1793d90ed 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -310,6 +310,9 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
+ manifest_action["key-sets"]["key-set"]["public-key"];
+ manifest_action["key-sets"]["upgrade-key-set"];
+
// Application actions.
xml::XmlNodeAction& application_action = manifest_action["application"];
application_action.Action(OptionalNameIsJavaClassName);
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 064d3650065d..da7f410b8b08 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -18,7 +18,8 @@
#include "test/Test.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::testing::NotNull;
namespace aapt {
@@ -121,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);
@@ -140,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);
@@ -159,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);
@@ -176,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);
@@ -198,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);
@@ -247,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;
@@ -296,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 =
@@ -320,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 =
@@ -343,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);
@@ -420,4 +421,22 @@ TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) {
EXPECT_EQ(nullptr, Verify(input));
}
+TEST_F(ManifestFixerTest, SupportKeySets) {
+ std::string input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <key-sets>
+ <key-set android:name="old-set">
+ <public-key android:name="old-key" android:value="some+old+key" />
+ </key-set>
+ <key-set android:name="new-set">
+ <public-key android:name="new-key" android:value="some+new+key" />
+ </key-set>
+ <upgrade-key-set android:name="old-set" />
+ <upgrade-key-set android:name="new-set" />
+ </key-sets>
+ </manifest>)";
+ EXPECT_THAT(Verify(input), NotNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index a8e510cd6140..414e56eb5dcc 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -109,18 +109,15 @@ class ReferenceLinkerVisitor : public ValueVisitor {
entry.value->Accept(this);
// Now verify that the type of this item is compatible with the
- // attribute it
- // is defined for. We pass `nullptr` as the DiagMessage so that this
- // check is
- // fast and we avoid creating a DiagMessage when the match is
- // successful.
- if (!symbol->attribute->Matches(entry.value.get(), nullptr)) {
+ // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is fast and we avoid creating a DiagMessage when the match is successful.
+ if (!symbol->attribute->Matches(*entry.value, nullptr)) {
// The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
// Call the matches method again, this time with a DiagMessage so we
// fill in the actual error message.
- symbol->attribute->Matches(entry.value.get(), &msg);
+ symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index d8e33a42711a..72a91689e392 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -19,6 +19,7 @@
#include "test/Test.h"
using android::ResTable_map;
+using ::testing::NotNull;
namespace aapt {
@@ -54,18 +55,18 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) {
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
Reference* ref = test::GetValue<Reference>(table.get(), "com.app.test:string/foo");
- ASSERT_NE(nullptr, ref);
- AAPT_ASSERT_TRUE(ref->id);
+ ASSERT_THAT(ref, NotNull());
+ ASSERT_TRUE(ref->id);
EXPECT_EQ(ResourceId(0x7f020001), ref->id.value());
ref = test::GetValue<Reference>(table.get(), "com.app.test:string/bar");
- ASSERT_NE(nullptr, ref);
- AAPT_ASSERT_TRUE(ref->id);
+ ASSERT_THAT(ref, NotNull());
+ ASSERT_TRUE(ref->id);
EXPECT_EQ(ResourceId(0x7f020002), ref->id.value());
ref = test::GetValue<Reference>(table.get(), "com.app.test:string/baz");
- ASSERT_NE(nullptr, ref);
- AAPT_ASSERT_TRUE(ref->id);
+ ASSERT_THAT(ref, NotNull());
+ ASSERT_TRUE(ref->id);
EXPECT_EQ(ResourceId(0x01040034), ref->id.value());
}
@@ -84,10 +85,9 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
{
// We need to fill in the value for the attribute android:attr/bar after we
- // build the
- // table, because we need access to the string pool.
+ // build the table, because we need access to the string pool.
Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(nullptr, style);
+ ASSERT_THAT(style, NotNull());
style->entries.back().value =
util::make_unique<RawString>(table->string_pool.MakeRef("one|two"));
}
@@ -118,20 +118,20 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().id);
+ ASSERT_THAT(style, NotNull());
+ ASSERT_TRUE(style->parent);
+ ASSERT_TRUE(style->parent.value().id);
EXPECT_EQ(ResourceId(0x01060000), style->parent.value().id.value());
ASSERT_EQ(2u, style->entries.size());
- AAPT_ASSERT_TRUE(style->entries[0].key.id);
+ ASSERT_TRUE(style->entries[0].key.id);
EXPECT_EQ(ResourceId(0x01010001), style->entries[0].key.id.value());
- ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(style->entries[0].value.get()));
+ ASSERT_THAT(ValueCast<BinaryPrimitive>(style->entries[0].value.get()), NotNull());
- AAPT_ASSERT_TRUE(style->entries[1].key.id);
+ ASSERT_TRUE(style->entries[1].key.id);
EXPECT_EQ(ResourceId(0x01010002), style->entries[1].key.id.value());
- ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(style->entries[1].value.get()));
+ ASSERT_THAT(ValueCast<BinaryPrimitive>(style->entries[1].value.get()), NotNull());
}
TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
@@ -165,9 +165,9 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(nullptr, style);
+ ASSERT_THAT(style, NotNull());
ASSERT_EQ(1u, style->entries.size());
- AAPT_ASSERT_TRUE(style->entries.front().key.id);
+ ASSERT_TRUE(style->entries.front().key.id);
EXPECT_EQ(ResourceId(0x7f010000), style->entries.front().key.id.value());
}
@@ -266,7 +266,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
*test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
- ASSERT_NE(nullptr, symbol);
+ ASSERT_THAT(symbol, NotNull());
EXPECT_TRUE(error.empty());
}
@@ -283,12 +283,12 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
std::string error;
const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
- AAPT_EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
+ EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
*test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
EXPECT_FALSE(error.empty());
error = "";
- AAPT_ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
+ ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
*test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
EXPECT_TRUE(error.empty());
}
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 de81e73f613e..ef99355e5b5f 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -18,6 +18,9 @@
#include "test/Test.h"
+using ::testing::IsNull;
+using ::testing::NotNull;
+
namespace aapt {
class XmlReferenceLinkerTest : public ::testing::Test {
@@ -76,199 +79,179 @@ 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());
- ASSERT_NE(nullptr, view_el);
+ xml::Element* view_el = doc->root.get();
+ ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id.value());
- ASSERT_NE(nullptr, xml_attr->compiled_value);
- ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()));
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
+ EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id.value());
- ASSERT_NE(nullptr, xml_attr->compiled_value);
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_NE(nullptr, ref);
- AAPT_ASSERT_TRUE(ref->name);
- EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name.value()); // Make sure the name
- // didn't change.
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ResourceId(0x7f020000), ref->id.value());
+ ASSERT_THAT(ref, NotNull());
+ EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name); // Make sure the name
+ // didn't change.
+ EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- ASSERT_FALSE(xml_attr->compiled_value); // Strings don't get compiled for memory sake.
+ ASSERT_THAT(xml_attr, NotNull());
+ EXPECT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_THAT(xml_attr->compiled_value, IsNull()); // Strings don't get compiled for memory sake.
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- ASSERT_FALSE(xml_attr->compiled_value); // Should be a plain string.
+ ASSERT_THAT(xml_attr, NotNull());
+ EXPECT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_THAT(xml_attr->compiled_value, IsNull()); // Should be a plain string.
xml_attr = view_el->FindAttribute("", "nonAaptAttr");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
- ASSERT_NE(nullptr, xml_attr->compiled_value);
- ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()));
+ ASSERT_THAT(xml_attr, NotNull());
+ EXPECT_FALSE(xml_attr->compiled_attribute);
+ EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
xml_attr = view_el->FindAttribute("", "nonAaptAttrRef");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
- ASSERT_NE(nullptr, xml_attr->compiled_value);
- ASSERT_NE(nullptr, ValueCast<Reference>(xml_attr->compiled_value.get()));
+ ASSERT_THAT(xml_attr, NotNull());
+ EXPECT_FALSE(xml_attr->compiled_attribute);
+ EXPECT_THAT(ValueCast<Reference>(xml_attr->compiled_value.get()), NotNull());
xml_attr = view_el->FindAttribute("", "class");
- ASSERT_NE(nullptr, xml_attr);
- AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
- ASSERT_EQ(nullptr, xml_attr->compiled_value);
+ ASSERT_THAT(xml_attr, NotNull());
+ EXPECT_FALSE(xml_attr->compiled_attribute);
+ EXPECT_THAT(xml_attr->compiled_value, IsNull());
}
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());
- ASSERT_NE(view_el, nullptr);
+ xml::Element* view_el = doc->root.get();
+ ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr =
view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
- ASSERT_NE(xml_attr, nullptr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010001));
- ASSERT_NE(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), nullptr);
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
+ EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
}
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());
- ASSERT_NE(view_el, nullptr);
+ xml::Element* view_el = doc->root.get();
+ ASSERT_THAT(view_el, NotNull());
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
- ASSERT_NE(xml_attr, nullptr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010000));
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+ ASSERT_THAT(ref, NotNull());
+ ASSERT_TRUE(ref->name);
+ EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
}
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());
- ASSERT_NE(view_el, nullptr);
+ xml::Element* view_el = doc->root.get();
+ ASSERT_THAT(view_el, NotNull());
// All attributes and references in this element should be referring to
// "android" (0x01).
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
- ASSERT_NE(xml_attr, nullptr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x01010002));
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
+ ASSERT_THAT(ref, NotNull());
+ EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
ASSERT_FALSE(view_el->GetChildElements().empty());
view_el = view_el->GetChildElements().front();
- ASSERT_NE(view_el, nullptr);
+ ASSERT_THAT(view_el, NotNull());
// All attributes and references in this element should be referring to
// "com.app.test" (0x7f).
xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
- ASSERT_NE(xml_attr, nullptr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010002));
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ ASSERT_THAT(ref, NotNull());
+ EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
}
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());
- ASSERT_NE(view_el, nullptr);
+ xml::Element* view_el = doc->root.get();
+ ASSERT_THAT(view_el, NotNull());
// All attributes and references in this element should be referring to
// "com.app.test" (0x7f).
- xml::Attribute* xml_attr =
- view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
- ASSERT_NE(xml_attr, nullptr);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
- AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
- EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x7f010002));
+ xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ ASSERT_THAT(ref, NotNull());
+ EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
}
} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
index 38bf4e3bd8eb..aa99c982f6ae 100644
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -18,8 +18,7 @@
namespace aapt {
-void SerializeStringPoolToPb(const StringPool& pool,
- pb::StringPool* out_pb_pool) {
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
BigBuffer buffer(1024);
StringPool::FlattenUtf8(&buffer, pool);
@@ -28,51 +27,47 @@ void SerializeStringPoolToPb(const StringPool& pool,
size_t offset = 0;
for (const BigBuffer::Block& block : buffer) {
- data->insert(data->begin() + offset, block.buffer.get(),
- block.buffer.get() + block.size);
+ data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
offset += block.size;
}
}
-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) {
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()));
}
}
-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) {
if (pb_source.has_path_idx()) {
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;
@@ -80,8 +75,7 @@ SymbolState DeserializeVisibilityFromPb(
return SymbolState::kUndefined;
}
-void SerializeConfig(const ConfigDescription& config,
- pb::ConfigDescription* out_pb_config) {
+void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) {
android::ResTable_config flat_config = config;
flat_config.size = sizeof(flat_config);
flat_config.swapHtoD();
@@ -99,8 +93,7 @@ bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
return false;
}
- config = reinterpret_cast<const android::ResTable_config*>(
- pb_config.data().data());
+ config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data());
out_config->copyFromDtoH(*config);
return true;
}
@@ -108,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;
@@ -132,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 4b5619235c06..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,46 +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::kStylePriority, 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");
@@ -223,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);
@@ -236,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)) {
@@ -256,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)) {
@@ -289,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);
@@ -300,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 {};
}
@@ -338,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;
}
@@ -351,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;
}
@@ -378,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 {};
}
@@ -441,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;
@@ -457,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 d87d64e1cb46..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 {
@@ -87,7 +87,9 @@ class PbSerializerVisitor : public RawValueVisitor {
pb_prim->set_data(val.data);
}
- void VisitItem(Item* item) override { LOG(FATAL) << "unimplemented item"; }
+ void VisitItem(Item* item) override {
+ LOG(FATAL) << "unimplemented item";
+ }
void Visit(Attribute* attr) override {
pb::Attribute* pb_attr = pb_compound_value()->mutable_attr();
@@ -96,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);
@@ -112,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);
}
}
@@ -125,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());
}
@@ -133,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);
}
}
@@ -151,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);
}
}
@@ -177,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());
}
@@ -190,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);
@@ -199,7 +198,6 @@ class PbSerializerVisitor : public RawValueVisitor {
}
StringPool* source_pool_;
- StringPool* symbol_pool_;
pb::Value* out_pb_value_;
pb::Item* out_pb_item_;
};
@@ -207,41 +205,35 @@ class PbSerializerVisitor : public RawValueVisitor {
} // namespace
std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may
- // change.
- table->string_pool.Sort(
- [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- int diff = a.context.priority - b.context.priority;
- if (diff < 0) return true;
- if (diff > 0) return false;
- diff = a.context.config.compare(b.context.config);
- if (diff < 0) return true;
- if (diff > 0) return false;
- return a.value < b.value;
- });
+ // We must do this before writing the resources, since the string pool IDs may change.
table->string_pool.Prune();
+ table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+ int diff = util::compare(a.priority, b.priority);
+ if (diff == 0) {
+ diff = a.config.compare(b.config);
+ }
+ return diff;
+ });
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());
}
@@ -255,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);
@@ -272,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);
}
}
@@ -280,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;
@@ -315,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_);
@@ -336,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) {}
@@ -354,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;
@@ -381,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 e6ce6d37b879..80608b3d9c05 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -20,6 +20,9 @@
#include "test/Test.h"
using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::SizeIs;
namespace aapt {
@@ -37,21 +40,29 @@ 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>();
- plural->values[Plural::One] =
- util::make_unique<String>(table->string_pool.MakeRef("one"));
+ plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"),
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);
@@ -77,44 +87,53 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
context->GetDiagnostics()));
std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table.get());
- ASSERT_NE(nullptr, pb_table);
+ ASSERT_THAT(pb_table, NotNull());
std::unique_ptr<ResourceTable> new_table = DeserializeTableFromPb(
*pb_table, Source{"test"}, context->GetDiagnostics());
- ASSERT_NE(nullptr, new_table);
+ ASSERT_THAT(new_table, NotNull());
Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
- ASSERT_NE(nullptr, new_id);
- EXPECT_EQ(id->IsWeak(), new_id->IsWeak());
+ ASSERT_THAT(new_id, NotNull());
+ EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
Maybe<ResourceTable::SearchResult> result =
new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
- AAPT_ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
+ ASSERT_TRUE(result);
+
+ 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_NE(nullptr, prim);
- EXPECT_EQ(123u, prim->value.data);
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(123u));
prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
- ASSERT_NE(nullptr, prim);
- EXPECT_EQ(321u, prim->value.data);
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(321u));
Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
- ASSERT_NE(nullptr, actual_ref);
- AAPT_ASSERT_TRUE(actual_ref->name);
- AAPT_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());
+ ASSERT_THAT(actual_ref, NotNull());
+ ASSERT_TRUE(actual_ref->name);
+ ASSERT_TRUE(actual_ref->id);
+ 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,12 +173,12 @@ 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(
new_pb_file, Source("test"), context->GetDiagnostics());
- ASSERT_NE(nullptr, file);
+ ASSERT_THAT(file, NotNull());
uint64_t offset, len;
ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
@@ -171,16 +190,14 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
EXPECT_EQ(0u, offset & 0x03);
ASSERT_EQ(1u, file->exported_symbols.size());
- EXPECT_EQ(test::ParseNameOrDie("id/unchecked"),
- file->exported_symbols[0].name);
+ EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), file->exported_symbols[0].name);
// Read the second compiled file.
ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
- file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"),
- context->GetDiagnostics());
- ASSERT_NE(nullptr, file);
+ file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"), context->GetDiagnostics());
+ ASSERT_THAT(file, NotNull());
ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
@@ -193,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";
@@ -215,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 0290e30dfced..8368f9d16af8 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,9 +1,45 @@
# 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)
+- Fixed issue where ints and floats with trailing whitespace would not be parsed. (bug 62902869)
+- Fixed issue where `--custom-package` was not honored when writing Manifest.java. (bug 62826426)
+- Add `<key-sets>` and its nested tags to the allowed set of XML tags in AndroidManifest.xml.
+ (bug 62839863)
+- Fixed issue where Java classes referenced from fragments and menus were not added to
+ the set of Proguard keep rules. (bug 62216174)
+- Fixed issue where escaped unicode characters would generate malformed UTF-8. (bug 62839202)
+- Fixed issue where apostrophes or quotes used in XML attribute values were ignored.
+ (bug 62840406, 62840718)
+
## Version 2.17
-### `aapt2 compile ...`
-- Fixed an issue where symlinks would not be followed when compiling PNGs. (bug 62144459)
+### `aapt2 ...`
+- Fixed issue where symlinks would not be followed when compiling PNGs. (bug 62144459)
- Fixed issue where overlays that declared `<add-resource>` did not compile. (bug 38355988)
+- Fixed issue where `%n` in a string resource was interpreted as a format argument. (bug 37132275)
+- Allow empty resources to compile, giving them a value of `""` or `@null`, depending on the
+ accepted formats. (bug 38425050)
+- Resources declared via `<item>` with no format attribute were changed to accept all
+ resource types. (bug 62260121)
+- Allow `<layout>` element under `<activity>` in AndroidManifest.xml. (bug 62189611)
+- Fix issue where `--no-version-vector` did not apply to `pathInterpolator` and `objectAnimator`.
+ (bug 62211148)
+- Fix issue where overlaid `<style>` would not be merged, and would replace the original resource
+ instead. This fix brings behavior in-line with AAPT. (bug 38355988)
## Version 2.16
### `aapt2 link ...`
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 f3da780cacbd..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,26 +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);
@@ -235,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);
@@ -250,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 8efd56a7514b..e6b38c0007b4 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -34,15 +34,6 @@
#include "io/File.h"
#include "process/IResourceTableConsumer.h"
-//
-// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to
-// fail to compile.
-//
-#define AAPT_ASSERT_TRUE(v) ASSERT_TRUE(bool(v))
-#define AAPT_ASSERT_FALSE(v) ASSERT_FALSE(bool(v))
-#define AAPT_EXPECT_TRUE(v) EXPECT_TRUE(bool(v))
-#define AAPT_EXPECT_FALSE(v) EXPECT_FALSE(bool(v))
-
namespace aapt {
namespace test {
@@ -151,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/text/Unicode.cpp b/tools/aapt2/text/Unicode.cpp
new file mode 100644
index 000000000000..75eeb46c7f5e
--- /dev/null
+++ b/tools/aapt2/text/Unicode.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Unicode.h"
+
+#include <algorithm>
+#include <array>
+
+#include "text/Utf8Iterator.h"
+
+using ::android::StringPiece;
+
+namespace aapt {
+namespace text {
+
+namespace {
+
+struct CharacterProperties {
+ enum : uint32_t {
+ kXidStart = 1 << 0,
+ kXidContinue = 1 << 1,
+ };
+
+ char32_t first_char;
+ char32_t last_char;
+ uint32_t properties;
+};
+
+// Incude the generated data table.
+#include "text/Unicode_data.cpp"
+
+bool CompareCharacterProperties(const CharacterProperties& a, char32_t codepoint) {
+ return a.last_char < codepoint;
+}
+
+uint32_t FindCharacterProperties(char32_t codepoint) {
+ const auto iter_end = sCharacterProperties.end();
+ const auto iter = std::lower_bound(sCharacterProperties.begin(), iter_end, codepoint,
+ CompareCharacterProperties);
+ if (iter != iter_end && codepoint >= iter->first_char) {
+ return iter->properties;
+ }
+ return 0u;
+}
+
+} // namespace
+
+bool IsXidStart(char32_t codepoint) {
+ return FindCharacterProperties(codepoint) & CharacterProperties::kXidStart;
+}
+
+bool IsXidContinue(char32_t codepoint) {
+ return FindCharacterProperties(codepoint) & CharacterProperties::kXidContinue;
+}
+
+// Hardcode the White_Space characters since they are few and the external/icu project doesn't
+// list them as data files to parse.
+// Sourced from http://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
+bool IsWhitespace(char32_t codepoint) {
+ return (codepoint >= 0x0009 && codepoint <= 0x000d) || (codepoint == 0x0020) ||
+ (codepoint == 0x0085) || (codepoint == 0x00a0) || (codepoint == 0x1680) ||
+ (codepoint >= 0x2000 && codepoint <= 0x200a) || (codepoint == 0x2028) ||
+ (codepoint == 0x2029) || (codepoint == 0x202f) || (codepoint == 0x205f) ||
+ (codepoint == 0x3000);
+}
+
+bool IsJavaIdentifier(const StringPiece& str) {
+ Utf8Iterator iter(str);
+
+ // Check the first character.
+ if (!iter.HasNext()) {
+ return false;
+ }
+
+ if (!IsXidStart(iter.Next())) {
+ return false;
+ }
+
+ while (iter.HasNext()) {
+ const char32_t codepoint = iter.Next();
+ if (!IsXidContinue(codepoint) && codepoint != U'$') {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IsValidResourceEntryName(const StringPiece& str) {
+ Utf8Iterator iter(str);
+
+ // Check the first character.
+ if (!iter.HasNext()) {
+ return false;
+ }
+
+ // Resources are allowed to start with '_'
+ const char32_t first_codepoint = iter.Next();
+ if (!IsXidStart(first_codepoint) && first_codepoint != U'_') {
+ return false;
+ }
+
+ while (iter.HasNext()) {
+ const char32_t codepoint = iter.Next();
+ if (!IsXidContinue(codepoint) && codepoint != U'.' && codepoint != U'-') {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace text
+} // namespace aapt
diff --git a/tools/aapt2/text/Unicode.h b/tools/aapt2/text/Unicode.h
new file mode 100644
index 000000000000..546714e9a290
--- /dev/null
+++ b/tools/aapt2/text/Unicode.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_TEXT_UNICODE_H
+#define AAPT_TEXT_UNICODE_H
+
+#include "androidfw/StringPiece.h"
+
+namespace aapt {
+namespace text {
+
+// Returns true if the Unicode codepoint has the XID_Start property, meaning it can be used as the
+// first character of a programming language identifier.
+// http://unicode.org/reports/tr31/#Default_Identifier_Syntax
+//
+// XID_Start is a Unicode Derived Core Property. It is a variation of the ID_Start
+// Derived Core Property, accounting for a few characters that, when normalized, yield valid
+// characters in the ID_Start set.
+bool IsXidStart(char32_t codepoint);
+
+// Returns true if the Unicode codepoint has the XID_Continue property, meaning it can be used in
+// any position of a programming language identifier, except the first.
+// http://unicode.org/reports/tr31/#Default_Identifier_Syntax
+//
+// XID_Continue is a Unicode Derived Core Property. It is a variation of the ID_Continue
+// Derived Core Property, accounting for a few characters that, when normalized, yield valid
+// characters in the ID_Continue set.
+bool IsXidContinue(char32_t codepoint);
+
+// Returns true if the Unicode codepoint has the White_Space property.
+// http://unicode.org/reports/tr44/#White_Space
+bool IsWhitespace(char32_t codepoint);
+
+// Returns true if the UTF8 string can be used as a Java identifier.
+// NOTE: This does not check against the set of reserved Java keywords.
+bool IsJavaIdentifier(const android::StringPiece& str);
+
+// Returns true if the UTF8 string can be used as the entry name of a resource name.
+// This is the `entry` part of package:type/entry.
+bool IsValidResourceEntryName(const android::StringPiece& str);
+
+} // namespace text
+} // namespace aapt
+
+#endif // AAPT_TEXT_UNICODE_H
diff --git a/tools/aapt2/text/Unicode_data.cpp b/tools/aapt2/text/Unicode_data.cpp
new file mode 100644
index 000000000000..96dc57b84031
--- /dev/null
+++ b/tools/aapt2/text/Unicode_data.cpp
@@ -0,0 +1,629 @@
+/*
+ * 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.
+ */
+
+const static std::array<CharacterProperties, 611> sCharacterProperties = {{
+ {0x0030, 0x0039, CharacterProperties::kXidContinue},
+ {0x0041, 0x005a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x005f, 0x005f, CharacterProperties::kXidContinue},
+ {0x0061, 0x007a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00aa, 0x00aa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00b5, 0x00b5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00b7, 0x00b7, CharacterProperties::kXidContinue},
+ {0x00ba, 0x00ba, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00c0, 0x00d6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00d8, 0x00f6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x00f8, 0x02c1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x02c6, 0x02d1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x02e0, 0x02e4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x02ec, 0x02ec, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x02ee, 0x02ee, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0300, 0x036f, CharacterProperties::kXidContinue},
+ {0x0370, 0x0374, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0376, 0x0377, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x037b, 0x037d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x037f, 0x037f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0386, 0x0386, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0387, 0x0387, CharacterProperties::kXidContinue},
+ {0x0388, 0x038a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x038c, 0x038c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x038e, 0x03a1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x03a3, 0x03f5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x03f7, 0x0481, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0483, 0x0487, CharacterProperties::kXidContinue},
+ {0x048a, 0x052f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0531, 0x0556, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0559, 0x0559, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0561, 0x0587, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0591, 0x05bd, CharacterProperties::kXidContinue},
+ {0x05bf, 0x05bf, CharacterProperties::kXidContinue},
+ {0x05c1, 0x05c2, CharacterProperties::kXidContinue},
+ {0x05c4, 0x05c5, CharacterProperties::kXidContinue},
+ {0x05c7, 0x05c7, CharacterProperties::kXidContinue},
+ {0x05d0, 0x05ea, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x05f0, 0x05f2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0610, 0x061a, CharacterProperties::kXidContinue},
+ {0x0620, 0x064a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x064b, 0x0669, CharacterProperties::kXidContinue},
+ {0x066e, 0x066f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0670, 0x0670, CharacterProperties::kXidContinue},
+ {0x0671, 0x06d3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x06d5, 0x06d5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x06d6, 0x06dc, CharacterProperties::kXidContinue},
+ {0x06df, 0x06e4, CharacterProperties::kXidContinue},
+ {0x06e5, 0x06e6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x06e7, 0x06e8, CharacterProperties::kXidContinue},
+ {0x06ea, 0x06ed, CharacterProperties::kXidContinue},
+ {0x06ee, 0x06ef, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x06f0, 0x06f9, CharacterProperties::kXidContinue},
+ {0x06fa, 0x06fc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x06ff, 0x06ff, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0710, 0x0710, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0711, 0x0711, CharacterProperties::kXidContinue},
+ {0x0712, 0x072f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0730, 0x074a, CharacterProperties::kXidContinue},
+ {0x074d, 0x07a5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x07a6, 0x07b0, CharacterProperties::kXidContinue},
+ {0x07b1, 0x07b1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x07c0, 0x07c9, CharacterProperties::kXidContinue},
+ {0x07ca, 0x07ea, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x07eb, 0x07f3, CharacterProperties::kXidContinue},
+ {0x07f4, 0x07f5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x07fa, 0x07fa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0800, 0x0815, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0816, 0x0819, CharacterProperties::kXidContinue},
+ {0x081a, 0x081a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x081b, 0x0823, CharacterProperties::kXidContinue},
+ {0x0824, 0x0824, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0825, 0x0827, CharacterProperties::kXidContinue},
+ {0x0828, 0x0828, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0829, 0x082d, CharacterProperties::kXidContinue},
+ {0x0840, 0x0858, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0859, 0x085b, CharacterProperties::kXidContinue},
+ {0x08a0, 0x08b4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x08b6, 0x08bd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x08d4, 0x08e1, CharacterProperties::kXidContinue},
+ {0x08e3, 0x0903, CharacterProperties::kXidContinue},
+ {0x0904, 0x0939, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x093a, 0x093c, CharacterProperties::kXidContinue},
+ {0x093d, 0x093d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x093e, 0x094f, CharacterProperties::kXidContinue},
+ {0x0950, 0x0950, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0951, 0x0957, CharacterProperties::kXidContinue},
+ {0x0958, 0x0961, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0962, 0x0963, CharacterProperties::kXidContinue},
+ {0x0966, 0x096f, CharacterProperties::kXidContinue},
+ {0x0971, 0x0980, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0981, 0x0983, CharacterProperties::kXidContinue},
+ {0x0985, 0x098c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x098f, 0x0990, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0993, 0x09a8, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09aa, 0x09b0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09b2, 0x09b2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09b6, 0x09b9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09bc, 0x09bc, CharacterProperties::kXidContinue},
+ {0x09bd, 0x09bd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09be, 0x09c4, CharacterProperties::kXidContinue},
+ {0x09c7, 0x09c8, CharacterProperties::kXidContinue},
+ {0x09cb, 0x09cd, CharacterProperties::kXidContinue},
+ {0x09ce, 0x09ce, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09d7, 0x09d7, CharacterProperties::kXidContinue},
+ {0x09dc, 0x09dd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09df, 0x09e1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x09e2, 0x09e3, CharacterProperties::kXidContinue},
+ {0x09e6, 0x09ef, CharacterProperties::kXidContinue},
+ {0x09f0, 0x09f1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a01, 0x0a03, CharacterProperties::kXidContinue},
+ {0x0a05, 0x0a0a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a0f, 0x0a10, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a13, 0x0a28, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a2a, 0x0a30, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a32, 0x0a33, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a35, 0x0a36, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a38, 0x0a39, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a3c, 0x0a3c, CharacterProperties::kXidContinue},
+ {0x0a3e, 0x0a42, CharacterProperties::kXidContinue},
+ {0x0a47, 0x0a48, CharacterProperties::kXidContinue},
+ {0x0a4b, 0x0a4d, CharacterProperties::kXidContinue},
+ {0x0a51, 0x0a51, CharacterProperties::kXidContinue},
+ {0x0a59, 0x0a5c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a5e, 0x0a5e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a66, 0x0a71, CharacterProperties::kXidContinue},
+ {0x0a72, 0x0a74, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a75, 0x0a75, CharacterProperties::kXidContinue},
+ {0x0a81, 0x0a83, CharacterProperties::kXidContinue},
+ {0x0a85, 0x0a8d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a8f, 0x0a91, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0a93, 0x0aa8, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0aaa, 0x0ab0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ab2, 0x0ab3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ab5, 0x0ab9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0abc, 0x0abc, CharacterProperties::kXidContinue},
+ {0x0abd, 0x0abd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0abe, 0x0ac5, CharacterProperties::kXidContinue},
+ {0x0ac7, 0x0ac9, CharacterProperties::kXidContinue},
+ {0x0acb, 0x0acd, CharacterProperties::kXidContinue},
+ {0x0ad0, 0x0ad0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ae0, 0x0ae1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ae2, 0x0ae3, CharacterProperties::kXidContinue},
+ {0x0ae6, 0x0aef, CharacterProperties::kXidContinue},
+ {0x0af9, 0x0af9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b01, 0x0b03, CharacterProperties::kXidContinue},
+ {0x0b05, 0x0b0c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b0f, 0x0b10, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b13, 0x0b28, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b2a, 0x0b30, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b32, 0x0b33, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b35, 0x0b39, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b3c, 0x0b3c, CharacterProperties::kXidContinue},
+ {0x0b3d, 0x0b3d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b3e, 0x0b44, CharacterProperties::kXidContinue},
+ {0x0b47, 0x0b48, CharacterProperties::kXidContinue},
+ {0x0b4b, 0x0b4d, CharacterProperties::kXidContinue},
+ {0x0b56, 0x0b57, CharacterProperties::kXidContinue},
+ {0x0b5c, 0x0b5d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b5f, 0x0b61, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b62, 0x0b63, CharacterProperties::kXidContinue},
+ {0x0b66, 0x0b6f, CharacterProperties::kXidContinue},
+ {0x0b71, 0x0b71, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b82, 0x0b82, CharacterProperties::kXidContinue},
+ {0x0b83, 0x0b83, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b85, 0x0b8a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b8e, 0x0b90, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b92, 0x0b95, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b99, 0x0b9a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b9c, 0x0b9c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0b9e, 0x0b9f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ba3, 0x0ba4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ba8, 0x0baa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0bae, 0x0bb9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0bbe, 0x0bc2, CharacterProperties::kXidContinue},
+ {0x0bc6, 0x0bc8, CharacterProperties::kXidContinue},
+ {0x0bca, 0x0bcd, CharacterProperties::kXidContinue},
+ {0x0bd0, 0x0bd0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0bd7, 0x0bd7, CharacterProperties::kXidContinue},
+ {0x0be6, 0x0bef, CharacterProperties::kXidContinue},
+ {0x0c00, 0x0c03, CharacterProperties::kXidContinue},
+ {0x0c05, 0x0c0c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c0e, 0x0c10, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c12, 0x0c28, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c2a, 0x0c39, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c3d, 0x0c3d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c3e, 0x0c44, CharacterProperties::kXidContinue},
+ {0x0c46, 0x0c48, CharacterProperties::kXidContinue},
+ {0x0c4a, 0x0c4d, CharacterProperties::kXidContinue},
+ {0x0c55, 0x0c56, CharacterProperties::kXidContinue},
+ {0x0c58, 0x0c5a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c60, 0x0c61, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c62, 0x0c63, CharacterProperties::kXidContinue},
+ {0x0c66, 0x0c6f, CharacterProperties::kXidContinue},
+ {0x0c80, 0x0c80, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c81, 0x0c83, CharacterProperties::kXidContinue},
+ {0x0c85, 0x0c8c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c8e, 0x0c90, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0c92, 0x0ca8, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0caa, 0x0cb3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0cb5, 0x0cb9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0cbc, 0x0cbc, CharacterProperties::kXidContinue},
+ {0x0cbd, 0x0cbd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0cbe, 0x0cc4, CharacterProperties::kXidContinue},
+ {0x0cc6, 0x0cc8, CharacterProperties::kXidContinue},
+ {0x0cca, 0x0ccd, CharacterProperties::kXidContinue},
+ {0x0cd5, 0x0cd6, CharacterProperties::kXidContinue},
+ {0x0cde, 0x0cde, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ce0, 0x0ce1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ce2, 0x0ce3, CharacterProperties::kXidContinue},
+ {0x0ce6, 0x0cef, CharacterProperties::kXidContinue},
+ {0x0cf1, 0x0cf2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d01, 0x0d03, CharacterProperties::kXidContinue},
+ {0x0d05, 0x0d0c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d0e, 0x0d10, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d12, 0x0d3a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d3d, 0x0d3d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d3e, 0x0d44, CharacterProperties::kXidContinue},
+ {0x0d46, 0x0d48, CharacterProperties::kXidContinue},
+ {0x0d4a, 0x0d4d, CharacterProperties::kXidContinue},
+ {0x0d4e, 0x0d4e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d54, 0x0d56, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d57, 0x0d57, CharacterProperties::kXidContinue},
+ {0x0d5f, 0x0d61, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d62, 0x0d63, CharacterProperties::kXidContinue},
+ {0x0d66, 0x0d6f, CharacterProperties::kXidContinue},
+ {0x0d7a, 0x0d7f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d82, 0x0d83, CharacterProperties::kXidContinue},
+ {0x0d85, 0x0d96, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0d9a, 0x0db1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0db3, 0x0dbb, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0dbd, 0x0dbd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0dc0, 0x0dc6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0dca, 0x0dca, CharacterProperties::kXidContinue},
+ {0x0dcf, 0x0dd4, CharacterProperties::kXidContinue},
+ {0x0dd6, 0x0dd6, CharacterProperties::kXidContinue},
+ {0x0dd8, 0x0ddf, CharacterProperties::kXidContinue},
+ {0x0de6, 0x0def, CharacterProperties::kXidContinue},
+ {0x0df2, 0x0df3, CharacterProperties::kXidContinue},
+ {0x0e01, 0x0e30, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e31, 0x0e31, CharacterProperties::kXidContinue},
+ {0x0e32, 0x0e32, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e33, 0x0e3a, CharacterProperties::kXidContinue},
+ {0x0e40, 0x0e46, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e47, 0x0e4e, CharacterProperties::kXidContinue},
+ {0x0e50, 0x0e59, CharacterProperties::kXidContinue},
+ {0x0e81, 0x0e82, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e84, 0x0e84, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e87, 0x0e88, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e8a, 0x0e8a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e8d, 0x0e8d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e94, 0x0e97, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0e99, 0x0e9f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ea1, 0x0ea3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ea5, 0x0ea5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ea7, 0x0ea7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0eaa, 0x0eab, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ead, 0x0eb0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0eb1, 0x0eb1, CharacterProperties::kXidContinue},
+ {0x0eb2, 0x0eb2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0eb3, 0x0eb9, CharacterProperties::kXidContinue},
+ {0x0ebb, 0x0ebc, CharacterProperties::kXidContinue},
+ {0x0ebd, 0x0ebd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ec0, 0x0ec4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ec6, 0x0ec6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0ec8, 0x0ecd, CharacterProperties::kXidContinue},
+ {0x0ed0, 0x0ed9, CharacterProperties::kXidContinue},
+ {0x0edc, 0x0edf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0f00, 0x0f00, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0f18, 0x0f19, CharacterProperties::kXidContinue},
+ {0x0f20, 0x0f29, CharacterProperties::kXidContinue},
+ {0x0f35, 0x0f35, CharacterProperties::kXidContinue},
+ {0x0f37, 0x0f37, CharacterProperties::kXidContinue},
+ {0x0f39, 0x0f39, CharacterProperties::kXidContinue},
+ {0x0f3e, 0x0f3f, CharacterProperties::kXidContinue},
+ {0x0f40, 0x0f47, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0f49, 0x0f6c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0f71, 0x0f84, CharacterProperties::kXidContinue},
+ {0x0f86, 0x0f87, CharacterProperties::kXidContinue},
+ {0x0f88, 0x0f8c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x0f8d, 0x0f97, CharacterProperties::kXidContinue},
+ {0x0f99, 0x0fbc, CharacterProperties::kXidContinue},
+ {0x0fc6, 0x0fc6, CharacterProperties::kXidContinue},
+ {0x1000, 0x102a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x102b, 0x103e, CharacterProperties::kXidContinue},
+ {0x103f, 0x103f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1040, 0x1049, CharacterProperties::kXidContinue},
+ {0x1050, 0x1055, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1056, 0x1059, CharacterProperties::kXidContinue},
+ {0x105a, 0x105d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x105e, 0x1060, CharacterProperties::kXidContinue},
+ {0x1061, 0x1061, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1062, 0x1064, CharacterProperties::kXidContinue},
+ {0x1065, 0x1066, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1067, 0x106d, CharacterProperties::kXidContinue},
+ {0x106e, 0x1070, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1071, 0x1074, CharacterProperties::kXidContinue},
+ {0x1075, 0x1081, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1082, 0x108d, CharacterProperties::kXidContinue},
+ {0x108e, 0x108e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x108f, 0x109d, CharacterProperties::kXidContinue},
+ {0x10a0, 0x10c5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x10c7, 0x10c7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x10cd, 0x10cd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x10d0, 0x10fa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x10fc, 0x1248, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x124a, 0x124d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1250, 0x1256, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1258, 0x1258, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x125a, 0x125d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1260, 0x1288, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x128a, 0x128d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1290, 0x12b0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12b2, 0x12b5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12b8, 0x12be, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12c0, 0x12c0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12c2, 0x12c5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12c8, 0x12d6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x12d8, 0x1310, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1312, 0x1315, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1318, 0x135a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x135d, 0x135f, CharacterProperties::kXidContinue},
+ {0x1369, 0x1371, CharacterProperties::kXidContinue},
+ {0x1380, 0x138f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x13a0, 0x13f5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x13f8, 0x13fd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1401, 0x166c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x166f, 0x167f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1681, 0x169a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x16a0, 0x16ea, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x16ee, 0x16f8, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1700, 0x170c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x170e, 0x1711, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1712, 0x1714, CharacterProperties::kXidContinue},
+ {0x1720, 0x1731, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1732, 0x1734, CharacterProperties::kXidContinue},
+ {0x1740, 0x1751, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1752, 0x1753, CharacterProperties::kXidContinue},
+ {0x1760, 0x176c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x176e, 0x1770, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1772, 0x1773, CharacterProperties::kXidContinue},
+ {0x1780, 0x17b3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x17b4, 0x17d3, CharacterProperties::kXidContinue},
+ {0x17d7, 0x17d7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x17dc, 0x17dc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x17dd, 0x17dd, CharacterProperties::kXidContinue},
+ {0x17e0, 0x17e9, CharacterProperties::kXidContinue},
+ {0x180b, 0x180d, CharacterProperties::kXidContinue},
+ {0x1810, 0x1819, CharacterProperties::kXidContinue},
+ {0x1820, 0x1877, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1880, 0x18a8, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x18a9, 0x18a9, CharacterProperties::kXidContinue},
+ {0x18aa, 0x18aa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x18b0, 0x18f5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1900, 0x191e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1920, 0x192b, CharacterProperties::kXidContinue},
+ {0x1930, 0x193b, CharacterProperties::kXidContinue},
+ {0x1946, 0x194f, CharacterProperties::kXidContinue},
+ {0x1950, 0x196d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1970, 0x1974, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1980, 0x19ab, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x19b0, 0x19c9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x19d0, 0x19da, CharacterProperties::kXidContinue},
+ {0x1a00, 0x1a16, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1a17, 0x1a1b, CharacterProperties::kXidContinue},
+ {0x1a20, 0x1a54, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1a55, 0x1a5e, CharacterProperties::kXidContinue},
+ {0x1a60, 0x1a7c, CharacterProperties::kXidContinue},
+ {0x1a7f, 0x1a89, CharacterProperties::kXidContinue},
+ {0x1a90, 0x1a99, CharacterProperties::kXidContinue},
+ {0x1aa7, 0x1aa7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1ab0, 0x1abd, CharacterProperties::kXidContinue},
+ {0x1b00, 0x1b04, CharacterProperties::kXidContinue},
+ {0x1b05, 0x1b33, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1b34, 0x1b44, CharacterProperties::kXidContinue},
+ {0x1b45, 0x1b4b, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1b50, 0x1b59, CharacterProperties::kXidContinue},
+ {0x1b6b, 0x1b73, CharacterProperties::kXidContinue},
+ {0x1b80, 0x1b82, CharacterProperties::kXidContinue},
+ {0x1b83, 0x1ba0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1ba1, 0x1bad, CharacterProperties::kXidContinue},
+ {0x1bae, 0x1baf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1bb0, 0x1bb9, CharacterProperties::kXidContinue},
+ {0x1bba, 0x1be5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1be6, 0x1bf3, CharacterProperties::kXidContinue},
+ {0x1c00, 0x1c23, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1c24, 0x1c37, CharacterProperties::kXidContinue},
+ {0x1c40, 0x1c49, CharacterProperties::kXidContinue},
+ {0x1c4d, 0x1c4f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1c50, 0x1c59, CharacterProperties::kXidContinue},
+ {0x1c5a, 0x1c7d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1c80, 0x1c88, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1cd0, 0x1cd2, CharacterProperties::kXidContinue},
+ {0x1cd4, 0x1ce8, CharacterProperties::kXidContinue},
+ {0x1ce9, 0x1cec, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1ced, 0x1ced, CharacterProperties::kXidContinue},
+ {0x1cee, 0x1cf1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1cf2, 0x1cf4, CharacterProperties::kXidContinue},
+ {0x1cf5, 0x1cf6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1cf8, 0x1cf9, CharacterProperties::kXidContinue},
+ {0x1d00, 0x1dbf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1dc0, 0x1df5, CharacterProperties::kXidContinue},
+ {0x1dfb, 0x1dff, CharacterProperties::kXidContinue},
+ {0x1e00, 0x1f15, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f18, 0x1f1d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f20, 0x1f45, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f48, 0x1f4d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f50, 0x1f57, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f59, 0x1f59, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f5b, 0x1f5b, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f5d, 0x1f5d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f5f, 0x1f7d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1f80, 0x1fb4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fb6, 0x1fbc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fbe, 0x1fbe, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fc2, 0x1fc4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fc6, 0x1fcc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fd0, 0x1fd3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fd6, 0x1fdb, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1fe0, 0x1fec, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1ff2, 0x1ff4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x1ff6, 0x1ffc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x203f, 0x2040, CharacterProperties::kXidContinue},
+ {0x2054, 0x2054, CharacterProperties::kXidContinue},
+ {0x2071, 0x2071, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x207f, 0x207f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2090, 0x209c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x20d0, 0x20dc, CharacterProperties::kXidContinue},
+ {0x20e1, 0x20e1, CharacterProperties::kXidContinue},
+ {0x20e5, 0x20f0, CharacterProperties::kXidContinue},
+ {0x2102, 0x2102, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2107, 0x2107, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x210a, 0x2113, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2115, 0x2115, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2118, 0x211d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2124, 0x2124, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2126, 0x2126, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2128, 0x2128, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x212a, 0x2139, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x213c, 0x213f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2145, 0x2149, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x214e, 0x214e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2160, 0x2188, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2c00, 0x2c2e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2c30, 0x2c5e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2c60, 0x2ce4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2ceb, 0x2cee, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2cef, 0x2cf1, CharacterProperties::kXidContinue},
+ {0x2cf2, 0x2cf3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d00, 0x2d25, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d27, 0x2d27, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d2d, 0x2d2d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d30, 0x2d67, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d6f, 0x2d6f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2d7f, 0x2d7f, CharacterProperties::kXidContinue},
+ {0x2d80, 0x2d96, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2da0, 0x2da6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2da8, 0x2dae, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2db0, 0x2db6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2db8, 0x2dbe, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2dc0, 0x2dc6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2dc8, 0x2dce, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2dd0, 0x2dd6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2dd8, 0x2dde, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x2de0, 0x2dff, CharacterProperties::kXidContinue},
+ {0x3005, 0x3007, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3021, 0x3029, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x302a, 0x302f, CharacterProperties::kXidContinue},
+ {0x3031, 0x3035, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3038, 0x303c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3041, 0x3096, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3099, 0x309a, CharacterProperties::kXidContinue},
+ {0x309d, 0x309f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x30a1, 0x30fa, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x30fc, 0x30ff, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3105, 0x312d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3131, 0x318e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x31a0, 0x31ba, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x31f0, 0x31ff, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x3400, 0x4db5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0x4e00, 0x9fd5, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa000, 0xa48c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa4d0, 0xa4fd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa500, 0xa60c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa610, 0xa61f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa620, 0xa629, CharacterProperties::kXidContinue},
+ {0xa62a, 0xa62b, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa640, 0xa66e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa66f, 0xa66f, CharacterProperties::kXidContinue},
+ {0xa674, 0xa67d, CharacterProperties::kXidContinue},
+ {0xa67f, 0xa69d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa69e, 0xa69f, CharacterProperties::kXidContinue},
+ {0xa6a0, 0xa6ef, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa6f0, 0xa6f1, CharacterProperties::kXidContinue},
+ {0xa717, 0xa71f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa722, 0xa788, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa78b, 0xa7ae, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa7b0, 0xa7b7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa7f7, 0xa801, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa802, 0xa802, CharacterProperties::kXidContinue},
+ {0xa803, 0xa805, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa806, 0xa806, CharacterProperties::kXidContinue},
+ {0xa807, 0xa80a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa80b, 0xa80b, CharacterProperties::kXidContinue},
+ {0xa80c, 0xa822, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa823, 0xa827, CharacterProperties::kXidContinue},
+ {0xa840, 0xa873, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa880, 0xa881, CharacterProperties::kXidContinue},
+ {0xa882, 0xa8b3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa8b4, 0xa8c5, CharacterProperties::kXidContinue},
+ {0xa8d0, 0xa8d9, CharacterProperties::kXidContinue},
+ {0xa8e0, 0xa8f1, CharacterProperties::kXidContinue},
+ {0xa8f2, 0xa8f7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa8fb, 0xa8fb, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa8fd, 0xa8fd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa900, 0xa909, CharacterProperties::kXidContinue},
+ {0xa90a, 0xa925, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa926, 0xa92d, CharacterProperties::kXidContinue},
+ {0xa930, 0xa946, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa947, 0xa953, CharacterProperties::kXidContinue},
+ {0xa960, 0xa97c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa980, 0xa983, CharacterProperties::kXidContinue},
+ {0xa984, 0xa9b2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa9b3, 0xa9c0, CharacterProperties::kXidContinue},
+ {0xa9cf, 0xa9cf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa9d0, 0xa9d9, CharacterProperties::kXidContinue},
+ {0xa9e0, 0xa9e4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa9e5, 0xa9e5, CharacterProperties::kXidContinue},
+ {0xa9e6, 0xa9ef, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xa9f0, 0xa9f9, CharacterProperties::kXidContinue},
+ {0xa9fa, 0xa9fe, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa00, 0xaa28, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa29, 0xaa36, CharacterProperties::kXidContinue},
+ {0xaa40, 0xaa42, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa43, 0xaa43, CharacterProperties::kXidContinue},
+ {0xaa44, 0xaa4b, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa4c, 0xaa4d, CharacterProperties::kXidContinue},
+ {0xaa50, 0xaa59, CharacterProperties::kXidContinue},
+ {0xaa60, 0xaa76, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa7a, 0xaa7a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaa7b, 0xaa7d, CharacterProperties::kXidContinue},
+ {0xaa7e, 0xaaaf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaab0, 0xaab0, CharacterProperties::kXidContinue},
+ {0xaab1, 0xaab1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaab2, 0xaab4, CharacterProperties::kXidContinue},
+ {0xaab5, 0xaab6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaab7, 0xaab8, CharacterProperties::kXidContinue},
+ {0xaab9, 0xaabd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaabe, 0xaabf, CharacterProperties::kXidContinue},
+ {0xaac0, 0xaac0, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaac1, 0xaac1, CharacterProperties::kXidContinue},
+ {0xaac2, 0xaac2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaadb, 0xaadd, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaae0, 0xaaea, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaaeb, 0xaaef, CharacterProperties::kXidContinue},
+ {0xaaf2, 0xaaf4, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xaaf5, 0xaaf6, CharacterProperties::kXidContinue},
+ {0xab01, 0xab06, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab09, 0xab0e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab11, 0xab16, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab20, 0xab26, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab28, 0xab2e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab30, 0xab5a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab5c, 0xab65, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xab70, 0xabe2, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xabe3, 0xabea, CharacterProperties::kXidContinue},
+ {0xabec, 0xabed, CharacterProperties::kXidContinue},
+ {0xabf0, 0xabf9, CharacterProperties::kXidContinue},
+ {0xac00, 0xd7a3, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xd7b0, 0xd7c6, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xd7cb, 0xd7fb, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xf900, 0xfa6d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfa70, 0xfad9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb00, 0xfb06, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb13, 0xfb17, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb1d, 0xfb1d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb1e, 0xfb1e, CharacterProperties::kXidContinue},
+ {0xfb1f, 0xfb28, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb2a, 0xfb36, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb38, 0xfb3c, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb3e, 0xfb3e, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb40, 0xfb41, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb43, 0xfb44, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfb46, 0xfbb1, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfbd3, 0xfc5d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfc64, 0xfd3d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfd50, 0xfd8f, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfd92, 0xfdc7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfdf0, 0xfdf9, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe00, 0xfe0f, CharacterProperties::kXidContinue},
+ {0xfe20, 0xfe2f, CharacterProperties::kXidContinue},
+ {0xfe33, 0xfe34, CharacterProperties::kXidContinue},
+ {0xfe4d, 0xfe4f, CharacterProperties::kXidContinue},
+ {0xfe71, 0xfe71, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe73, 0xfe73, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe77, 0xfe77, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe79, 0xfe79, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe7b, 0xfe7b, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe7d, 0xfe7d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xfe7f, 0xfefc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xff10, 0xff19, CharacterProperties::kXidContinue},
+ {0xff21, 0xff3a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xff3f, 0xff3f, CharacterProperties::kXidContinue},
+ {0xff41, 0xff5a, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xff66, 0xff9d, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xff9e, 0xff9f, CharacterProperties::kXidContinue},
+ {0xffa0, 0xffbe, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xffc2, 0xffc7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xffca, 0xffcf, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xffd2, 0xffd7, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+ {0xffda, 0xffdc, CharacterProperties::kXidStart | CharacterProperties::kXidContinue},
+}};
diff --git a/tools/aapt2/text/Unicode_test.cpp b/tools/aapt2/text/Unicode_test.cpp
new file mode 100644
index 000000000000..d47fb282bc0a
--- /dev/null
+++ b/tools/aapt2/text/Unicode_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Unicode.h"
+
+#include "test/Test.h"
+
+using ::testing::Each;
+using ::testing::Eq;
+using ::testing::ResultOf;
+
+namespace aapt {
+namespace text {
+
+TEST(UnicodeTest, IsXidStart) {
+ std::u32string valid_input = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZˮø";
+ EXPECT_THAT(valid_input, Each(ResultOf(IsXidStart, Eq(true))));
+
+ std::u32string invalid_input = U"$;\'/<>+=-.{}[]()\\|?@#%^&*!~`\",1234567890_";
+ EXPECT_THAT(invalid_input, Each(ResultOf(IsXidStart, Eq(false))));
+}
+
+TEST(UnicodeTest, IsXidContinue) {
+ std::u32string valid_input = U"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_ˮø";
+ EXPECT_THAT(valid_input, Each(ResultOf(IsXidContinue, Eq(true))));
+
+ std::u32string invalid_input = U"$;\'/<>+=-.{}[]()\\|?@#%^&*!~`\",";
+ EXPECT_THAT(invalid_input, Each(ResultOf(IsXidContinue, Eq(false))));
+}
+
+TEST(UnicodeTest, IsJavaIdentifier) {
+ EXPECT_TRUE(IsJavaIdentifier("FøøBar_12"));
+ EXPECT_TRUE(IsJavaIdentifier("Føø$Bar"));
+
+ EXPECT_FALSE(IsJavaIdentifier("12FøøBar"));
+ EXPECT_FALSE(IsJavaIdentifier("_FøøBar"));
+ EXPECT_FALSE(IsJavaIdentifier("$Føø$Bar"));
+}
+
+TEST(UnicodeTest, IsValidResourceEntryName) {
+ EXPECT_TRUE(IsJavaIdentifier("FøøBar"));
+ EXPECT_TRUE(IsValidResourceEntryName("FøøBar_12"));
+ EXPECT_TRUE(IsValidResourceEntryName("Føø.Bar"));
+ EXPECT_TRUE(IsValidResourceEntryName("Føø-Bar"));
+ EXPECT_TRUE(IsValidResourceEntryName("_FøøBar"));
+
+ EXPECT_FALSE(IsValidResourceEntryName("12FøøBar"));
+ EXPECT_FALSE(IsValidResourceEntryName("Føø$Bar"));
+ EXPECT_FALSE(IsValidResourceEntryName("Føø/Bar"));
+ EXPECT_FALSE(IsValidResourceEntryName("Føø:Bar"));
+ EXPECT_FALSE(IsValidResourceEntryName("Føø;Bar"));
+}
+
+} // namespace text
+} // namespace aapt
diff --git a/tools/aapt2/text/Utf8Iterator.cpp b/tools/aapt2/text/Utf8Iterator.cpp
new file mode 100644
index 000000000000..20b9073b9a26
--- /dev/null
+++ b/tools/aapt2/text/Utf8Iterator.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Utf8Iterator.h"
+
+#include "android-base/logging.h"
+#include "utils/Unicode.h"
+
+using ::android::StringPiece;
+
+namespace aapt {
+namespace text {
+
+Utf8Iterator::Utf8Iterator(const StringPiece& str)
+ : str_(str), current_pos_(0), next_pos_(0), current_codepoint_(0) {
+ DoNext();
+}
+
+void Utf8Iterator::DoNext() {
+ current_pos_ = next_pos_;
+ int32_t result = utf32_from_utf8_at(str_.data(), str_.size(), current_pos_, &next_pos_);
+ if (result == -1) {
+ current_codepoint_ = 0u;
+ } else {
+ current_codepoint_ = static_cast<char32_t>(result);
+ }
+}
+
+bool Utf8Iterator::HasNext() const {
+ return current_codepoint_ != 0;
+}
+
+size_t Utf8Iterator::Position() const {
+ return current_pos_;
+}
+
+void Utf8Iterator::Skip(int amount) {
+ while (amount > 0 && HasNext()) {
+ Next();
+ --amount;
+ }
+}
+
+char32_t Utf8Iterator::Next() {
+ CHECK(HasNext()) << "Next() called after iterator exhausted";
+ char32_t result = current_codepoint_;
+ DoNext();
+ return result;
+}
+
+} // namespace text
+} // namespace aapt
diff --git a/tools/aapt2/text/Utf8Iterator.h b/tools/aapt2/text/Utf8Iterator.h
new file mode 100644
index 000000000000..9318401876d1
--- /dev/null
+++ b/tools/aapt2/text/Utf8Iterator.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_TEXT_UTF8ITERATOR_H
+#define AAPT_TEXT_UTF8ITERATOR_H
+
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
+namespace aapt {
+namespace text {
+
+class Utf8Iterator {
+ public:
+ explicit Utf8Iterator(const android::StringPiece& str);
+
+ bool HasNext() const;
+
+ // Returns the current position of the iterator in bytes of the source UTF8 string.
+ // This position is the start of the codepoint returned by the next call to Next().
+ size_t Position() const;
+
+ void Skip(int amount);
+
+ char32_t Next();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Utf8Iterator);
+
+ void DoNext();
+
+ android::StringPiece str_;
+ size_t current_pos_;
+ size_t next_pos_;
+ char32_t current_codepoint_;
+};
+
+} // namespace text
+} // namespace aapt
+
+#endif // AAPT_TEXT_UTF8ITERATOR_H
diff --git a/tools/aapt2/text/Utf8Iterator_test.cpp b/tools/aapt2/text/Utf8Iterator_test.cpp
new file mode 100644
index 000000000000..8c3e77446595
--- /dev/null
+++ b/tools/aapt2/text/Utf8Iterator_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "text/Utf8Iterator.h"
+
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::testing::Eq;
+
+namespace aapt {
+namespace text {
+
+TEST(Utf8IteratorTest, IteratesOverAscii) {
+ Utf8Iterator iter("hello");
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'h'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'e'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'l'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'l'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'o'));
+
+ EXPECT_FALSE(iter.HasNext());
+}
+
+TEST(Utf8IteratorTest, IteratesOverUnicode) {
+ Utf8Iterator iter("Hi there 華勵蓮🍩");
+ iter.Skip(9);
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'華'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'勵'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'蓮'));
+
+ ASSERT_TRUE(iter.HasNext());
+ EXPECT_THAT(iter.Next(), Eq(U'🍩'));
+
+ EXPECT_FALSE(iter.HasNext());
+}
+
+TEST(Utf8IteratorTest, PositionPointsToTheCorrectPlace) {
+ const StringPiece expected("Mm🍩");
+ Utf8Iterator iter(expected);
+
+ // Before any character, the position should be 0.
+ EXPECT_THAT(iter.Position(), Eq(0u));
+
+ // The 'M' character, one byte.
+ ASSERT_TRUE(iter.HasNext());
+ iter.Next();
+ EXPECT_THAT(iter.Position(), Eq(1u));
+
+ // The 'm' character, one byte.
+ ASSERT_TRUE(iter.HasNext());
+ iter.Next();
+ EXPECT_THAT(iter.Position(), Eq(2u));
+
+ // The doughnut character, 4 bytes.
+ ASSERT_TRUE(iter.HasNext());
+ iter.Next();
+ EXPECT_THAT(iter.Position(), Eq(6u));
+
+ // There should be nothing left.
+ EXPECT_FALSE(iter.HasNext());
+ EXPECT_THAT(iter.Position(), Eq(expected.size()));
+}
+
+} // namespace text
+} // namespace aapt
diff --git a/tools/aapt2/tools/extract_unicode_properties.py b/tools/aapt2/tools/extract_unicode_properties.py
new file mode 100644
index 000000000000..7577ec82aa86
--- /dev/null
+++ b/tools/aapt2/tools/extract_unicode_properties.py
@@ -0,0 +1,102 @@
+#!/bin/env python3
+
+"""Extracts the XID_Start and XID_Continue Derived core properties from the ICU data files
+and emits a std::array<> for binary searching.
+"""
+
+import re
+import sys
+
+CharacterPropertyEnumMap = {
+ 1: "CharacterProperties::kXidStart",
+ 2: "CharacterProperties::kXidContinue"
+}
+
+class CharacterProperty:
+ def __init__(self, first_char, last_char, prop_type):
+ self.first_char = first_char
+ self.last_char = last_char
+ self.prop_type = prop_type
+
+ def key(self):
+ return self.first_char
+
+ def merge(self, other):
+ if self.last_char + 1 == other.first_char and self.prop_type == other.prop_type:
+ self.last_char = other.last_char
+ else:
+ raise KeyError()
+
+ def __repr__(self):
+ types = []
+ for enum_int, enum_str in CharacterPropertyEnumMap.items():
+ if enum_int & self.prop_type:
+ types.append(enum_str)
+ return "{}0x{:04x}, 0x{:04x}, {}{}".format(
+ "{", self.first_char, self.last_char, ' | '.join(types), "}")
+
+def extract_unicode_properties(f, props, chars_out):
+ prog = re.compile(r"^(?P<first>\w{4})(..(?P<last>\w{4}))?\W+;\W+(?P<prop>\w+)")
+ for line in f:
+ result = prog.match(line)
+ if result:
+ prop_type_str = result.group('prop')
+ first_char_str = result.group('first')
+ last_char_str = result.group('last')
+ if prop_type_str in props:
+ start_char = int(first_char_str, 16)
+ last_char = (int(last_char_str, 16) if last_char_str else start_char) + 1
+ prop_type = props[prop_type_str]
+ for char in range(start_char, last_char):
+ if char not in chars_out:
+ chars_out[char] = CharacterProperty(char, char, 0)
+ chars_out[char].prop_type |= prop_type
+ return chars_out
+
+def flatten_unicode_properties(chars):
+ result = []
+ for char_prop in sorted(chars.values(), key=CharacterProperty.key):
+ if len(result) == 0:
+ result.append(char_prop)
+ else:
+ try:
+ result[len(result) - 1].merge(char_prop)
+ except KeyError:
+ result.append(char_prop)
+ return result
+
+license = """/*
+ * 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.
+ */
+"""
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("must specify path to icu DerivedCoreProperties file (e.g:" \
+ "external/icu/icu4c/source/data/unidata/DerivedCoreProperties.txt)")
+ sys.exit(1)
+
+ props = {"XID_Start": 1, "XID_Continue": 2}
+ char_props = {}
+ for file_path in sys.argv[1:]:
+ with open(file_path) as f:
+ extract_unicode_properties(f, props, char_props)
+ result = flatten_unicode_properties(char_props)
+ print("{}\nconst static std::array<CharacterProperties, {}> sCharacterProperties = {}"
+ .format(license, len(result), "{{"))
+ for prop in result:
+ print(" {},".format(prop))
+ print("}};")
+
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index f3116701056b..892aee6fadcb 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -38,20 +38,17 @@ namespace aapt {
using namespace android;
-using android::base::StringPrintf;
+using ::android::base::StringPrintf;
namespace {
-/*
- * Visitor that converts a reference's resource ID to a resource name,
- * given a mapping from resource ID to resource name.
- */
+// Visitor that converts a reference's resource ID to a resource name, given a mapping from
+// resource ID to resource name.
class ReferenceIdToNameVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
- explicit ReferenceIdToNameVisitor(
- const std::map<ResourceId, ResourceName>* mapping)
+ explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping)
: mapping_(mapping) {
CHECK(mapping_ != nullptr);
}
@@ -99,7 +96,7 @@ bool BinaryResourceParser::Parse() {
if (parser.chunk()->type != android::RES_TABLE_TYPE) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< StringPrintf("unknown chunk of type 0x%02x",
- (int)parser.chunk()->type));
+ static_cast<int>(parser.chunk()->type)));
return false;
}
@@ -115,7 +112,7 @@ bool BinaryResourceParser::Parse() {
context_->GetDiagnostics()->Warn(
DiagMessage(source_) << StringPrintf(
"unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE",
- (int)parser.chunk()->type));
+ static_cast<int>(parser.chunk()->type)));
}
}
return true;
@@ -165,9 +162,8 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
default:
context_->GetDiagnostics()->Warn(
- DiagMessage(source_)
- << "unexpected chunk type "
- << (int)util::DeviceToHost16(parser.chunk()->type));
+ DiagMessage(source_) << "unexpected chunk type "
+ << static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
break;
}
}
@@ -245,8 +241,7 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
return false;
}
} else {
- context_->GetDiagnostics()->Warn(DiagMessage(source_)
- << "unexpected string pool");
+ context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool");
}
break;
@@ -270,9 +265,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
default:
context_->GetDiagnostics()->Warn(
- DiagMessage(source_)
- << "unexpected chunk type "
- << (int)util::DeviceToHost16(parser.chunk()->type));
+ DiagMessage(source_) << "unexpected chunk type "
+ << static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
break;
}
}
@@ -550,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/BigBuffer_test.cpp b/tools/aapt2/util/BigBuffer_test.cpp
index 12c0b3ee3214..a7776e33ae74 100644
--- a/tools/aapt2/util/BigBuffer_test.cpp
+++ b/tools/aapt2/util/BigBuffer_test.cpp
@@ -18,12 +18,14 @@
#include "test/Test.h"
+using ::testing::NotNull;
+
namespace aapt {
TEST(BigBufferTest, AllocateSingleBlock) {
BigBuffer buffer(4);
- EXPECT_NE(nullptr, buffer.NextBlock<char>(2));
+ EXPECT_THAT(buffer.NextBlock<char>(2), NotNull());
EXPECT_EQ(2u, buffer.size());
}
@@ -31,10 +33,10 @@ TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
BigBuffer buffer(16);
char* b1 = buffer.NextBlock<char>(8);
- EXPECT_NE(nullptr, b1);
+ EXPECT_THAT(b1, NotNull());
char* b2 = buffer.NextBlock<char>(4);
- EXPECT_NE(nullptr, b2);
+ EXPECT_THAT(b2, NotNull());
EXPECT_EQ(b1 + 8, b2);
}
@@ -42,7 +44,7 @@ TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
BigBuffer buffer(16);
- EXPECT_NE(nullptr, buffer.NextBlock<char>(32));
+ EXPECT_THAT(buffer.NextBlock<char>(32), NotNull());
EXPECT_EQ(32u, buffer.size());
}
@@ -50,13 +52,13 @@ TEST(BigBufferTest, AppendAndMoveBlock) {
BigBuffer buffer(16);
uint32_t* b1 = buffer.NextBlock<uint32_t>();
- ASSERT_NE(nullptr, b1);
+ ASSERT_THAT(b1, NotNull());
*b1 = 33;
{
BigBuffer buffer2(16);
b1 = buffer2.NextBlock<uint32_t>();
- ASSERT_NE(nullptr, b1);
+ ASSERT_THAT(b1, NotNull());
*b1 = 44;
buffer.AppendBuffer(std::move(buffer2));
@@ -83,7 +85,7 @@ TEST(BigBufferTest, AppendAndMoveBlock) {
TEST(BigBufferTest, PadAndAlignProperly) {
BigBuffer buffer(16);
- ASSERT_NE(buffer.NextBlock<char>(2), nullptr);
+ ASSERT_THAT(buffer.NextBlock<char>(2), NotNull());
ASSERT_EQ(2u, buffer.size());
buffer.Pad(2);
ASSERT_EQ(4u, buffer.size());
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/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index ca14793dd5c3..2057ddcc9e45 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -80,22 +80,22 @@ struct Dummy {
TEST(MaybeTest, MakeNothing) {
Maybe<int> val = make_nothing<int>();
- AAPT_EXPECT_FALSE(val);
+ EXPECT_FALSE(val);
Maybe<std::string> val2 = make_nothing<std::string>();
- AAPT_EXPECT_FALSE(val2);
+ EXPECT_FALSE(val2);
val2 = make_nothing<std::string>();
- AAPT_EXPECT_FALSE(val2);
+ EXPECT_FALSE(val2);
}
TEST(MaybeTest, MakeSomething) {
Maybe<int> val = make_value(23);
- AAPT_ASSERT_TRUE(val);
+ ASSERT_TRUE(val);
EXPECT_EQ(23, val.value());
Maybe<std::string> val2 = make_value(std::string("hey"));
- AAPT_ASSERT_TRUE(val2);
+ ASSERT_TRUE(val2);
EXPECT_EQ(std::string("hey"), val2.value());
}
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 8a8be858cb4c..51a75d7556ad 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -16,19 +16,21 @@
#include "util/Util.h"
-#include <utils/Unicode.h>
#include <algorithm>
#include <ostream>
#include <string>
#include <vector>
#include "androidfw/StringPiece.h"
+#include "utils/Unicode.h"
+#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
#include "util/Maybe.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::aapt::text::Utf8Iterator;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
namespace util {
@@ -283,33 +285,46 @@ bool VerifyJavaStringFormat(const StringPiece& str) {
return true;
}
-static Maybe<std::string> ParseUnicodeCodepoint(const char** start,
- const char* end) {
+static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
+ ssize_t len = utf32_to_utf8_length(&codepoint, 1);
+ if (len < 0) {
+ return false;
+ }
+
+ const size_t start_append_pos = output->size();
+
+ // Make room for the next character.
+ output->resize(output->size() + len);
+
+ char* dst = &*(output->begin() + start_append_pos);
+ utf32_to_utf8(&codepoint, 1, dst, len + 1);
+ return true;
+}
+
+static bool AppendUnicodeCodepoint(Utf8Iterator* iter, std::string* output) {
char32_t code = 0;
- for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
- char c = **start;
+ for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
+ char32_t codepoint = iter->Next();
char32_t a;
- if (c >= '0' && c <= '9') {
- a = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- a = c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- a = c - 'A' + 10;
+ if (codepoint >= U'0' && codepoint <= U'9') {
+ a = codepoint - U'0';
+ } else if (codepoint >= U'a' && codepoint <= U'f') {
+ a = codepoint - U'a' + 10;
+ } else if (codepoint >= U'A' && codepoint <= U'F') {
+ a = codepoint - U'A' + 10;
} else {
return {};
}
code = (code << 4) | a;
}
+ return AppendCodepointToUtf8String(code, output);
+}
- ssize_t len = utf32_to_utf8_length(&code, 1);
- if (len < 0) {
- return {};
+static bool IsCodepointSpace(char32_t codepoint) {
+ if (static_cast<uint32_t>(codepoint) & 0xffffff00u) {
+ return false;
}
-
- std::string result_utf8;
- result_utf8.resize(len);
- utf32_to_utf8(&code, 1, &*result_utf8.begin(), len + 1);
- return result_utf8;
+ return isspace(static_cast<char>(codepoint));
}
StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces) {
@@ -321,57 +336,46 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
}
// Where the new data will be appended to.
- size_t new_data_index = str_.size();
+ const size_t new_data_index = str_.size();
+
+ Utf8Iterator iter(str);
+ while (iter.HasNext()) {
+ const char32_t codepoint = iter.Next();
- const char* const end = str.end();
- const char* start = str.begin();
- const char* current = start;
- while (current != end) {
if (last_char_was_escape_) {
- switch (*current) {
- case 't':
+ switch (codepoint) {
+ case U't':
str_ += '\t';
break;
- case 'n':
+
+ case U'n':
str_ += '\n';
break;
- case '#':
- str_ += '#';
- break;
- case '@':
- str_ += '@';
- break;
- case '?':
- str_ += '?';
- break;
- case '"':
- str_ += '"';
- break;
- case '\'':
- str_ += '\'';
- break;
- case '\\':
- str_ += '\\';
+
+ case U'#':
+ case U'@':
+ case U'?':
+ case U'"':
+ case U'\'':
+ case U'\\':
+ str_ += static_cast<char>(codepoint);
break;
- case 'u': {
- current++;
- Maybe<std::string> c = ParseUnicodeCodepoint(&current, end);
- if (!c) {
+
+ case U'u':
+ if (!AppendUnicodeCodepoint(&iter, &str_)) {
error_ = "invalid unicode escape sequence";
return *this;
}
- str_ += c.value();
- current -= 1;
break;
- }
default:
- // Ignore.
+ // Ignore the escape character and just include the codepoint.
+ AppendCodepointToUtf8String(codepoint, &str_);
break;
}
last_char_was_escape_ = false;
- start = current + 1;
- } else if (!preserve_spaces_ && *current == '"') {
+
+ } else if (!preserve_spaces_ && codepoint == U'"') {
if (!quote_ && trailing_space_) {
// We found an opening quote, and we have trailing space, so we should append that
// space now.
@@ -384,13 +388,13 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
}
}
quote_ = !quote_;
- str_.append(start, current - start);
- start = current + 1;
- } else if (!preserve_spaces_ && *current == '\'' && !quote_) {
+
+ } else if (!preserve_spaces_ && codepoint == U'\'' && !quote_) {
// This should be escaped.
error_ = "unescaped apostrophe";
return *this;
- } else if (*current == '\\') {
+
+ } else if (codepoint == U'\\') {
// This is an escape sequence, convert to the real value.
if (!quote_ && trailing_space_) {
// We had trailing whitespace, so
@@ -400,40 +404,35 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
}
trailing_space_ = false;
}
- str_.append(start, current - start);
- start = current + 1;
last_char_was_escape_ = true;
- } else if (!preserve_spaces_ && !quote_) {
- // This is not quoted text, so look for whitespace.
- if (isspace(*current)) {
- // We found whitespace, see if we have seen some
- // before.
- if (!trailing_space_) {
- // We didn't see a previous adjacent space,
- // so mark that we did.
+ } else {
+ if (preserve_spaces_ || quote_) {
+ // Quotes mean everything is taken, including whitespace.
+ AppendCodepointToUtf8String(codepoint, &str_);
+ } else {
+ // This is not quoted text, so we will accumulate whitespace and only emit a single
+ // character of whitespace if it is followed by a non-whitespace character.
+ if (IsCodepointSpace(codepoint)) {
+ // We found whitespace.
trailing_space_ = true;
- str_.append(start, current - start);
- }
-
- // Keep skipping whitespace.
- start = current + 1;
- } else if (trailing_space_) {
- // We saw trailing space before, so replace all
- // that trailing space with one space.
- if (!str_.empty()) {
- str_ += ' ';
+ } else {
+ if (trailing_space_) {
+ // We saw trailing space before, so replace all
+ // that trailing space with one space.
+ if (!str_.empty()) {
+ str_ += ' ';
+ }
+ trailing_space_ = false;
+ }
+ AppendCodepointToUtf8String(codepoint, &str_);
}
- trailing_space_ = false;
}
}
- current++;
}
- str_.append(start, end - start);
// Accumulate the added string's UTF-16 length.
- ssize_t len = utf8_to_utf16_length(
- reinterpret_cast<const uint8_t*>(str_.data()) + new_data_index,
- str_.size() - new_data_index);
+ ssize_t len = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str_.data()) + new_data_index,
+ str_.size() - new_data_index);
if (len < 0) {
error_ = "invalid unicode code point";
return *this;
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index b9ada7704a26..8f021ab8cb8a 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -70,12 +70,6 @@ bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffi
android::StringPiece TrimWhitespace(const android::StringPiece& str);
/**
- * UTF-16 isspace(). It basically checks for lower range characters that are
- * whitespace.
- */
-inline bool isspace16(char16_t c) { return c < 0x0080 && isspace(c); }
-
-/**
* Returns an iterator to the first character that is not alpha-numeric and that
* is not in the allowedChars set.
*/
@@ -104,6 +98,16 @@ bool IsJavaPackageName(const android::StringPiece& str);
Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
const android::StringPiece& class_name);
+template <typename T>
+typename std::enable_if<std::is_arithmetic<T>::value, int>::type compare(const T& a, const T& b) {
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ }
+ return 0;
+}
+
/**
* Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
* This will be present in C++14 and can be removed then.
@@ -224,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;
@@ -246,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/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 5cced3e9acab..adb52911ab82 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -136,58 +136,34 @@ TEST(UtilTest, IsJavaPackageName) {
}
TEST(UtilTest, FullyQualifiedClassName) {
- Maybe<std::string> res = util::GetFullyQualifiedClassName("android", ".asdf");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.asdf");
-
- res = util::GetFullyQualifiedClassName("android", ".a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.a.b");
-
- res = util::GetFullyQualifiedClassName("android", "a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "a.b");
-
- res = util::GetFullyQualifiedClassName("", "a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "a.b");
-
- res = util::GetFullyQualifiedClassName("android", "Class");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.Class");
-
- res = util::GetFullyQualifiedClassName("", "");
- AAPT_ASSERT_FALSE(res);
-
- res = util::GetFullyQualifiedClassName("android", "./Apple");
- AAPT_ASSERT_FALSE(res);
+ EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".asdf"), Eq("android.asdf"));
+ EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".a.b"), Eq("android.a.b"));
+ EXPECT_THAT(util::GetFullyQualifiedClassName("android", "a.b"), Eq("a.b"));
+ EXPECT_THAT(util::GetFullyQualifiedClassName("", "a.b"), Eq("a.b"));
+ EXPECT_THAT(util::GetFullyQualifiedClassName("android", "Class"), Eq("android.Class"));
+ EXPECT_FALSE(util::GetFullyQualifiedClassName("", ""));
+ EXPECT_FALSE(util::GetFullyQualifiedClassName("android", "./Apple"));
}
TEST(UtilTest, ExtractResourcePathComponents) {
StringPiece prefix, entry, suffix;
- ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.xml",
- &prefix, &entry, &suffix));
- EXPECT_EQ(prefix, "res/xml-sw600dp/");
- EXPECT_EQ(entry, "entry");
- EXPECT_EQ(suffix, ".xml");
-
- ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.9.png",
- &prefix, &entry, &suffix));
-
- EXPECT_EQ(prefix, "res/xml-sw600dp/");
- EXPECT_EQ(entry, "entry");
- EXPECT_EQ(suffix, ".9.png");
-
- EXPECT_FALSE(util::ExtractResFilePathParts("AndroidManifest.xml", &prefix,
- &entry, &suffix));
- EXPECT_FALSE(
- util::ExtractResFilePathParts("res/.xml", &prefix, &entry, &suffix));
-
- ASSERT_TRUE(
- util::ExtractResFilePathParts("res//.", &prefix, &entry, &suffix));
- EXPECT_EQ(prefix, "res//");
- EXPECT_EQ(entry, "");
- EXPECT_EQ(suffix, ".");
+ ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.xml", &prefix, &entry, &suffix));
+ EXPECT_THAT(prefix, Eq("res/xml-sw600dp/"));
+ EXPECT_THAT(entry, Eq("entry"));
+ EXPECT_THAT(suffix, Eq(".xml"));
+
+ ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.9.png", &prefix, &entry, &suffix));
+ EXPECT_THAT(prefix, Eq("res/xml-sw600dp/"));
+ EXPECT_THAT(entry, Eq("entry"));
+ EXPECT_THAT(suffix, Eq(".9.png"));
+
+ ASSERT_TRUE(util::ExtractResFilePathParts("res//.", &prefix, &entry, &suffix));
+ EXPECT_THAT(prefix, Eq("res//"));
+ EXPECT_THAT(entry, Eq(""));
+ EXPECT_THAT(suffix, Eq("."));
+
+ EXPECT_FALSE(util::ExtractResFilePathParts("AndroidManifest.xml", &prefix, &entry, &suffix));
+ EXPECT_FALSE(util::ExtractResFilePathParts("res/.xml", &prefix, &entry, &suffix));
}
TEST(UtilTest, VerifyJavaStringFormat) {
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/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp
index 7110c90fa3a9..0fe7ab05ceb2 100644
--- a/tools/aapt2/xml/XmlActionExecutor_test.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp
@@ -18,6 +18,8 @@
#include "test/Test.h"
+using ::testing::NotNull;
+
namespace aapt {
namespace xml {
@@ -42,12 +44,11 @@ TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) {
test::BuildXmlDom("<manifest><application /></manifest>");
StdErrDiagnostics diag;
- ASSERT_TRUE(
- executor.Execute(XmlActionExecutorPolicy::kNone, &diag, doc.get()));
- ASSERT_NE(nullptr, manifest_el);
+ ASSERT_TRUE(executor.Execute(XmlActionExecutorPolicy::kNone, &diag, doc.get()));
+ ASSERT_THAT(manifest_el, NotNull());
EXPECT_EQ(std::string("manifest"), manifest_el->name);
- ASSERT_NE(nullptr, application_el);
+ ASSERT_THAT(application_el, NotNull());
EXPECT_EQ(std::string("application"), application_el->name);
}
@@ -58,8 +59,7 @@ TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) {
std::unique_ptr<XmlResource> doc =
test::BuildXmlDom("<manifest><application /><activity /></manifest>");
StdErrDiagnostics diag;
- ASSERT_FALSE(
- executor.Execute(XmlActionExecutorPolicy::kWhitelist, &diag, doc.get()));
+ ASSERT_FALSE(executor.Execute(XmlActionExecutorPolicy::kWhitelist, &diag, doc.get()));
}
} // namespace xml
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 031801e1dd2f..6ed2d616f782 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -16,19 +16,22 @@
#include "xml/XmlDom.h"
-#include <sstream>
#include <string>
+#include "io/StringInputStream.h"
#include "test/Test.h"
-namespace aapt {
+using ::aapt::io::StringInputStream;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
-constexpr const char* kXmlPreamble =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+namespace aapt {
+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">
@@ -37,47 +40,82 @@ 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);
- ASSERT_NE(doc, nullptr);
-
- xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());
- ASSERT_NE(ns, nullptr);
- EXPECT_EQ(ns->namespace_uri, xml::kSchemaAndroid);
- EXPECT_EQ(ns->namespace_prefix, "android");
+ StringInputStream in(input);
+ std::unique_ptr<XmlResource> doc = Inflate(&in, &diag, Source("test.xml"));
+ ASSERT_THAT(doc, NotNull());
+
+ 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->root.get());
- ASSERT_NE(nullptr, el);
+ Element* el = doc->root.get();
- xml::Attribute* attr = el->FindAttribute({}, "pattern");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("\\\\d{5}", attr->value);
+ Attribute* attr = el->FindAttribute({}, "pattern");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, Eq("\\\\d{5}"));
attr = el->FindAttribute({}, "value");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("\\?hello", attr->value);
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, Eq("\\?hello"));
- xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
- ASSERT_NE(nullptr, text);
- EXPECT_EQ("\\\\d{5}", text->text);
+ ASSERT_THAT(el->children, SizeIs(1u));
+
+ 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_NE(nullptr, el);
+ 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("\""));
+}
- xml::Attribute* attr = el->FindAttribute({}, "value");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("\"", attr->value);
+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);
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index 5eecc8f5fb20..cbded8ffac8e 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -21,37 +21,30 @@
namespace aapt {
TEST(XmlUtilTest, ExtractPackageFromNamespace) {
- AAPT_ASSERT_FALSE(xml::ExtractPackageFromNamespace("com.android"));
- AAPT_ASSERT_FALSE(
- xml::ExtractPackageFromNamespace("http://schemas.android.com/apk"));
- AAPT_ASSERT_FALSE(
- xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res"));
- AAPT_ASSERT_FALSE(
- xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
- AAPT_ASSERT_FALSE(xml::ExtractPackageFromNamespace(
- "http://schemas.android.com/apk/prv/res/"));
+ ASSERT_FALSE(xml::ExtractPackageFromNamespace("com.android"));
+ ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk"));
+ ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res"));
+ ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
+ ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/"));
Maybe<xml::ExtractedPackage> p =
xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/a");
- AAPT_ASSERT_TRUE(p);
+ ASSERT_TRUE(p);
EXPECT_EQ(std::string("a"), p.value().package);
EXPECT_FALSE(p.value().private_namespace);
- p = xml::ExtractPackageFromNamespace(
- "http://schemas.android.com/apk/prv/res/android");
- AAPT_ASSERT_TRUE(p);
+ p = xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/android");
+ ASSERT_TRUE(p);
EXPECT_EQ(std::string("android"), p.value().package);
EXPECT_TRUE(p.value().private_namespace);
- p = xml::ExtractPackageFromNamespace(
- "http://schemas.android.com/apk/prv/res/com.test");
- AAPT_ASSERT_TRUE(p);
+ p = xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/com.test");
+ ASSERT_TRUE(p);
EXPECT_EQ(std::string("com.test"), p.value().package);
EXPECT_TRUE(p.value().private_namespace);
- p = xml::ExtractPackageFromNamespace(
- "http://schemas.android.com/apk/res-auto");
- AAPT_ASSERT_TRUE(p);
+ p = xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res-auto");
+ ASSERT_TRUE(p);
EXPECT_EQ(std::string(), p.value().package);
EXPECT_TRUE(p.value().private_namespace);
}
diff --git a/tools/bit/adb.cpp b/tools/bit/adb.cpp
index 0c8424de566d..c8faf5c66722 100644
--- a/tools/bit/adb.cpp
+++ b/tools/bit/adb.cpp
@@ -283,10 +283,19 @@ run_instrumentation_test(const string& packageName, const string& runner, const
cmd.AddArg("instrument");
cmd.AddArg("-w");
cmd.AddArg("-m");
- if (className.length() > 0) {
- cmd.AddArg("-e");
- cmd.AddArg("class");
- cmd.AddArg(className);
+ const int classLen = className.length();
+ if (classLen > 0) {
+ if (classLen > 1 && className[classLen - 1] == '.') {
+ cmd.AddArg("-e");
+ cmd.AddArg("package");
+
+ // "am" actually accepts without removing the last ".", but for cleanlines...
+ cmd.AddArg(className.substr(0, classLen - 1));
+ } else {
+ cmd.AddArg("-e");
+ cmd.AddArg("class");
+ cmd.AddArg(className);
+ }
}
cmd.AddArg(packageName + "/" + runner);
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index d056ba53ca84..91ca5143965e 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -50,6 +50,7 @@ struct Target {
int testPassCount;
int testFailCount;
+ int unknownFailureCount; // unknown failure == "Process crashed", etc.
bool actionsWithNoTests;
Target(bool b, bool i, bool t, const string& p);
@@ -63,6 +64,7 @@ Target::Target(bool b, bool i, bool t, const string& p)
testActionCount(0),
testPassCount(0),
testFailCount(0),
+ unknownFailureCount(0),
actionsWithNoTests(false)
{
}
@@ -188,8 +190,13 @@ public:
*/
void SetCurrentAction(TestAction* action);
+ bool IsSuccess();
+
+ string GetErrorMessage();
+
private:
TestAction* m_currentAction;
+ SessionStatus m_sessionStatus;
};
void
@@ -241,8 +248,10 @@ TestResults::OnTestStatus(TestStatus& status)
}
line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName;
print_one_line("%s", line.str().c_str());
- } else if (resultCode == -2) {
+ } else if ((resultCode == -1) || (resultCode == -2)) {
// test failed
+ // Note -2 means an assertion failure, and -1 means other exceptions. We just treat them
+ // all as "failures".
m_currentAction->failCount++;
m_currentAction->target->testFailCount++;
printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold,
@@ -257,9 +266,13 @@ TestResults::OnTestStatus(TestStatus& status)
}
void
-TestResults::OnSessionStatus(SessionStatus& /*status*/)
+TestResults::OnSessionStatus(SessionStatus& status)
{
//status.PrintDebugString();
+ m_sessionStatus = status;
+ if (m_currentAction && !IsSuccess()) {
+ m_currentAction->target->unknownFailureCount++;
+ }
}
void
@@ -268,6 +281,24 @@ TestResults::SetCurrentAction(TestAction* action)
m_currentAction = action;
}
+bool
+TestResults::IsSuccess()
+{
+ return m_sessionStatus.result_code() == -1; // Activity.RESULT_OK.
+}
+
+string
+TestResults::GetErrorMessage()
+{
+ bool found;
+ string shortMsg = get_bundle_string(m_sessionStatus.results(), &found, "shortMsg", NULL);
+ if (!found) {
+ return IsSuccess() ? "" : "Unknown failure";
+ }
+ return shortMsg;
+}
+
+
/**
* Prints the usage statement / help text.
*/
@@ -342,6 +373,10 @@ print_usage(FILE* out) {
fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n");
fprintf(out, " and testRepeated test methods on that class.\n");
fprintf(out, "\n");
+ fprintf(out, " bit CtsProtoTestCases:android.util.proto.cts.\n");
+ fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the tests in the java package\n");
+ fprintf(out, " \"android.util.proto.cts\".\n");
+ fprintf(out, "\n");
fprintf(out, " Launching an Activity\n");
fprintf(out, " ---------------------\n");
fprintf(out, " To launch an activity, specify the activity class name after\n");
@@ -564,7 +599,7 @@ check_device_property(const string& property, const string& expected)
/**
* Run the build, install, and test actions.
*/
-void
+bool
run_phases(vector<Target*> targets, const Options& options)
{
int err = 0;
@@ -731,7 +766,7 @@ run_phases(vector<Target*> targets, const Options& options)
InstallApk& apk = installApks[i];
if (!apk.file.fileInfo.exists || apk.file.HasChanged()) {
// It didn't exist before or it changed, so int needs install
- err = run_adb("install", "-r", apk.file.filename.c_str(), NULL);
+ err = run_adb("install", "-r", "-g", apk.file.filename.c_str(), NULL);
check_error(err);
apk.installed = true;
} else {
@@ -833,6 +868,10 @@ run_phases(vector<Target*> targets, const Options& options)
printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount,
g_escapeEndColor, action.failCount);
}
+ if (!testResults.IsSuccess()) {
+ printf("\n%sTest didn't finish successfully: %s%s\n", g_escapeRedBold,
+ testResults.GetErrorMessage().c_str(), g_escapeEndColor);
+ }
}
}
@@ -903,6 +942,7 @@ run_phases(vector<Target*> targets, const Options& options)
}
// Tests
+ bool hasErrors = false;
if (testActions.size() > 0) {
printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor);
size_t maxNameLength = 0;
@@ -920,12 +960,18 @@ run_phases(vector<Target*> targets, const Options& options)
Target* target = targets[i];
if (target->testActionCount > 0) {
printf(" %s%s", target->name.c_str(), padding.c_str() + target->name.length());
- if (target->actionsWithNoTests) {
+ if (target->unknownFailureCount > 0) {
+ printf(" %sUnknown failure, see above message.%s\n",
+ g_escapeRedBold, g_escapeEndColor);
+ hasErrors = true;
+ } else if (target->actionsWithNoTests) {
printf(" %s%d passed, %d failed%s\n", g_escapeYellowBold,
target->testPassCount, target->testFailCount, g_escapeEndColor);
+ hasErrors = true;
} else if (target->testFailCount > 0) {
printf(" %d passed, %s%d failed%s\n", target->testPassCount,
g_escapeRedBold, target->testFailCount, g_escapeEndColor);
+ hasErrors = true;
} else {
printf(" %s%d passed%s, %d failed\n", g_escapeGreenBold,
target->testPassCount, g_escapeEndColor, target->testFailCount);
@@ -940,6 +986,7 @@ run_phases(vector<Target*> targets, const Options& options)
}
printf("%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+ return !hasErrors;
}
/**
@@ -987,7 +1034,7 @@ main(int argc, const char** argv)
exit(0);
} else {
// Normal run
- run_phases(options.targets, options);
+ exit(run_phases(options.targets, options) ? 0 : 1);
}
return 0;
diff --git a/tools/incident_report/Android.bp b/tools/incident_report/Android.bp
index 6f21605a99a4..ab55dbd81821 100644
--- a/tools/incident_report/Android.bp
+++ b/tools/incident_report/Android.bp
@@ -32,7 +32,4 @@ cc_binary_host {
],
cflags: ["-Wno-unused-parameter"],
-
- // b/34740546, work around clang-tidy segmentation fault.
- tidy_checks: ["-modernize*"],
}
diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp
index 7f8151f196ae..1756e06c66fa 100644
--- a/tools/incident_section_gen/Android.bp
+++ b/tools/incident_section_gen/Android.bp
@@ -19,8 +19,6 @@
// ==========================================================
cc_binary_host {
name: "incident-section-gen",
- // b/34740546, work around clang-tidy segmentation fault.
- tidy_checks: ["-modernize*"],
cflags: [
"-g",
"-O0",