Merge changes Id8bdb14e,I573a6735,Ia804777f,Ia68122cb,Ia1997800, ... into oc-mr1-dev
* changes: AAPT2: Bump to version 2.19 AAPT2: Fix issue with resource deduping AAPT: Dump badging should pickup strings from the right package AAPT2: Change the daemon mode to be line based AAPT2: better error handling for daemon mode AAPT2: Fix regression in Manifest.java permissions AAPT2: Define intermediate compiled XML proto AAPT2: Add a daemon mode AAPT2: Fix mkdirs implementation AAPT2: Change XmlDom to exclude Namespace as a node AAPT2: Fix windows unicode path issues AAPT2: Fix typo in Config parsing AAPT2: Change proto format to reduce usage of StringPool AAPT2: Add navigation type AAPT2: Document Format.proto
This commit is contained in:
committed by
Android (Google) Code Review
commit
e02983681a
@@ -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();
|
||||
}
|
||||
resTable.resolveReference(&value, 0);
|
||||
if (value.dataType != Res_value::TYPE_STRING) {
|
||||
if (outError != NULL) {
|
||||
*outError = "attribute is not a string value";
|
||||
}
|
||||
return String8();
|
||||
if (tree.getAttributeValue(idx, &value) == BAD_TYPE) {
|
||||
if (outError != NULL) {
|
||||
*outError = "attribute value is corrupt";
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,9 @@ cc_library_host_static {
|
||||
"flatten/XmlFlattener.cpp",
|
||||
"io/BigBufferStreams.cpp",
|
||||
"io/File.cpp",
|
||||
"io/FileInputStream.cpp",
|
||||
"io/FileSystem.cpp",
|
||||
"io/StringInputStream.cpp",
|
||||
"io/Util.cpp",
|
||||
"io/ZipArchive.cpp",
|
||||
"link/AutoVersioner.cpp",
|
||||
@@ -140,7 +142,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,
|
||||
@@ -164,6 +167,7 @@ cc_library_host_shared {
|
||||
cc_test_host {
|
||||
name: "aapt2_tests",
|
||||
srcs: [
|
||||
"test/Builders.cpp",
|
||||
"test/Common.cpp",
|
||||
"**/*_test.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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -14,12 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
// clang-format off
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "android-base/utf8.h"
|
||||
#include "androidfw/StringPiece.h"
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "util/Files.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
using ::android::StringPiece;
|
||||
using ::android::base::StringPrintf;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -27,54 +41,136 @@ namespace aapt {
|
||||
static const char* sMajorVersion = "2";
|
||||
|
||||
// Update minor version whenever a feature or flag is added.
|
||||
static const char* sMinorVersion = "18";
|
||||
static const char* sMinorVersion = "19";
|
||||
|
||||
int PrintVersion() {
|
||||
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
|
||||
<< sMinorVersion << std::endl;
|
||||
return 0;
|
||||
static void PrintVersion() {
|
||||
std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
|
||||
sMinorVersion)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
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);
|
||||
static void PrintUsage() {
|
||||
std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void RunDaemon(IDiagnostics* diagnostics) {
|
||||
std::cout << "Ready" << std::endl;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!std::cin) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 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 main(int argc, char** argv) {
|
||||
if (argc >= 2) {
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
|
||||
std::vector<android::StringPiece> args;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
std::cerr << "unknown command '" << command << "'\n";
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -59,6 +59,7 @@ enum class ResourceType {
|
||||
kLayout,
|
||||
kMenu,
|
||||
kMipmap,
|
||||
kNavigation,
|
||||
kPlurals,
|
||||
kRaw,
|
||||
kString,
|
||||
|
||||
@@ -1219,7 +1219,7 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
|
||||
continue;
|
||||
}
|
||||
item->SetSource(item_source);
|
||||
array->items.emplace_back(std::move(item));
|
||||
array->elements.emplace_back(std::move(item));
|
||||
|
||||
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
|
||||
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "io/StringInputStream.h"
|
||||
#include "test/Test.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
using ::aapt::io::StringInputStream;
|
||||
using ::aapt::test::StrValueEq;
|
||||
using ::aapt::test::ValueEq;
|
||||
using ::android::ResTable_map;
|
||||
@@ -43,11 +45,13 @@ constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
|
||||
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
std::stringstream input(kXmlPreamble);
|
||||
input << R"(<attr name="foo"/>)" << std::endl;
|
||||
ResourceTable table;
|
||||
ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
|
||||
xml::XmlPullParser xml_parser(input);
|
||||
|
||||
std::string input = kXmlPreamble;
|
||||
input += R"(<attr name="foo"/>)";
|
||||
StringInputStream in(input);
|
||||
xml::XmlPullParser xml_parser(&in);
|
||||
ASSERT_FALSE(parser.Parse(&xml_parser));
|
||||
}
|
||||
|
||||
@@ -62,12 +66,16 @@ class ResourceParserTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
|
||||
std::stringstream input(kXmlPreamble);
|
||||
input << "<resources>\n" << str << "\n</resources>" << std::endl;
|
||||
ResourceParserOptions parserOptions;
|
||||
ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
|
||||
parserOptions);
|
||||
xml::XmlPullParser xmlParser(input);
|
||||
|
||||
std::string input = kXmlPreamble;
|
||||
input += "<resources>\n";
|
||||
input.append(str.data(), str.size());
|
||||
input += "\n</resources>";
|
||||
StringInputStream in(input);
|
||||
xml::XmlPullParser xmlParser(&in);
|
||||
if (parser.Parse(&xmlParser)) {
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
@@ -532,11 +540,11 @@ TEST_F(ResourceParserTest, ParseArray) {
|
||||
|
||||
Array* array = test::GetValue<Array>(&table_, "array/foo");
|
||||
ASSERT_THAT(array, NotNull());
|
||||
ASSERT_THAT(array->items, SizeIs(3));
|
||||
ASSERT_THAT(array->elements, SizeIs(3));
|
||||
|
||||
EXPECT_THAT(ValueCast<Reference>(array->items[0].get()), NotNull());
|
||||
EXPECT_THAT(ValueCast<String>(array->items[1].get()), NotNull());
|
||||
EXPECT_THAT(ValueCast<BinaryPrimitive>(array->items[2].get()), NotNull());
|
||||
EXPECT_THAT(ValueCast<Reference>(array->elements[0].get()), NotNull());
|
||||
EXPECT_THAT(ValueCast<String>(array->elements[1].get()), NotNull());
|
||||
EXPECT_THAT(ValueCast<BinaryPrimitive>(array->elements[2].get()), NotNull());
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseStringArray) {
|
||||
@@ -557,9 +565,9 @@ TEST_F(ResourceParserTest, ParseArrayWithFormat) {
|
||||
|
||||
Array* array = test::GetValue<Array>(&table_, "array/foo");
|
||||
ASSERT_THAT(array, NotNull());
|
||||
ASSERT_THAT(array->items, SizeIs(1));
|
||||
ASSERT_THAT(array->elements, SizeIs(1));
|
||||
|
||||
String* str = ValueCast<String>(array->items[0].get());
|
||||
String* str = ValueCast<String>(array->elements[0].get());
|
||||
ASSERT_THAT(str, NotNull());
|
||||
EXPECT_THAT(*str, StrValueEq("100"));
|
||||
}
|
||||
|
||||
@@ -805,13 +805,12 @@ bool Array::Equals(const Value* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (items.size() != other->items.size()) {
|
||||
if (elements.size() != other->elements.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::equal(items.begin(), items.end(), other->items.begin(),
|
||||
[](const std::unique_ptr<Item>& a,
|
||||
const std::unique_ptr<Item>& b) -> bool {
|
||||
return std::equal(elements.begin(), elements.end(), other->elements.begin(),
|
||||
[](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
|
||||
return a->Equals(b.get());
|
||||
});
|
||||
}
|
||||
@@ -820,14 +819,14 @@ Array* Array::Clone(StringPool* new_pool) const {
|
||||
Array* array = new Array();
|
||||
array->comment_ = comment_;
|
||||
array->source_ = source_;
|
||||
for (auto& item : items) {
|
||||
array->items.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
|
||||
for (auto& item : elements) {
|
||||
array->elements.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void Array::Print(std::ostream* out) const {
|
||||
*out << "(array) [" << util::Joiner(items, ", ") << "]";
|
||||
*out << "(array) [" << util::Joiner(elements, ", ") << "]";
|
||||
}
|
||||
|
||||
bool Plural::Equals(const Value* value) const {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
|
||||
471
tools/aapt2/Resources.proto
Normal file
471
tools/aapt2/Resources.proto
Normal file
@@ -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;
|
||||
}
|
||||
52
tools/aapt2/ResourcesInternal.proto
Normal file
52
tools/aapt2/ResourcesInternal.proto
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(©ing_adaptor);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "flatten/TableFlattener.h"
|
||||
#include "flatten/XmlFlattener.h"
|
||||
#include "io/BigBufferInputStream.h"
|
||||
#include "io/FileInputStream.h"
|
||||
#include "io/FileSystem.h"
|
||||
#include "io/Util.h"
|
||||
#include "io/ZipArchive.h"
|
||||
@@ -61,8 +62,9 @@
|
||||
#include "util/Files.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
using android::StringPiece;
|
||||
using android::base::StringPrintf;
|
||||
using ::aapt::io::FileInputStream;
|
||||
using ::android::StringPiece;
|
||||
using ::android::base::StringPrintf;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -284,13 +286,11 @@ static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, cons
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates an XML file from the source path.
|
||||
*/
|
||||
// Inflates an XML file from the source path.
|
||||
static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) {
|
||||
std::ifstream fin(path, std::ifstream::binary);
|
||||
if (!fin) {
|
||||
diag->Error(DiagMessage(path) << strerror(errno));
|
||||
FileInputStream fin(path);
|
||||
if (fin.HadError()) {
|
||||
diag->Error(DiagMessage(path) << "failed to load XML file: " << fin.GetError());
|
||||
return {};
|
||||
}
|
||||
return xml::Inflate(&fin, diag, Source(path));
|
||||
@@ -482,7 +482,7 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
|
||||
|
||||
if (options_.no_version_vectors || options_.no_version_transitions) {
|
||||
// Skip this if it is a vector or animated-vector.
|
||||
xml::Element* el = xml::FindRootElement(doc);
|
||||
xml::Element* el = doc->root.get();
|
||||
if (el && el->namespace_uri.empty()) {
|
||||
if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
|
||||
(options_.no_version_transitions && IsTransitionElement(el->name))) {
|
||||
@@ -1295,7 +1295,7 @@ class LinkCommand {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_files; i++) {
|
||||
pb::CompiledFile compiled_file;
|
||||
pb::internal::CompiledFile compiled_file;
|
||||
if (!input_stream.ReadCompiledFile(&compiled_file)) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "failed to read compiled file header");
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
using android::StringPiece;
|
||||
using ::android::StringPiece;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -134,19 +134,21 @@ static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) {
|
||||
return xml::AaptAttribute(Attribute(), id);
|
||||
}
|
||||
|
||||
static xml::NamespaceDecl CreateAndroidNamespaceDecl() {
|
||||
xml::NamespaceDecl decl;
|
||||
decl.prefix = "android";
|
||||
decl.uri = xml::kSchemaAndroid;
|
||||
return decl;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
|
||||
const SplitConstraints& constraints) {
|
||||
const ResourceId kVersionCode(0x0101021b);
|
||||
const ResourceId kRevisionCode(0x010104d5);
|
||||
const ResourceId kHasCode(0x0101000c);
|
||||
|
||||
std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
|
||||
|
||||
std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>();
|
||||
namespace_android->namespace_uri = xml::kSchemaAndroid;
|
||||
namespace_android->namespace_prefix = "android";
|
||||
|
||||
std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>();
|
||||
manifest_el->namespace_decls.push_back(CreateAndroidNamespaceDecl());
|
||||
manifest_el->name = "manifest";
|
||||
manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package});
|
||||
|
||||
@@ -179,8 +181,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
|
||||
xml::Attribute{"", "configForSplit", app_info.split_name.value()});
|
||||
}
|
||||
|
||||
// Splits may contain more configurations than originally desired (fallback densities, etc.).
|
||||
// This makes programmatic discovery of split targetting difficult. Encode the original
|
||||
// Splits may contain more configurations than originally desired (fall-back densities, etc.).
|
||||
// This makes programmatic discovery of split targeting difficult. Encode the original
|
||||
// split constraints intended for this split.
|
||||
std::stringstream target_config_str;
|
||||
target_config_str << util::Joiner(constraints.configs, ",");
|
||||
@@ -193,8 +195,9 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
|
||||
util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, 0u)});
|
||||
|
||||
manifest_el->AppendChild(std::move(application_el));
|
||||
namespace_android->AppendChild(std::move(manifest_el));
|
||||
doc->root = std::move(namespace_android);
|
||||
|
||||
std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
|
||||
doc->root = std::move(manifest_el);
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -284,7 +287,7 @@ static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error
|
||||
|
||||
Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
|
||||
// Make sure the first element is <manifest> with package attribute.
|
||||
xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
|
||||
xml::Element* manifest_el = xml_res->root.get();
|
||||
if (manifest_el == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
EXPECT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(1u));
|
||||
|
||||
xml::Element* el = xml::FindRootElement(doc.get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
|
||||
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());
|
||||
ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(2u));
|
||||
|
||||
xml::Element* el = xml::FindRootElement(doc.get());
|
||||
ASSERT_NE(nullptr, el);
|
||||
|
||||
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"));
|
||||
EXPECT_THAT(el->FindChild(xml::kSchemaAapt, "attr"), IsNull());
|
||||
|
||||
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_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_NE(nullptr, extracted_doc_drawable);
|
||||
el = xml::FindRootElement(extracted_doc_drawable);
|
||||
ASSERT_NE(nullptr, el);
|
||||
EXPECT_EQ("vector", el->name);
|
||||
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
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/logging.h"
|
||||
|
||||
#include "ConfigDescription.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "io/File.h"
|
||||
#include "io/FileSystem.h"
|
||||
#include "io/StringInputStream.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlActionExecutor.h"
|
||||
@@ -49,9 +50,9 @@ using ::aapt::configuration::Group;
|
||||
using ::aapt::configuration::Locale;
|
||||
using ::aapt::io::IFile;
|
||||
using ::aapt::io::RegularFile;
|
||||
using ::aapt::io::StringInputStream;
|
||||
using ::aapt::util::TrimWhitespace;
|
||||
using ::aapt::xml::Element;
|
||||
using ::aapt::xml::FindRootElement;
|
||||
using ::aapt::xml::NodeCast;
|
||||
using ::aapt::xml::XmlActionExecutor;
|
||||
using ::aapt::xml::XmlActionExecutorPolicy;
|
||||
@@ -182,15 +183,14 @@ ConfigurationParser::ConfigurationParser(std::string contents)
|
||||
}
|
||||
|
||||
Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
|
||||
std::istringstream in(contents_);
|
||||
|
||||
auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
|
||||
StringInputStream in(contents_);
|
||||
std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
|
||||
if (!doc) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
|
||||
auto* root = FindRootElement(doc.get());
|
||||
Element* root = doc->root.get();
|
||||
if (root == nullptr) {
|
||||
diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
|
||||
return {};
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
|
||||
#include "test/Test.h"
|
||||
@@ -29,7 +26,7 @@
|
||||
namespace aapt {
|
||||
namespace {
|
||||
|
||||
using android::ResTable_config;
|
||||
using ::android::ResTable_config;
|
||||
using configuration::Abi;
|
||||
using configuration::AndroidSdk;
|
||||
using configuration::Artifact;
|
||||
@@ -38,7 +35,7 @@ using configuration::DeviceFeature;
|
||||
using configuration::GlTexture;
|
||||
using configuration::Locale;
|
||||
using configuration::AndroidManifest;
|
||||
using testing::ElementsAre;
|
||||
using ::testing::ElementsAre;
|
||||
using xml::Element;
|
||||
using xml::NodeCast;
|
||||
|
||||
@@ -192,7 +189,7 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
|
||||
auto doc = test::BuildXmlDom(xml);
|
||||
|
||||
PostProcessingConfiguration config;
|
||||
bool ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
|
||||
bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
|
||||
ASSERT_TRUE(ok);
|
||||
|
||||
EXPECT_EQ(1ul, config.artifacts.size());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -38,12 +38,10 @@ namespace {
|
||||
|
||||
constexpr uint32_t kLowPriority = 0xffffffffu;
|
||||
|
||||
static bool cmp_xml_attribute_by_id(const xml::Attribute* a,
|
||||
const xml::Attribute* b) {
|
||||
static bool cmp_xml_attribute_by_id(const xml::Attribute* a, const xml::Attribute* b) {
|
||||
if (a->compiled_attribute && a->compiled_attribute.value().id) {
|
||||
if (b->compiled_attribute && b->compiled_attribute.value().id) {
|
||||
return a->compiled_attribute.value().id.value() <
|
||||
b->compiled_attribute.value().id.value();
|
||||
return a->compiled_attribute.value().id.value() < b->compiled_attribute.value().id.value();
|
||||
}
|
||||
return true;
|
||||
} else if (!b->compiled_attribute) {
|
||||
@@ -75,17 +73,6 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
|
||||
: buffer_(buffer), options_(options) {}
|
||||
|
||||
void Visit(xml::Namespace* node) override {
|
||||
if (node->namespace_uri == xml::kSchemaTools) {
|
||||
// Skip dedicated tools namespace.
|
||||
xml::Visitor::Visit(node);
|
||||
} else {
|
||||
WriteNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
|
||||
xml::Visitor::Visit(node);
|
||||
WriteNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(xml::Text* node) override {
|
||||
if (util::TrimWhitespace(node->text).empty()) {
|
||||
// Skip whitespace only text nodes.
|
||||
@@ -93,8 +80,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
}
|
||||
|
||||
ChunkWriter writer(buffer_);
|
||||
ResXMLTree_node* flat_node =
|
||||
writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
|
||||
ResXMLTree_node* flat_node = writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
|
||||
flat_node->lineNumber = util::HostToDevice32(node->line_number);
|
||||
flat_node->comment.index = util::HostToDevice32(-1);
|
||||
|
||||
@@ -109,6 +95,13 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
}
|
||||
|
||||
void Visit(xml::Element* node) override {
|
||||
for (const xml::NamespaceDecl& decl : node->namespace_decls) {
|
||||
// Skip dedicated tools namespace.
|
||||
if (decl.uri != xml::kSchemaTools) {
|
||||
WriteNamespace(decl, android::RES_XML_START_NAMESPACE_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ChunkWriter start_writer(buffer_);
|
||||
ResXMLTree_node* flat_node =
|
||||
@@ -116,19 +109,15 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
flat_node->lineNumber = util::HostToDevice32(node->line_number);
|
||||
flat_node->comment.index = util::HostToDevice32(-1);
|
||||
|
||||
ResXMLTree_attrExt* flat_elem =
|
||||
start_writer.NextBlock<ResXMLTree_attrExt>();
|
||||
ResXMLTree_attrExt* flat_elem = start_writer.NextBlock<ResXMLTree_attrExt>();
|
||||
|
||||
// A missing namespace must be null, not an empty string. Otherwise the
|
||||
// runtime complains.
|
||||
// A missing namespace must be null, not an empty string. Otherwise the runtime complains.
|
||||
AddString(node->namespace_uri, kLowPriority, &flat_elem->ns,
|
||||
true /* treat_empty_string_as_null */);
|
||||
AddString(node->name, kLowPriority, &flat_elem->name,
|
||||
true /* treat_empty_string_as_null */);
|
||||
AddString(node->name, kLowPriority, &flat_elem->name, true /* treat_empty_string_as_null */);
|
||||
|
||||
flat_elem->attributeStart = util::HostToDevice16(sizeof(*flat_elem));
|
||||
flat_elem->attributeSize =
|
||||
util::HostToDevice16(sizeof(ResXMLTree_attribute));
|
||||
flat_elem->attributeSize = util::HostToDevice16(sizeof(ResXMLTree_attribute));
|
||||
|
||||
WriteAttributes(node, flat_elem, &start_writer);
|
||||
|
||||
@@ -144,14 +133,20 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
flat_end_node->lineNumber = util::HostToDevice32(node->line_number);
|
||||
flat_end_node->comment.index = util::HostToDevice32(-1);
|
||||
|
||||
ResXMLTree_endElementExt* flat_end_elem =
|
||||
end_writer.NextBlock<ResXMLTree_endElementExt>();
|
||||
ResXMLTree_endElementExt* flat_end_elem = end_writer.NextBlock<ResXMLTree_endElementExt>();
|
||||
AddString(node->namespace_uri, kLowPriority, &flat_end_elem->ns,
|
||||
true /* treat_empty_string_as_null */);
|
||||
AddString(node->name, kLowPriority, &flat_end_elem->name);
|
||||
|
||||
end_writer.Finish();
|
||||
}
|
||||
|
||||
for (auto iter = node->namespace_decls.rbegin(); iter != node->namespace_decls.rend(); ++iter) {
|
||||
// Skip dedicated tools namespace.
|
||||
if (iter->uri != xml::kSchemaTools) {
|
||||
WriteNamespace(*iter, android::RES_XML_END_NAMESPACE_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -173,16 +168,16 @@ class XmlFlattenerVisitor : public xml::Visitor {
|
||||
string_refs.push_back(StringFlattenDest{ref, dest});
|
||||
}
|
||||
|
||||
void WriteNamespace(xml::Namespace* node, uint16_t type) {
|
||||
void WriteNamespace(const xml::NamespaceDecl& decl, uint16_t type) {
|
||||
ChunkWriter writer(buffer_);
|
||||
|
||||
ResXMLTree_node* flatNode = writer.StartChunk<ResXMLTree_node>(type);
|
||||
flatNode->lineNumber = util::HostToDevice32(node->line_number);
|
||||
flatNode->lineNumber = util::HostToDevice32(decl.line_number);
|
||||
flatNode->comment.index = util::HostToDevice32(-1);
|
||||
|
||||
ResXMLTree_namespaceExt* flat_ns = writer.NextBlock<ResXMLTree_namespaceExt>();
|
||||
AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix);
|
||||
AddString(node->namespace_uri, kLowPriority, &flat_ns->uri);
|
||||
AddString(decl.prefix, kLowPriority, &flat_ns->prefix);
|
||||
AddString(decl.uri, kLowPriority, &flat_ns->uri);
|
||||
|
||||
writer.Finish();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation />
|
||||
102
tools/aapt2/io/FileInputStream.cpp
Normal file
102
tools/aapt2/io/FileInputStream.cpp
Normal file
@@ -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
|
||||
63
tools/aapt2/io/FileInputStream.h
Normal file
63
tools/aapt2/io/FileInputStream.h
Normal file
@@ -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
|
||||
87
tools/aapt2/io/FileInputStream_test.cpp
Normal file
87
tools/aapt2/io/FileInputStream_test.cpp
Normal file
@@ -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
|
||||
50
tools/aapt2/io/StringInputStream.cpp
Normal file
50
tools/aapt2/io/StringInputStream.cpp
Normal file
@@ -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
|
||||
56
tools/aapt2/io/StringInputStream.h
Normal file
56
tools/aapt2/io/StringInputStream.h
Normal file
@@ -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
|
||||
72
tools/aapt2/io/StringInputStream_test.cpp
Normal file
72
tools/aapt2/io/StringInputStream_test.cpp
Normal file
@@ -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
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,21 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
|
||||
EXPECT_THAT(actual, HasSubstr(expected_secret));
|
||||
}
|
||||
|
||||
// This is bad but part of public API behaviour so we need to preserve it.
|
||||
TEST(ManifestClassGeneratorTest, LastSeenPermissionWithSameLeafNameTakesPrecedence) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<permission android:name="android.permission.ACCESS_INTERNET" />
|
||||
<permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
|
||||
</manifest>)");
|
||||
|
||||
std::string actual;
|
||||
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
|
||||
EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
|
||||
EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
|
||||
}
|
||||
|
||||
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
|
||||
std::string* out_str) {
|
||||
std::unique_ptr<ClassDefinition> manifest_class =
|
||||
|
||||
@@ -29,18 +29,12 @@ namespace proguard {
|
||||
|
||||
class BaseVisitor : public xml::Visitor {
|
||||
public:
|
||||
BaseVisitor(const Source& source, KeepSet* keep_set)
|
||||
: source_(source), keep_set_(keep_set) {}
|
||||
using xml::Visitor::Visit;
|
||||
|
||||
virtual void Visit(xml::Text*) override{};
|
||||
|
||||
virtual void Visit(xml::Namespace* node) override {
|
||||
for (const auto& child : node->children) {
|
||||
child->Accept(this);
|
||||
}
|
||||
BaseVisitor(const Source& source, KeepSet* keep_set) : source_(source), keep_set_(keep_set) {
|
||||
}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
if (!node->namespace_uri.empty()) {
|
||||
Maybe<xml::ExtractedPackage> maybe_package =
|
||||
xml::ExtractPackageFromNamespace(node->namespace_uri);
|
||||
@@ -78,10 +72,10 @@ class BaseVisitor : public xml::Visitor {
|
||||
|
||||
class LayoutVisitor : public BaseVisitor {
|
||||
public:
|
||||
LayoutVisitor(const Source& source, KeepSet* keep_set)
|
||||
: BaseVisitor(source, keep_set) {}
|
||||
LayoutVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
|
||||
}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
bool check_class = false;
|
||||
bool check_name = false;
|
||||
if (node->namespace_uri.empty()) {
|
||||
@@ -119,7 +113,7 @@ class MenuVisitor : public BaseVisitor {
|
||||
MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
|
||||
}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
if (node->namespace_uri.empty() && node->name == "item") {
|
||||
for (const auto& attr : node->attributes) {
|
||||
if (attr.namespace_uri == xml::kSchemaAndroid) {
|
||||
@@ -142,10 +136,10 @@ class MenuVisitor : public BaseVisitor {
|
||||
|
||||
class XmlResourceVisitor : public BaseVisitor {
|
||||
public:
|
||||
XmlResourceVisitor(const Source& source, KeepSet* keep_set)
|
||||
: BaseVisitor(source, keep_set) {}
|
||||
XmlResourceVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
|
||||
}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
bool check_fragment = false;
|
||||
if (node->namespace_uri.empty()) {
|
||||
check_fragment =
|
||||
@@ -169,13 +163,12 @@ class XmlResourceVisitor : public BaseVisitor {
|
||||
|
||||
class TransitionVisitor : public BaseVisitor {
|
||||
public:
|
||||
TransitionVisitor(const Source& source, KeepSet* keep_set)
|
||||
: BaseVisitor(source, keep_set) {}
|
||||
TransitionVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
|
||||
}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
bool check_class =
|
||||
node->namespace_uri.empty() &&
|
||||
(node->name == "transition" || node->name == "pathMotion");
|
||||
node->namespace_uri.empty() && (node->name == "transition" || node->name == "pathMotion");
|
||||
if (check_class) {
|
||||
xml::Attribute* attr = node->FindAttribute({}, "class");
|
||||
if (attr && util::IsJavaClassName(attr->value)) {
|
||||
@@ -195,7 +188,7 @@ class ManifestVisitor : public BaseVisitor {
|
||||
ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
|
||||
: BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
|
||||
|
||||
virtual void Visit(xml::Element* node) override {
|
||||
void Visit(xml::Element* node) override {
|
||||
if (node->namespace_uri.empty()) {
|
||||
bool get_name = false;
|
||||
if (node->name == "manifest") {
|
||||
@@ -205,18 +198,15 @@ class ManifestVisitor : public BaseVisitor {
|
||||
}
|
||||
} else if (node->name == "application") {
|
||||
get_name = true;
|
||||
xml::Attribute* attr =
|
||||
node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
|
||||
xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
|
||||
if (attr) {
|
||||
Maybe<std::string> result =
|
||||
util::GetFullyQualifiedClassName(package_, attr->value);
|
||||
Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
|
||||
if (result) {
|
||||
AddClass(node->line_number, result.value());
|
||||
}
|
||||
}
|
||||
if (main_dex_only_) {
|
||||
xml::Attribute* default_process =
|
||||
node->FindAttribute(xml::kSchemaAndroid, "process");
|
||||
xml::Attribute* default_process = node->FindAttribute(xml::kSchemaAndroid, "process");
|
||||
if (default_process) {
|
||||
default_process_ = default_process->value;
|
||||
}
|
||||
@@ -226,8 +216,7 @@ class ManifestVisitor : public BaseVisitor {
|
||||
get_name = true;
|
||||
|
||||
if (main_dex_only_) {
|
||||
xml::Attribute* component_process =
|
||||
node->FindAttribute(xml::kSchemaAndroid, "process");
|
||||
xml::Attribute* component_process = node->FindAttribute(xml::kSchemaAndroid, "process");
|
||||
|
||||
const std::string& process =
|
||||
component_process ? component_process->value : default_process_;
|
||||
@@ -242,8 +231,7 @@ class ManifestVisitor : public BaseVisitor {
|
||||
get_name = attr != nullptr;
|
||||
|
||||
if (get_name) {
|
||||
Maybe<std::string> result =
|
||||
util::GetFullyQualifiedClassName(package_, attr->value);
|
||||
Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
|
||||
if (result) {
|
||||
AddClass(node->line_number, result.value());
|
||||
}
|
||||
@@ -261,8 +249,7 @@ class ManifestVisitor : public BaseVisitor {
|
||||
std::string default_process_;
|
||||
};
|
||||
|
||||
bool CollectProguardRulesForManifest(const Source& source,
|
||||
xml::XmlResource* res, KeepSet* keep_set,
|
||||
bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keep_set,
|
||||
bool main_dex_only) {
|
||||
ManifestVisitor visitor(source, keep_set, main_dex_only);
|
||||
if (res->root) {
|
||||
@@ -272,8 +259,7 @@ bool CollectProguardRulesForManifest(const Source& source,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollectProguardRules(const Source& source, xml::XmlResource* res,
|
||||
KeepSet* keep_set) {
|
||||
bool CollectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keep_set) {
|
||||
if (!res->root) {
|
||||
return false;
|
||||
}
|
||||
@@ -321,8 +307,7 @@ bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
|
||||
for (const Source& source : entry.second) {
|
||||
*out << "# Referenced at " << source << "\n";
|
||||
}
|
||||
*out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n"
|
||||
<< std::endl;
|
||||
*out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
|
||||
xml::Element* el;
|
||||
xml::Attribute* attr;
|
||||
|
||||
el = xml::FindRootElement(doc.get());
|
||||
el = doc->root.get();
|
||||
ASSERT_NE(nullptr, el);
|
||||
el = el->FindChild({}, "uses-sdk");
|
||||
ASSERT_NE(nullptr, el);
|
||||
@@ -141,7 +141,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
el = xml::FindRootElement(doc.get());
|
||||
el = doc->root.get();
|
||||
ASSERT_NE(nullptr, el);
|
||||
el = el->FindChild({}, "uses-sdk");
|
||||
ASSERT_NE(nullptr, el);
|
||||
@@ -160,7 +160,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
el = xml::FindRootElement(doc.get());
|
||||
el = doc->root.get();
|
||||
ASSERT_NE(nullptr, el);
|
||||
el = el->FindChild({}, "uses-sdk");
|
||||
ASSERT_NE(nullptr, el);
|
||||
@@ -177,7 +177,7 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
el = xml::FindRootElement(doc.get());
|
||||
el = doc->root.get();
|
||||
ASSERT_NE(nullptr, el);
|
||||
el = el->FindChild({}, "uses-sdk");
|
||||
ASSERT_NE(nullptr, el);
|
||||
@@ -199,7 +199,7 @@ TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
xml::Element* manifest_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* manifest_el = doc->root.get();
|
||||
ASSERT_NE(nullptr, manifest_el);
|
||||
ASSERT_EQ("manifest", manifest_el->name);
|
||||
|
||||
@@ -248,7 +248,7 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
xml::Element* manifestEl = xml::FindRootElement(doc.get());
|
||||
xml::Element* manifestEl = doc->root.get();
|
||||
ASSERT_NE(nullptr, manifestEl);
|
||||
|
||||
xml::Attribute* attr = nullptr;
|
||||
@@ -297,7 +297,7 @@ TEST_F(ManifestFixerTest,
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
xml::Element* manifest_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* manifest_el = doc->root.get();
|
||||
ASSERT_NE(nullptr, manifest_el);
|
||||
|
||||
xml::Element* instrumentation_el =
|
||||
@@ -321,7 +321,7 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
|
||||
options);
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
xml::Element* manifest_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* manifest_el = doc->root.get();
|
||||
ASSERT_NE(nullptr, manifest_el);
|
||||
|
||||
xml::Attribute* attr =
|
||||
@@ -344,7 +344,7 @@ TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
|
||||
Verify("<manifest package=\"android\" coreApp=\"true\" />");
|
||||
ASSERT_NE(nullptr, doc);
|
||||
|
||||
xml::Element* el = xml::FindRootElement(doc.get());
|
||||
xml::Element* el = doc->root.get();
|
||||
ASSERT_NE(nullptr, el);
|
||||
|
||||
EXPECT_EQ("manifest", el->name);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_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_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[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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
for (const auto& attr : el->attributes) {
|
||||
EXPECT_EQ(std::string(), attr.namespace_uri);
|
||||
}
|
||||
EXPECT_EQ(std::string(), el->namespace_uri);
|
||||
xml::Visitor::Visit(el);
|
||||
}
|
||||
EXPECT_THAT(el->namespace_decls, SizeIs(0u));
|
||||
|
||||
void Visit(xml::Namespace* ns) override {
|
||||
EXPECT_EQ(std::string(), ns->namespace_uri);
|
||||
xml::Visitor::Visit(ns);
|
||||
for (const auto& attr : el->attributes) {
|
||||
EXPECT_THAT(attr.namespace_uri, StrEq(""));
|
||||
}
|
||||
EXPECT_THAT(el->namespace_uri, StrEq(""));
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -79,20 +79,20 @@ class XmlReferenceLinkerTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/green"
|
||||
android:text="hello"
|
||||
android:attr="\?hello"
|
||||
nonAaptAttr="1"
|
||||
nonAaptAttrRef="@id/id"
|
||||
class="hello" />)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/green"
|
||||
android:text="hello"
|
||||
android:attr="\?hello"
|
||||
nonAaptAttr="1"
|
||||
nonAaptAttrRef="@id/id"
|
||||
class="hello" />)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
xml::Element* view_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* view_el = doc->root.get();
|
||||
ASSERT_THAT(view_el, NotNull());
|
||||
|
||||
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
|
||||
@@ -138,32 +138,32 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="@android:color/hidden" />)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="@android:color/hidden" />)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="@*android:color/hidden" />)EOF");
|
||||
android:colorAccent="@*android:color/hidden" />)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
|
||||
support:colorAccent="#ff0000" />)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
|
||||
support:colorAccent="#ff0000" />)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
xml::Element* view_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* view_el = doc->root.get();
|
||||
ASSERT_THAT(view_el, NotNull());
|
||||
|
||||
xml::Attribute* xml_attr =
|
||||
@@ -175,14 +175,14 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:colorAccent="@app:color/red" />)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:colorAccent="@app:color/red" />)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
xml::Element* view_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* view_el = doc->root.get();
|
||||
ASSERT_THAT(view_el, NotNull());
|
||||
|
||||
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
|
||||
@@ -196,17 +196,15 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/android"
|
||||
app:attr="@app:id/id">
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
|
||||
app:attr="@app:id/id"/>
|
||||
</View>)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
|
||||
</View>)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
xml::Element* view_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* view_el = doc->root.get();
|
||||
ASSERT_THAT(view_el, NotNull());
|
||||
|
||||
// All attributes and references in this element should be referring to
|
||||
@@ -235,14 +233,14 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
|
||||
android:attr="@id/id"/>)EOF");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
|
||||
android:attr="@id/id"/>)");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
xml::Element* view_el = xml::FindRootElement(doc.get());
|
||||
xml::Element* view_el = doc->root.get();
|
||||
ASSERT_THAT(view_el, NotNull());
|
||||
|
||||
// All attributes and references in this element should be referring to
|
||||
|
||||
@@ -36,7 +36,7 @@ void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source*
|
||||
StringPool::Ref ref = src_pool->MakeRef(source.path);
|
||||
out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
|
||||
if (source.line) {
|
||||
out_pb_source->set_line_no(static_cast<uint32_t>(source.line.value()));
|
||||
out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,29 +46,28 @@ void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStri
|
||||
out_source->path = util::GetString(src_pool, pb_source.path_idx());
|
||||
}
|
||||
|
||||
if (pb_source.has_line_no()) {
|
||||
out_source->line = static_cast<size_t>(pb_source.line_no());
|
||||
if (pb_source.has_position()) {
|
||||
out_source->line = static_cast<size_t>(pb_source.position().line_number());
|
||||
}
|
||||
}
|
||||
|
||||
pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
|
||||
switch (state) {
|
||||
case SymbolState::kPrivate:
|
||||
return pb::SymbolStatus_Visibility_Private;
|
||||
return pb::SymbolStatus_Visibility_PRIVATE;
|
||||
case SymbolState::kPublic:
|
||||
return pb::SymbolStatus_Visibility_Public;
|
||||
return pb::SymbolStatus_Visibility_PUBLIC;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return pb::SymbolStatus_Visibility_Unknown;
|
||||
return pb::SymbolStatus_Visibility_UNKNOWN;
|
||||
}
|
||||
|
||||
SymbolState DeserializeVisibilityFromPb(
|
||||
pb::SymbolStatus_Visibility pb_visibility) {
|
||||
SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility) {
|
||||
switch (pb_visibility) {
|
||||
case pb::SymbolStatus_Visibility_Private:
|
||||
case pb::SymbolStatus_Visibility_PRIVATE:
|
||||
return SymbolState::kPrivate;
|
||||
case pb::SymbolStatus_Visibility_Public:
|
||||
case pb::SymbolStatus_Visibility_PUBLIC:
|
||||
return SymbolState::kPublic;
|
||||
default:
|
||||
break;
|
||||
@@ -102,20 +101,20 @@ bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
|
||||
pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
|
||||
switch (type) {
|
||||
case Reference::Type::kResource:
|
||||
return pb::Reference_Type_Ref;
|
||||
return pb::Reference_Type_REFERENCE;
|
||||
case Reference::Type::kAttribute:
|
||||
return pb::Reference_Type_Attr;
|
||||
return pb::Reference_Type_ATTRIBUTE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return pb::Reference_Type_Ref;
|
||||
return pb::Reference_Type_REFERENCE;
|
||||
}
|
||||
|
||||
Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
|
||||
switch (pb_type) {
|
||||
case pb::Reference_Type_Ref:
|
||||
case pb::Reference_Type_REFERENCE:
|
||||
return Reference::Type::kResource;
|
||||
case pb::Reference_Type_Attr:
|
||||
case pb::Reference_Type_ATTRIBUTE:
|
||||
return Reference::Type::kAttribute;
|
||||
default:
|
||||
break;
|
||||
@@ -126,32 +125,32 @@ Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
|
||||
pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
|
||||
switch (plural_idx) {
|
||||
case Plural::Zero:
|
||||
return pb::Plural_Arity_Zero;
|
||||
return pb::Plural_Arity_ZERO;
|
||||
case Plural::One:
|
||||
return pb::Plural_Arity_One;
|
||||
return pb::Plural_Arity_ONE;
|
||||
case Plural::Two:
|
||||
return pb::Plural_Arity_Two;
|
||||
return pb::Plural_Arity_TWO;
|
||||
case Plural::Few:
|
||||
return pb::Plural_Arity_Few;
|
||||
return pb::Plural_Arity_FEW;
|
||||
case Plural::Many:
|
||||
return pb::Plural_Arity_Many;
|
||||
return pb::Plural_Arity_MANY;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return pb::Plural_Arity_Other;
|
||||
return pb::Plural_Arity_OTHER;
|
||||
}
|
||||
|
||||
size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity) {
|
||||
switch (arity) {
|
||||
case pb::Plural_Arity_Zero:
|
||||
case pb::Plural_Arity_ZERO:
|
||||
return Plural::Zero;
|
||||
case pb::Plural_Arity_One:
|
||||
case pb::Plural_Arity_ONE:
|
||||
return Plural::One;
|
||||
case pb::Plural_Arity_Two:
|
||||
case pb::Plural_Arity_TWO:
|
||||
return Plural::Two;
|
||||
case pb::Plural_Arity_Few:
|
||||
case pb::Plural_Arity_FEW:
|
||||
return Plural::Few;
|
||||
case pb::Plural_Arity_Many:
|
||||
case pb::Plural_Arity_MANY:
|
||||
return Plural::Many;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -55,66 +55,61 @@ class ReferenceIdToNameVisitor : public ValueVisitor {
|
||||
|
||||
class PackagePbDeserializer {
|
||||
public:
|
||||
PackagePbDeserializer(const android::ResStringPool* valuePool,
|
||||
const android::ResStringPool* sourcePool,
|
||||
const android::ResStringPool* symbolPool,
|
||||
const Source& source, IDiagnostics* diag)
|
||||
: value_pool_(valuePool),
|
||||
source_pool_(sourcePool),
|
||||
symbol_pool_(symbolPool),
|
||||
source_(source),
|
||||
diag_(diag) {}
|
||||
PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source,
|
||||
IDiagnostics* diag)
|
||||
: source_pool_(sourcePool), source_(source), diag_(diag) {
|
||||
}
|
||||
|
||||
public:
|
||||
bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
|
||||
bool DeserializeFromPb(const pb::Package& pb_package, ResourceTable* table) {
|
||||
Maybe<uint8_t> id;
|
||||
if (pbPackage.has_package_id()) {
|
||||
id = static_cast<uint8_t>(pbPackage.package_id());
|
||||
if (pb_package.has_package_id()) {
|
||||
id = static_cast<uint8_t>(pb_package.package_id());
|
||||
}
|
||||
|
||||
std::map<ResourceId, ResourceNameRef> idIndex;
|
||||
std::map<ResourceId, ResourceNameRef> id_index;
|
||||
|
||||
ResourceTablePackage* pkg = table->CreatePackage(pbPackage.package_name(), id);
|
||||
for (const pb::Type& pbType : pbPackage.types()) {
|
||||
const ResourceType* resType = ParseResourceType(pbType.name());
|
||||
if (!resType) {
|
||||
diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name() << "'");
|
||||
ResourceTablePackage* pkg = table->CreatePackage(pb_package.package_name(), id);
|
||||
for (const pb::Type& pb_type : pb_package.type()) {
|
||||
const ResourceType* res_type = ParseResourceType(pb_type.name());
|
||||
if (res_type == nullptr) {
|
||||
diag_->Error(DiagMessage(source_) << "unknown type '" << pb_type.name() << "'");
|
||||
return {};
|
||||
}
|
||||
|
||||
ResourceTableType* type = pkg->FindOrCreateType(*resType);
|
||||
ResourceTableType* type = pkg->FindOrCreateType(*res_type);
|
||||
|
||||
for (const pb::Entry& pbEntry : pbType.entries()) {
|
||||
ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name());
|
||||
for (const pb::Entry& pb_entry : pb_type.entry()) {
|
||||
ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
|
||||
|
||||
// Deserialize the symbol status (public/private with source and
|
||||
// comments).
|
||||
if (pbEntry.has_symbol_status()) {
|
||||
const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
|
||||
if (pbStatus.has_source()) {
|
||||
DeserializeSourceFromPb(pbStatus.source(), *source_pool_, &entry->symbol_status.source);
|
||||
// Deserialize the symbol status (public/private with source and comments).
|
||||
if (pb_entry.has_symbol_status()) {
|
||||
const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
|
||||
if (pb_status.has_source()) {
|
||||
DeserializeSourceFromPb(pb_status.source(), *source_pool_,
|
||||
&entry->symbol_status.source);
|
||||
}
|
||||
|
||||
if (pbStatus.has_comment()) {
|
||||
entry->symbol_status.comment = pbStatus.comment();
|
||||
if (pb_status.has_comment()) {
|
||||
entry->symbol_status.comment = pb_status.comment();
|
||||
}
|
||||
|
||||
entry->symbol_status.allow_new = pbStatus.allow_new();
|
||||
entry->symbol_status.allow_new = pb_status.allow_new();
|
||||
|
||||
SymbolState visibility = DeserializeVisibilityFromPb(pbStatus.visibility());
|
||||
SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
|
||||
entry->symbol_status.state = visibility;
|
||||
|
||||
if (visibility == SymbolState::kPublic) {
|
||||
// This is a public symbol, we must encode the ID now if there is one.
|
||||
if (pbEntry.has_id()) {
|
||||
entry->id = static_cast<uint16_t>(pbEntry.id());
|
||||
if (pb_entry.has_id()) {
|
||||
entry->id = static_cast<uint16_t>(pb_entry.id());
|
||||
}
|
||||
|
||||
if (type->symbol_status.state != SymbolState::kPublic) {
|
||||
// If the type has not been made public, do so now.
|
||||
type->symbol_status.state = SymbolState::kPublic;
|
||||
if (pbType.has_id()) {
|
||||
type->id = static_cast<uint8_t>(pbType.id());
|
||||
if (pb_type.has_id()) {
|
||||
type->id = static_cast<uint8_t>(pb_type.id());
|
||||
}
|
||||
}
|
||||
} else if (visibility == SymbolState::kPrivate) {
|
||||
@@ -124,45 +119,44 @@ class PackagePbDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
|
||||
if (resId.is_valid()) {
|
||||
idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
|
||||
ResourceId resid(pb_package.package_id(), pb_type.id(), pb_entry.id());
|
||||
if (resid.is_valid()) {
|
||||
id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
|
||||
}
|
||||
|
||||
for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
|
||||
const pb::ConfigDescription& pbConfig = pbConfigValue.config();
|
||||
for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
|
||||
const pb::ConfigDescription& pb_config = pb_config_value.config();
|
||||
|
||||
ConfigDescription config;
|
||||
if (!DeserializeConfigDescriptionFromPb(pbConfig, &config)) {
|
||||
if (!DeserializeConfigDescriptionFromPb(pb_config, &config)) {
|
||||
diag_->Error(DiagMessage(source_) << "invalid configuration");
|
||||
return {};
|
||||
}
|
||||
|
||||
ResourceConfigValue* configValue = entry->FindOrCreateValue(config, pbConfig.product());
|
||||
if (configValue->value) {
|
||||
ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
|
||||
if (config_value->value) {
|
||||
// Duplicate config.
|
||||
diag_->Error(DiagMessage(source_) << "duplicate configuration");
|
||||
return {};
|
||||
}
|
||||
|
||||
configValue->value =
|
||||
DeserializeValueFromPb(pbConfigValue.value(), config, &table->string_pool);
|
||||
if (!configValue->value) {
|
||||
config_value->value =
|
||||
DeserializeValueFromPb(pb_config_value.value(), config, &table->string_pool);
|
||||
if (!config_value->value) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReferenceIdToNameVisitor visitor(&idIndex);
|
||||
ReferenceIdToNameVisitor visitor(&id_index);
|
||||
VisitAllValuesInPackage(pkg, &visitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
|
||||
const ConfigDescription& config,
|
||||
StringPool* pool) {
|
||||
const ConfigDescription& config, StringPool* pool) {
|
||||
if (pb_item.has_ref()) {
|
||||
const pb::Reference& pb_ref = pb_item.ref();
|
||||
std::unique_ptr<Reference> ref = util::make_unique<Reference>();
|
||||
@@ -173,45 +167,32 @@ class PackagePbDeserializer {
|
||||
|
||||
} else if (pb_item.has_prim()) {
|
||||
const pb::Primitive& pb_prim = pb_item.prim();
|
||||
android::Res_value prim = {};
|
||||
prim.dataType = static_cast<uint8_t>(pb_prim.type());
|
||||
prim.data = pb_prim.data();
|
||||
return util::make_unique<BinaryPrimitive>(prim);
|
||||
return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
|
||||
pb_prim.data());
|
||||
|
||||
} else if (pb_item.has_id()) {
|
||||
return util::make_unique<Id>();
|
||||
|
||||
} else if (pb_item.has_str()) {
|
||||
const uint32_t idx = pb_item.str().idx();
|
||||
const std::string str = util::GetString(*value_pool_, idx);
|
||||
|
||||
const android::ResStringPool_span* spans = value_pool_->styleAt(idx);
|
||||
if (spans && spans->name.index != android::ResStringPool_span::END) {
|
||||
StyleString style_str = {str};
|
||||
while (spans->name.index != android::ResStringPool_span::END) {
|
||||
style_str.spans.push_back(
|
||||
Span{util::GetString(*value_pool_, spans->name.index),
|
||||
spans->firstChar, spans->lastChar});
|
||||
spans++;
|
||||
}
|
||||
return util::make_unique<StyledString>(pool->MakeRef(
|
||||
style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
|
||||
}
|
||||
return util::make_unique<String>(
|
||||
pool->MakeRef(str, StringPool::Context(config)));
|
||||
pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
|
||||
|
||||
} else if (pb_item.has_raw_str()) {
|
||||
const uint32_t idx = pb_item.raw_str().idx();
|
||||
const std::string str = util::GetString(*value_pool_, idx);
|
||||
return util::make_unique<RawString>(
|
||||
pool->MakeRef(str, StringPool::Context(config)));
|
||||
pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
|
||||
|
||||
} else if (pb_item.has_styled_str()) {
|
||||
const pb::StyledString& pb_str = pb_item.styled_str();
|
||||
StyleString style_str{pb_str.value()};
|
||||
for (const pb::StyledString::Span& pb_span : pb_str.span()) {
|
||||
style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
|
||||
}
|
||||
return util::make_unique<StyledString>(pool->MakeRef(
|
||||
style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
|
||||
|
||||
} else if (pb_item.has_file()) {
|
||||
const uint32_t idx = pb_item.file().path_idx();
|
||||
const std::string str = util::GetString(*value_pool_, idx);
|
||||
return util::make_unique<FileReference>(pool->MakeRef(
|
||||
str,
|
||||
StringPool::Context(StringPool::Context::kHighPriority, config)));
|
||||
pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
|
||||
|
||||
} else {
|
||||
diag_->Error(DiagMessage(source_) << "unknown item");
|
||||
@@ -222,8 +203,6 @@ class PackagePbDeserializer {
|
||||
std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
const ConfigDescription& config,
|
||||
StringPool* pool) {
|
||||
const bool is_weak = pb_value.has_weak() ? pb_value.weak() : false;
|
||||
|
||||
std::unique_ptr<Value> value;
|
||||
if (pb_value.has_item()) {
|
||||
value = DeserializeItemFromPb(pb_value.item(), config, pool);
|
||||
@@ -235,11 +214,11 @@ class PackagePbDeserializer {
|
||||
const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
|
||||
if (pb_compound_value.has_attr()) {
|
||||
const pb::Attribute& pb_attr = pb_compound_value.attr();
|
||||
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
|
||||
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
|
||||
attr->type_mask = pb_attr.format_flags();
|
||||
attr->min_int = pb_attr.min_int();
|
||||
attr->max_int = pb_attr.max_int();
|
||||
for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbols()) {
|
||||
for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
|
||||
Attribute::Symbol symbol;
|
||||
DeserializeItemCommon(pb_symbol, &symbol.symbol);
|
||||
if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
|
||||
@@ -255,20 +234,18 @@ class PackagePbDeserializer {
|
||||
std::unique_ptr<Style> style = util::make_unique<Style>();
|
||||
if (pb_style.has_parent()) {
|
||||
style->parent = Reference();
|
||||
if (!DeserializeReferenceFromPb(pb_style.parent(),
|
||||
&style->parent.value())) {
|
||||
if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pb_style.has_parent_source()) {
|
||||
Source parent_source;
|
||||
DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_,
|
||||
&parent_source);
|
||||
DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source);
|
||||
style->parent.value().SetSource(std::move(parent_source));
|
||||
}
|
||||
}
|
||||
|
||||
for (const pb::Style_Entry& pb_entry : pb_style.entries()) {
|
||||
for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
|
||||
Style::Entry entry;
|
||||
DeserializeItemCommon(pb_entry, &entry.key);
|
||||
if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
|
||||
@@ -288,7 +265,7 @@ class PackagePbDeserializer {
|
||||
} else if (pb_compound_value.has_styleable()) {
|
||||
const pb::Styleable& pb_styleable = pb_compound_value.styleable();
|
||||
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
|
||||
for (const pb::Styleable_Entry& pb_entry : pb_styleable.entries()) {
|
||||
for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
|
||||
Reference attr_ref;
|
||||
DeserializeItemCommon(pb_entry, &attr_ref);
|
||||
DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
|
||||
@@ -299,25 +276,23 @@ class PackagePbDeserializer {
|
||||
} else if (pb_compound_value.has_array()) {
|
||||
const pb::Array& pb_array = pb_compound_value.array();
|
||||
std::unique_ptr<Array> array = util::make_unique<Array>();
|
||||
for (const pb::Array_Entry& pb_entry : pb_array.entries()) {
|
||||
std::unique_ptr<Item> item =
|
||||
DeserializeItemFromPb(pb_entry.item(), config, pool);
|
||||
for (const pb::Array_Element& pb_entry : pb_array.element()) {
|
||||
std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool);
|
||||
if (!item) {
|
||||
return {};
|
||||
}
|
||||
|
||||
DeserializeItemCommon(pb_entry, item.get());
|
||||
array->items.push_back(std::move(item));
|
||||
array->elements.push_back(std::move(item));
|
||||
}
|
||||
value = std::move(array);
|
||||
|
||||
} else if (pb_compound_value.has_plural()) {
|
||||
const pb::Plural& pb_plural = pb_compound_value.plural();
|
||||
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
|
||||
for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) {
|
||||
for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
|
||||
size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
|
||||
plural->values[pluralIdx] =
|
||||
DeserializeItemFromPb(pb_entry.item(), config, pool);
|
||||
plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool);
|
||||
if (!plural->values[pluralIdx]) {
|
||||
return {};
|
||||
}
|
||||
@@ -337,7 +312,7 @@ class PackagePbDeserializer {
|
||||
|
||||
CHECK(value) << "forgot to set value";
|
||||
|
||||
value->SetWeak(is_weak);
|
||||
value->SetWeak(pb_value.weak());
|
||||
DeserializeItemCommon(pb_value, value.get());
|
||||
return value;
|
||||
}
|
||||
@@ -350,11 +325,10 @@ class PackagePbDeserializer {
|
||||
out_ref->id = ResourceId(pb_ref.id());
|
||||
}
|
||||
|
||||
if (pb_ref.has_symbol_idx()) {
|
||||
const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx());
|
||||
if (pb_ref.has_name()) {
|
||||
ResourceNameRef name_ref;
|
||||
if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
|
||||
diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'");
|
||||
if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
|
||||
diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -377,61 +351,33 @@ class PackagePbDeserializer {
|
||||
}
|
||||
|
||||
private:
|
||||
const android::ResStringPool* value_pool_;
|
||||
const android::ResStringPool* source_pool_;
|
||||
const android::ResStringPool* symbol_pool_;
|
||||
const Source source_;
|
||||
IDiagnostics* diag_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ResourceTable> DeserializeTableFromPb(
|
||||
const pb::ResourceTable& pb_table, const Source& source,
|
||||
IDiagnostics* diag) {
|
||||
// We import the android namespace because on Windows NO_ERROR is a macro, not
|
||||
// an enum, which
|
||||
std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table,
|
||||
const Source& source, IDiagnostics* diag) {
|
||||
// We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
|
||||
// causes errors when qualifying it with android::
|
||||
using namespace android;
|
||||
|
||||
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
||||
|
||||
if (!pb_table.has_string_pool()) {
|
||||
diag->Error(DiagMessage(source) << "no string pool found");
|
||||
return {};
|
||||
}
|
||||
|
||||
ResStringPool value_pool;
|
||||
status_t result = value_pool.setTo(pb_table.string_pool().data().data(),
|
||||
pb_table.string_pool().data().size());
|
||||
if (result != NO_ERROR) {
|
||||
diag->Error(DiagMessage(source) << "invalid string pool");
|
||||
return {};
|
||||
}
|
||||
|
||||
ResStringPool source_pool;
|
||||
if (pb_table.has_source_pool()) {
|
||||
result = source_pool.setTo(pb_table.source_pool().data().data(),
|
||||
pb_table.source_pool().data().size());
|
||||
status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
|
||||
pb_table.source_pool().data().size());
|
||||
if (result != NO_ERROR) {
|
||||
diag->Error(DiagMessage(source) << "invalid source pool");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ResStringPool symbol_pool;
|
||||
if (pb_table.has_symbol_pool()) {
|
||||
result = symbol_pool.setTo(pb_table.symbol_pool().data().data(),
|
||||
pb_table.symbol_pool().data().size());
|
||||
if (result != NO_ERROR) {
|
||||
diag->Error(DiagMessage(source) << "invalid symbol pool");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool,
|
||||
&symbol_pool, source, diag);
|
||||
for (const pb::Package& pb_package : pb_table.packages()) {
|
||||
PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag);
|
||||
for (const pb::Package& pb_package : pb_table.package()) {
|
||||
if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
|
||||
return {};
|
||||
}
|
||||
@@ -440,7 +386,7 @@ std::unique_ptr<ResourceTable> DeserializeTableFromPb(
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
|
||||
const pb::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
|
||||
const pb::internal::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
|
||||
std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
|
||||
|
||||
ResourceNameRef name_ref;
|
||||
@@ -456,19 +402,20 @@ std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
|
||||
file->source.path = pb_file.source_path();
|
||||
DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
|
||||
|
||||
for (const pb::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbols()) {
|
||||
// Need to create an lvalue here so that nameRef can point to something
|
||||
// real.
|
||||
if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(),
|
||||
&name_ref)) {
|
||||
for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
|
||||
// Need to create an lvalue here so that nameRef can point to something real.
|
||||
if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
|
||||
diag->Error(DiagMessage(source)
|
||||
<< "invalid resource name for exported symbol in "
|
||||
"compiled file header: "
|
||||
<< pb_file.resource_name());
|
||||
return {};
|
||||
}
|
||||
file->exported_symbols.push_back(
|
||||
SourcedResourceName{name_ref.ToResourceName(), pb_symbol.line_no()});
|
||||
size_t line = 0u;
|
||||
if (pb_symbol.has_source()) {
|
||||
line = pb_symbol.source().line_number();
|
||||
}
|
||||
file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -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 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, 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 an Item.
|
||||
PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem)
|
||||
: source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {
|
||||
}
|
||||
|
||||
void Visit(Reference* ref) override {
|
||||
SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
|
||||
}
|
||||
|
||||
void Visit(String* str) override {
|
||||
pb_item()->mutable_str()->set_idx(str->value.index());
|
||||
pb_item()->mutable_str()->set_value(*str->value);
|
||||
}
|
||||
|
||||
void Visit(RawString* str) override {
|
||||
pb_item()->mutable_raw_str()->set_value(*str->value);
|
||||
}
|
||||
|
||||
void Visit(StyledString* str) override {
|
||||
pb_item()->mutable_str()->set_idx(str->value.index());
|
||||
pb::StyledString* pb_str = pb_item()->mutable_styled_str();
|
||||
pb_str->set_value(str->value->value);
|
||||
|
||||
for (const StringPool::Span& span : str->value->spans) {
|
||||
pb::StyledString::Span* pb_span = pb_str->add_span();
|
||||
pb_span->set_tag(*span.name);
|
||||
pb_span->set_first_char(span.first_char);
|
||||
pb_span->set_last_char(span.last_char);
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(FileReference* file) override {
|
||||
pb_item()->mutable_file()->set_path_idx(file->path.index());
|
||||
pb_item()->mutable_file()->set_path(*file->path);
|
||||
}
|
||||
|
||||
void Visit(Id* id) override { pb_item()->mutable_id(); }
|
||||
|
||||
void Visit(RawString* raw_str) override {
|
||||
pb_item()->mutable_raw_str()->set_idx(raw_str->value.index());
|
||||
void Visit(Id* /*id*/) override {
|
||||
pb_item()->mutable_id();
|
||||
}
|
||||
|
||||
void Visit(BinaryPrimitive* prim) override {
|
||||
@@ -98,7 +98,7 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
pb_attr->set_max_int(attr->max_int);
|
||||
|
||||
for (auto& symbol : attr->symbols) {
|
||||
pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbols();
|
||||
pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
|
||||
SerializeItemCommonToPb(symbol.symbol, pb_symbol);
|
||||
SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
|
||||
pb_symbol->set_value(symbol.value);
|
||||
@@ -114,12 +114,12 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
for (Style::Entry& entry : style->entries) {
|
||||
pb::Style_Entry* pb_entry = pb_style->add_entries();
|
||||
pb::Style_Entry* pb_entry = pb_style->add_entry();
|
||||
SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
|
||||
|
||||
pb::Item* pb_item = pb_entry->mutable_item();
|
||||
SerializeItemCommonToPb(entry.key, pb_entry);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, pb_item);
|
||||
entry.value->Accept(&sub_visitor);
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
void Visit(Styleable* styleable) override {
|
||||
pb::Styleable* pb_styleable = pb_compound_value()->mutable_styleable();
|
||||
for (Reference& entry : styleable->entries) {
|
||||
pb::Styleable_Entry* pb_entry = pb_styleable->add_entries();
|
||||
pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
|
||||
SerializeItemCommonToPb(entry, pb_entry);
|
||||
SerializeReferenceToPb(entry, pb_entry->mutable_attr());
|
||||
}
|
||||
@@ -135,11 +135,10 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
|
||||
void Visit(Array* array) override {
|
||||
pb::Array* pb_array = pb_compound_value()->mutable_array();
|
||||
for (auto& value : array->items) {
|
||||
pb::Array_Entry* pb_entry = pb_array->add_entries();
|
||||
SerializeItemCommonToPb(*value, pb_entry);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_,
|
||||
pb_entry->mutable_item());
|
||||
for (auto& value : array->elements) {
|
||||
pb::Array_Element* pb_element = pb_array->add_element();
|
||||
SerializeItemCommonToPb(*value, pb_element);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, pb_element->mutable_item());
|
||||
value->Accept(&sub_visitor);
|
||||
}
|
||||
}
|
||||
@@ -153,11 +152,11 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
continue;
|
||||
}
|
||||
|
||||
pb::Plural_Entry* pb_entry = pb_plural->add_entries();
|
||||
pb::Plural_Entry* pb_entry = pb_plural->add_entry();
|
||||
pb_entry->set_arity(SerializePluralEnumToPb(i));
|
||||
pb::Item* pb_element = pb_entry->mutable_item();
|
||||
SerializeItemCommonToPb(*plural->values[i], pb_entry);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element);
|
||||
PbSerializerVisitor sub_visitor(source_pool_, pb_element);
|
||||
plural->values[i]->Accept(&sub_visitor);
|
||||
}
|
||||
}
|
||||
@@ -179,8 +178,7 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
|
||||
template <typename T>
|
||||
void SerializeItemCommonToPb(const Item& item, T* pb_item) {
|
||||
SerializeSourceToPb(item.GetSource(), source_pool_,
|
||||
pb_item->mutable_source());
|
||||
SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source());
|
||||
if (!item.GetComment().empty()) {
|
||||
pb_item->set_comment(item.GetComment());
|
||||
}
|
||||
@@ -192,8 +190,7 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
if (ref.name) {
|
||||
StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString());
|
||||
pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index()));
|
||||
pb_ref->set_name(ref.name.value().ToString());
|
||||
}
|
||||
|
||||
pb_ref->set_private_(ref.private_reference);
|
||||
@@ -201,7 +198,6 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
StringPool* source_pool_;
|
||||
StringPool* symbol_pool_;
|
||||
pb::Value* out_pb_value_;
|
||||
pb::Item* out_pb_item_;
|
||||
};
|
||||
@@ -220,26 +216,24 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
|
||||
});
|
||||
|
||||
auto pb_table = util::make_unique<pb::ResourceTable>();
|
||||
SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool());
|
||||
|
||||
StringPool source_pool, symbol_pool;
|
||||
StringPool source_pool;
|
||||
|
||||
for (auto& package : table->packages) {
|
||||
pb::Package* pb_package = pb_table->add_packages();
|
||||
pb::Package* pb_package = pb_table->add_package();
|
||||
if (package->id) {
|
||||
pb_package->set_package_id(package->id.value());
|
||||
}
|
||||
pb_package->set_package_name(package->name);
|
||||
|
||||
for (auto& type : package->types) {
|
||||
pb::Type* pb_type = pb_package->add_types();
|
||||
pb::Type* pb_type = pb_package->add_type();
|
||||
if (type->id) {
|
||||
pb_type->set_id(type->id.value());
|
||||
}
|
||||
pb_type->set_name(ToString(type->type).to_string());
|
||||
|
||||
for (auto& entry : type->entries) {
|
||||
pb::Entry* pb_entry = pb_type->add_entries();
|
||||
pb::Entry* pb_entry = pb_type->add_entry();
|
||||
if (entry->id) {
|
||||
pb_entry->set_id(entry->id.value());
|
||||
}
|
||||
@@ -253,7 +247,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
|
||||
pb_status->set_allow_new(entry->symbol_status.allow_new);
|
||||
|
||||
for (auto& config_value : entry->values) {
|
||||
pb::ConfigValue* pb_config_value = pb_entry->add_config_values();
|
||||
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
|
||||
SerializeConfig(config_value->config, pb_config_value->mutable_config());
|
||||
if (!config_value->product.empty()) {
|
||||
pb_config_value->mutable_config()->set_product(config_value->product);
|
||||
@@ -270,7 +264,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
|
||||
pb_value->set_weak(true);
|
||||
}
|
||||
|
||||
PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value);
|
||||
PbSerializerVisitor visitor(&source_pool, pb_value);
|
||||
config_value->value->Accept(&visitor);
|
||||
}
|
||||
}
|
||||
@@ -278,27 +272,25 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
|
||||
}
|
||||
|
||||
SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
|
||||
SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool());
|
||||
return pb_table;
|
||||
}
|
||||
|
||||
std::unique_ptr<pb::CompiledFile> SerializeCompiledFileToPb(
|
||||
const ResourceFile& file) {
|
||||
auto pb_file = util::make_unique<pb::CompiledFile>();
|
||||
std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file) {
|
||||
auto pb_file = util::make_unique<pb::internal::CompiledFile>();
|
||||
pb_file->set_resource_name(file.name.ToString());
|
||||
pb_file->set_source_path(file.source.path);
|
||||
SerializeConfig(file.config, pb_file->mutable_config());
|
||||
|
||||
for (const SourcedResourceName& exported : file.exported_symbols) {
|
||||
pb::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbols();
|
||||
pb::internal::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbol();
|
||||
pb_symbol->set_resource_name(exported.name.ToString());
|
||||
pb_symbol->set_line_no(exported.line);
|
||||
pb_symbol->mutable_source()->set_line_number(exported.line);
|
||||
}
|
||||
return pb_file;
|
||||
}
|
||||
|
||||
CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out)
|
||||
: out_(out) {}
|
||||
CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
|
||||
}
|
||||
|
||||
void CompiledFileOutputStream::EnsureAlignedWrite() {
|
||||
const int padding = out_.ByteCount() % 4;
|
||||
@@ -313,8 +305,7 @@ void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
|
||||
out_.WriteLittleEndian32(val);
|
||||
}
|
||||
|
||||
void CompiledFileOutputStream::WriteCompiledFile(
|
||||
const pb::CompiledFile* compiled_file) {
|
||||
void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile* compiled_file) {
|
||||
EnsureAlignedWrite();
|
||||
out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file->ByteSize()));
|
||||
compiled_file->SerializeWithCachedSizes(&out_);
|
||||
@@ -334,7 +325,9 @@ void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
|
||||
out_.WriteRaw(data, len);
|
||||
}
|
||||
|
||||
bool CompiledFileOutputStream::HadError() { return out_.HadError(); }
|
||||
bool CompiledFileOutputStream::HadError() {
|
||||
return out_.HadError();
|
||||
}
|
||||
|
||||
CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
|
||||
: in_(static_cast<const uint8_t*>(data), size) {}
|
||||
@@ -352,7 +345,7 @@ bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
|
||||
return in_.ReadLittleEndian32(out_val);
|
||||
}
|
||||
|
||||
bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
|
||||
bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
|
||||
EnsureAlignedRead();
|
||||
|
||||
google::protobuf::uint64 pb_size = 0u;
|
||||
@@ -379,8 +372,7 @@ bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset,
|
||||
uint64_t* out_len) {
|
||||
bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
|
||||
EnsureAlignedRead();
|
||||
|
||||
google::protobuf::uint64 pb_size = 0u;
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#include "test/Test.h"
|
||||
|
||||
using ::google::protobuf::io::StringOutputStream;
|
||||
using ::testing::Eq;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::SizeIs;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -38,12 +40,12 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
|
||||
Symbol public_symbol;
|
||||
public_symbol.state = SymbolState::kPublic;
|
||||
ASSERT_TRUE(table->SetSymbolState(
|
||||
test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000),
|
||||
public_symbol, context->GetDiagnostics()));
|
||||
ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
|
||||
ResourceId(0x7f020000), public_symbol,
|
||||
context->GetDiagnostics()));
|
||||
|
||||
Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
|
||||
ASSERT_NE(nullptr, id);
|
||||
ASSERT_THAT(id, NotNull());
|
||||
|
||||
// Make a plural.
|
||||
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
|
||||
@@ -52,6 +54,15 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
ConfigDescription{}, {}, std::move(plural),
|
||||
context->GetDiagnostics()));
|
||||
|
||||
// Make a styled string.
|
||||
StyleString style_string;
|
||||
style_string.str = "hello";
|
||||
style_string.spans.push_back(Span{"b", 0u, 4u});
|
||||
ASSERT_TRUE(
|
||||
table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
|
||||
util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
|
||||
context->GetDiagnostics()));
|
||||
|
||||
// Make a resource with different products.
|
||||
ASSERT_TRUE(table->AddResource(
|
||||
test::ParseNameOrDie("com.app.a:integer/one"),
|
||||
@@ -65,9 +76,8 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
context->GetDiagnostics()));
|
||||
|
||||
// Make a reference with both resource name and resource ID.
|
||||
// The reference should point to a resource outside of this table to test that
|
||||
// both
|
||||
// name and id get serialized.
|
||||
// The reference should point to a resource outside of this table to test that both name and id
|
||||
// get serialized.
|
||||
Reference expected_ref;
|
||||
expected_ref.name = test::ParseNameOrDie("android:layout/main");
|
||||
expected_ref.id = ResourceId(0x01020000);
|
||||
@@ -85,36 +95,45 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
|
||||
Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
|
||||
ASSERT_THAT(new_id, NotNull());
|
||||
EXPECT_EQ(id->IsWeak(), new_id->IsWeak());
|
||||
EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
|
||||
|
||||
Maybe<ResourceTable::SearchResult> result =
|
||||
new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
|
||||
ASSERT_TRUE(result);
|
||||
EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
|
||||
EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
|
||||
|
||||
EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
|
||||
EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
|
||||
|
||||
result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
|
||||
ASSERT_TRUE(result);
|
||||
EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state);
|
||||
EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
|
||||
EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
|
||||
|
||||
// Find the product-dependent values
|
||||
BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
|
||||
new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
|
||||
ASSERT_THAT(prim, NotNull());
|
||||
EXPECT_EQ(123u, prim->value.data);
|
||||
EXPECT_THAT(prim->value.data, Eq(123u));
|
||||
|
||||
prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
|
||||
new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
|
||||
ASSERT_THAT(prim, NotNull());
|
||||
EXPECT_EQ(321u, prim->value.data);
|
||||
EXPECT_THAT(prim->value.data, Eq(321u));
|
||||
|
||||
Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
|
||||
ASSERT_THAT(actual_ref, NotNull());
|
||||
ASSERT_TRUE(actual_ref->name);
|
||||
ASSERT_TRUE(actual_ref->id);
|
||||
EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value());
|
||||
EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value());
|
||||
EXPECT_THAT(*actual_ref, Eq(expected_ref));
|
||||
|
||||
StyledString* actual_styled_str =
|
||||
test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled");
|
||||
ASSERT_THAT(actual_styled_str, NotNull());
|
||||
EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
|
||||
ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
|
||||
EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
|
||||
EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
|
||||
EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
|
||||
}
|
||||
|
||||
TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
@@ -132,10 +151,10 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
|
||||
std::string output_str;
|
||||
{
|
||||
std::unique_ptr<pb::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
|
||||
std::unique_ptr<pb::internal::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
|
||||
|
||||
f.name.entry = "__" + f.name.entry + "$0";
|
||||
std::unique_ptr<pb::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
|
||||
std::unique_ptr<pb::internal::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
|
||||
|
||||
StringOutputStream out_stream(&output_str);
|
||||
CompiledFileOutputStream out_file_stream(&out_stream);
|
||||
@@ -154,7 +173,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
|
||||
// Read the first compiled file.
|
||||
|
||||
pb::CompiledFile new_pb_file;
|
||||
pb::internal::CompiledFile new_pb_file;
|
||||
ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
|
||||
|
||||
std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(
|
||||
@@ -191,7 +210,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
|
||||
TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
|
||||
ResourceFile f;
|
||||
std::unique_ptr<pb::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
|
||||
std::unique_ptr<pb::internal::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
|
||||
|
||||
const std::string expected_data = "1234";
|
||||
|
||||
@@ -213,7 +232,7 @@ TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
|
||||
EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
|
||||
EXPECT_EQ(1u, num_files);
|
||||
|
||||
pb::CompiledFile new_pb_file;
|
||||
pb::internal::CompiledFile new_pb_file;
|
||||
EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
|
||||
|
||||
uint64_t offset, len;
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
|
||||
|
||||
## Version 2.19
|
||||
- Added navigation resource type.
|
||||
- Fixed issue with resource deduplication. (bug 64397629)
|
||||
- Added a daemon mode for issuing commands. This is invoked with `aapt2 daemon`.
|
||||
Command line arguments are separated by newlines, with an empty line signalling the
|
||||
end of a command. Sending `EOF (Ctrl+D)` to the daemon will exit.
|
||||
- Fixed an issue where multiple permissions defined in AndroidManifest.xml would generate
|
||||
conflicting definitions for the same Java constant in Manifest.java. Changed the implementation
|
||||
to match that of `aapt`, which will take the last definition as the sole definition.
|
||||
A warning is logged if such a scenario occurs. (bug 64472942)
|
||||
- Made improvements to handling of paths on Windows. This should resolve a lot of issues with
|
||||
Unicode paths. (bug 62336414, 63830502)
|
||||
|
||||
## Version 2.18
|
||||
### `aapt2 ...`
|
||||
- Fixed issue where enum values were interpreted as integers and range checked. (bug 62358540)
|
||||
|
||||
216
tools/aapt2/test/Builders.cpp
Normal file
216
tools/aapt2/test/Builders.cpp
Normal file
@@ -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
|
||||
@@ -19,13 +19,13 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "test/Common.h"
|
||||
#include "util/Util.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
@@ -35,97 +35,37 @@ class ResourceTableBuilder {
|
||||
public:
|
||||
ResourceTableBuilder() = default;
|
||||
|
||||
StringPool* string_pool() { return &table_->string_pool; }
|
||||
|
||||
ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id) {
|
||||
ResourceTablePackage* package = table_->CreatePackage(package_name, id);
|
||||
CHECK(package != nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}) {
|
||||
return AddValue(name, id, util::make_unique<Id>());
|
||||
}
|
||||
|
||||
ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id);
|
||||
ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {});
|
||||
ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config,
|
||||
const ResourceId& id = {}) {
|
||||
return AddValue(name, config, id, util::make_unique<Id>());
|
||||
}
|
||||
|
||||
const ResourceId& id = {});
|
||||
ResourceTableBuilder& AddReference(const android::StringPiece& name,
|
||||
const android::StringPiece& ref) {
|
||||
return AddReference(name, {}, ref);
|
||||
}
|
||||
|
||||
const android::StringPiece& ref);
|
||||
ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id,
|
||||
const android::StringPiece& ref) {
|
||||
return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
|
||||
}
|
||||
|
||||
const android::StringPiece& ref);
|
||||
ResourceTableBuilder& AddString(const android::StringPiece& name,
|
||||
const android::StringPiece& str) {
|
||||
return AddString(name, {}, str);
|
||||
}
|
||||
|
||||
const android::StringPiece& str);
|
||||
ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
|
||||
const android::StringPiece& str) {
|
||||
return AddValue(
|
||||
name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
|
||||
}
|
||||
|
||||
const android::StringPiece& str);
|
||||
ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
|
||||
const ConfigDescription& config,
|
||||
const android::StringPiece& str) {
|
||||
return AddValue(name, config, id, util::make_unique<String>(
|
||||
table_->string_pool.MakeRef(str)));
|
||||
}
|
||||
|
||||
const ConfigDescription& config, const android::StringPiece& str);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
|
||||
const android::StringPiece& path) {
|
||||
return AddFileReference(name, {}, path);
|
||||
}
|
||||
|
||||
const android::StringPiece& path);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id,
|
||||
const android::StringPiece& path) {
|
||||
return AddValue(name, id, util::make_unique<FileReference>(
|
||||
table_->string_pool.MakeRef(path)));
|
||||
}
|
||||
|
||||
const android::StringPiece& path);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
|
||||
const android::StringPiece& path,
|
||||
const ConfigDescription& config) {
|
||||
return AddValue(name, config, {}, util::make_unique<FileReference>(
|
||||
table_->string_pool.MakeRef(path)));
|
||||
}
|
||||
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value) {
|
||||
return AddValue(name, {}, std::move(value));
|
||||
}
|
||||
|
||||
const ConfigDescription& config);
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value);
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id,
|
||||
std::unique_ptr<Value> value) {
|
||||
return AddValue(name, {}, id, std::move(value));
|
||||
}
|
||||
|
||||
std::unique_ptr<Value> value);
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
|
||||
const ResourceId& id, std::unique_ptr<Value> value) {
|
||||
ResourceName res_name = ParseNameOrDie(name);
|
||||
CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
|
||||
GetDiagnostics()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ResourceId& id, std::unique_ptr<Value> value);
|
||||
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
|
||||
SymbolState state, bool allow_new = false) {
|
||||
ResourceName res_name = ParseNameOrDie(name);
|
||||
Symbol symbol;
|
||||
symbol.state = state;
|
||||
symbol.allow_new = allow_new;
|
||||
CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
|
||||
return *this;
|
||||
}
|
||||
SymbolState state, bool allow_new = false);
|
||||
|
||||
std::unique_ptr<ResourceTable> Build() { return std::move(table_); }
|
||||
StringPool* string_pool();
|
||||
std::unique_ptr<ResourceTable> Build();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
|
||||
@@ -133,29 +73,16 @@ class ResourceTableBuilder {
|
||||
std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
|
||||
};
|
||||
|
||||
inline std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
|
||||
const Maybe<ResourceId>& id = {}) {
|
||||
std::unique_ptr<Reference> reference =
|
||||
util::make_unique<Reference>(ParseNameOrDie(ref));
|
||||
reference->id = id;
|
||||
return reference;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type,
|
||||
uint32_t data) {
|
||||
android::Res_value value = {};
|
||||
value.size = sizeof(value);
|
||||
value.dataType = type;
|
||||
value.data = data;
|
||||
return util::make_unique<BinaryPrimitive>(value);
|
||||
}
|
||||
std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
|
||||
const Maybe<ResourceId>& id = {});
|
||||
std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
|
||||
|
||||
template <typename T>
|
||||
class ValueBuilder {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit ValueBuilder(Args&&... args)
|
||||
: value_(new T{std::forward<Args>(args)...}) {}
|
||||
explicit ValueBuilder(Args&&... args) : value_(new T{std::forward<Args>(args)...}) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
ValueBuilder& SetSource(Args&&... args) {
|
||||
@@ -168,7 +95,9 @@ class ValueBuilder {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Value> Build() { return std::move(value_); }
|
||||
std::unique_ptr<Value> Build() {
|
||||
return std::move(value_);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ValueBuilder);
|
||||
@@ -178,23 +107,10 @@ class ValueBuilder {
|
||||
|
||||
class AttributeBuilder {
|
||||
public:
|
||||
explicit AttributeBuilder(bool weak = false)
|
||||
: attr_(util::make_unique<Attribute>(weak)) {
|
||||
attr_->type_mask = android::ResTable_map::TYPE_ANY;
|
||||
}
|
||||
|
||||
AttributeBuilder& SetTypeMask(uint32_t typeMask) {
|
||||
attr_->type_mask = typeMask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value) {
|
||||
attr_->symbols.push_back(Attribute::Symbol{
|
||||
Reference(ResourceName({}, ResourceType::kId, name)), value});
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Attribute> Build() { return std::move(attr_); }
|
||||
explicit AttributeBuilder(bool weak = false);
|
||||
AttributeBuilder& SetTypeMask(uint32_t typeMask);
|
||||
AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value);
|
||||
std::unique_ptr<Attribute> Build();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AttributeBuilder);
|
||||
@@ -205,27 +121,11 @@ class AttributeBuilder {
|
||||
class StyleBuilder {
|
||||
public:
|
||||
StyleBuilder() = default;
|
||||
|
||||
StyleBuilder& SetParent(const android::StringPiece& str) {
|
||||
style_->parent = Reference(ParseNameOrDie(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value) {
|
||||
style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
|
||||
return *this;
|
||||
}
|
||||
|
||||
StyleBuilder& SetParent(const android::StringPiece& str);
|
||||
StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value);
|
||||
StyleBuilder& AddItem(const android::StringPiece& str, const ResourceId& id,
|
||||
std::unique_ptr<Item> value) {
|
||||
AddItem(str, std::move(value));
|
||||
style_->entries.back().key.id = id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Style> Build() {
|
||||
return std::move(style_);
|
||||
}
|
||||
std::unique_ptr<Item> value);
|
||||
std::unique_ptr<Style> Build();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(StyleBuilder);
|
||||
@@ -236,14 +136,8 @@ class StyleBuilder {
|
||||
class StyleableBuilder {
|
||||
public:
|
||||
StyleableBuilder() = default;
|
||||
|
||||
StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}) {
|
||||
styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
|
||||
styleable_->entries.back().id = id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Styleable> Build() { return std::move(styleable_); }
|
||||
StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
|
||||
std::unique_ptr<Styleable> Build();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(StyleableBuilder);
|
||||
@@ -251,22 +145,9 @@ class StyleableBuilder {
|
||||
std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>();
|
||||
};
|
||||
|
||||
inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str) {
|
||||
std::stringstream in;
|
||||
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<xml::XmlResource> doc =
|
||||
xml::Inflate(&in, &diag, Source("test.xml"));
|
||||
CHECK(doc != nullptr) << "failed to parse inline XML string";
|
||||
return doc;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(
|
||||
IAaptContext* context, const android::StringPiece& str) {
|
||||
std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
|
||||
doc->file.name.package = context->GetCompilationPackage();
|
||||
return doc;
|
||||
}
|
||||
std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str);
|
||||
std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
|
||||
const android::StringPiece& str);
|
||||
|
||||
} // namespace test
|
||||
} // namespace aapt
|
||||
|
||||
@@ -142,10 +142,97 @@ MATCHER_P(StrEq, a,
|
||||
return android::StringPiece16(arg) == a;
|
||||
}
|
||||
|
||||
MATCHER_P(ValueEq, a,
|
||||
std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
|
||||
return arg.Equals(&a);
|
||||
}
|
||||
class ValueEq {
|
||||
public:
|
||||
template <typename arg_type>
|
||||
class BaseImpl : public ::testing::MatcherInterface<arg_type> {
|
||||
BaseImpl(const BaseImpl&) = default;
|
||||
|
||||
void DescribeTo(::std::ostream* os) const override {
|
||||
*os << "is equal to " << *expected_;
|
||||
}
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
*os << "is not equal to " << *expected_;
|
||||
}
|
||||
|
||||
protected:
|
||||
BaseImpl(const Value* expected) : expected_(expected) {
|
||||
}
|
||||
|
||||
const Value* expected_;
|
||||
};
|
||||
|
||||
template <typename T, bool>
|
||||
class Impl {};
|
||||
|
||||
template <typename T>
|
||||
class Impl<T, false> : public ::testing::MatcherInterface<T> {
|
||||
public:
|
||||
explicit Impl(const Value* expected) : expected_(expected) {
|
||||
}
|
||||
|
||||
bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
|
||||
return expected_->Equals(&x);
|
||||
}
|
||||
|
||||
void DescribeTo(::std::ostream* os) const override {
|
||||
*os << "is equal to " << *expected_;
|
||||
}
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
*os << "is not equal to " << *expected_;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Impl);
|
||||
|
||||
const Value* expected_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Impl<T, true> : public ::testing::MatcherInterface<T> {
|
||||
public:
|
||||
explicit Impl(const Value* expected) : expected_(expected) {
|
||||
}
|
||||
|
||||
bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
|
||||
return expected_->Equals(x);
|
||||
}
|
||||
|
||||
void DescribeTo(::std::ostream* os) const override {
|
||||
*os << "is equal to " << *expected_;
|
||||
}
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const override {
|
||||
*os << "is not equal to " << *expected_;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Impl);
|
||||
|
||||
const Value* expected_;
|
||||
};
|
||||
|
||||
ValueEq(const Value& expected) : expected_(&expected) {
|
||||
}
|
||||
ValueEq(const Value* expected) : expected_(expected) {
|
||||
}
|
||||
ValueEq(const ValueEq&) = default;
|
||||
|
||||
template <typename T>
|
||||
operator ::testing::Matcher<T>() const {
|
||||
return ::testing::Matcher<T>(new Impl<T, std::is_pointer<T>::value>(expected_));
|
||||
}
|
||||
|
||||
private:
|
||||
const Value* expected_;
|
||||
};
|
||||
|
||||
// MATCHER_P(ValueEq, a,
|
||||
// std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
|
||||
// return arg.Equals(&a);
|
||||
//}
|
||||
|
||||
MATCHER_P(StrValueEq, a,
|
||||
std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
|
||||
|
||||
@@ -544,7 +544,7 @@ std::unique_ptr<Array> BinaryResourceParser::ParseArray(
|
||||
const ResTable_map_entry* map) {
|
||||
std::unique_ptr<Array> array = util::make_unique<Array>();
|
||||
for (const ResTable_map& map_entry : map) {
|
||||
array->items.push_back(ParseValue(name, config, map_entry.value));
|
||||
array->elements.push_back(ParseValue(name, config, map_entry.value));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -228,6 +228,12 @@ class Tokenizer {
|
||||
public:
|
||||
class iterator {
|
||||
public:
|
||||
using reference = android::StringPiece&;
|
||||
using value_type = android::StringPiece;
|
||||
using difference_type = size_t;
|
||||
using pointer = android::StringPiece*;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
iterator(const iterator&) = default;
|
||||
iterator& operator=(const iterator&) = default;
|
||||
|
||||
@@ -250,9 +256,13 @@ class Tokenizer {
|
||||
|
||||
Tokenizer(android::StringPiece str, char sep);
|
||||
|
||||
iterator begin() { return begin_; }
|
||||
iterator begin() const {
|
||||
return begin_;
|
||||
}
|
||||
|
||||
iterator end() { return end_; }
|
||||
iterator end() const {
|
||||
return end_;
|
||||
}
|
||||
|
||||
private:
|
||||
const iterator begin_;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
void Text::Accept(Visitor* visitor) {
|
||||
visitor->Visit(this);
|
||||
}
|
||||
|
||||
Visitor::Visit(ns);
|
||||
|
||||
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 {};
|
||||
|
||||
@@ -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 {
|
||||
// Visitor whose default implementation visits the children nodes of any node.
|
||||
class Visitor {
|
||||
public:
|
||||
virtual ~RawVisitor() = default;
|
||||
virtual ~Visitor() = default;
|
||||
|
||||
virtual void Visit(Namespace* node) {}
|
||||
virtual void Visit(Element* node) {}
|
||||
virtual void Visit(Text* text) {}
|
||||
};
|
||||
virtual void Visit(Element* el) {
|
||||
VisitChildren(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor whose default implementation visits the children nodes of any node.
|
||||
*/
|
||||
class Visitor : public RawVisitor {
|
||||
public:
|
||||
using RawVisitor::Visit;
|
||||
virtual void Visit(Text* text) {
|
||||
}
|
||||
|
||||
void Visit(Namespace* node) override { VisitChildren(node); }
|
||||
protected:
|
||||
Visitor() = default;
|
||||
|
||||
void Visit(Element* node) override { VisitChildren(node); }
|
||||
|
||||
void Visit(Text* text) override { VisitChildren(text); }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -16,23 +16,22 @@
|
||||
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "io/StringInputStream.h"
|
||||
#include "test/Test.h"
|
||||
|
||||
using ::aapt::io::StringInputStream;
|
||||
using ::testing::Eq;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::StrEq;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
constexpr const char* kXmlPreamble =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||
namespace xml {
|
||||
|
||||
TEST(XmlDomTest, Inflate) {
|
||||
std::stringstream in(kXmlPreamble);
|
||||
in << R"(
|
||||
std::string input = R"(<?xml version="1.0" encoding="utf-8"?>
|
||||
<Layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -41,26 +40,25 @@ TEST(XmlDomTest, Inflate) {
|
||||
android:layout_height="wrap_content" />
|
||||
</Layout>)";
|
||||
|
||||
const Source source("test.xml");
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);
|
||||
StringInputStream in(input);
|
||||
std::unique_ptr<XmlResource> doc = Inflate(&in, &diag, Source("test.xml"));
|
||||
ASSERT_THAT(doc, NotNull());
|
||||
|
||||
xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());
|
||||
ASSERT_THAT(ns, NotNull());
|
||||
EXPECT_THAT(ns->namespace_uri, Eq(xml::kSchemaAndroid));
|
||||
EXPECT_THAT(ns->namespace_prefix, Eq("android"));
|
||||
Element* el = doc->root.get();
|
||||
EXPECT_THAT(el->namespace_decls, SizeIs(1u));
|
||||
EXPECT_THAT(el->namespace_decls[0].uri, StrEq(xml::kSchemaAndroid));
|
||||
EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android"));
|
||||
}
|
||||
|
||||
// Escaping is handled after parsing of the values for resource-specific values.
|
||||
TEST(XmlDomTest, ForwardEscapes) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
|
||||
std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
|
||||
<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)");
|
||||
|
||||
xml::Element* el = xml::FindRootElement(doc.get());
|
||||
ASSERT_THAT(el, NotNull());
|
||||
Element* el = doc->root.get();
|
||||
|
||||
xml::Attribute* attr = el->FindAttribute({}, "pattern");
|
||||
Attribute* attr = el->FindAttribute({}, "pattern");
|
||||
ASSERT_THAT(attr, NotNull());
|
||||
EXPECT_THAT(attr->value, Eq("\\\\d{5}"));
|
||||
|
||||
@@ -70,21 +68,54 @@ TEST(XmlDomTest, ForwardEscapes) {
|
||||
|
||||
ASSERT_THAT(el->children, SizeIs(1u));
|
||||
|
||||
xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
|
||||
Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
|
||||
ASSERT_THAT(text, NotNull());
|
||||
EXPECT_THAT(text->text, Eq("\\\\d{5}"));
|
||||
}
|
||||
|
||||
TEST(XmlDomTest, XmlEscapeSequencesAreParsed) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element value=""" />)");
|
||||
|
||||
xml::Element* el = xml::FindRootElement(doc.get());
|
||||
ASSERT_THAT(el, NotNull());
|
||||
|
||||
xml::Attribute* attr = el->FindAttribute({}, "value");
|
||||
std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(<element value=""" />)");
|
||||
Attribute* attr = doc->root->FindAttribute({}, "value");
|
||||
ASSERT_THAT(attr, NotNull());
|
||||
|
||||
EXPECT_THAT(attr->value, Eq("\""));
|
||||
}
|
||||
|
||||
class TestVisitor : public PackageAwareVisitor {
|
||||
public:
|
||||
using PackageAwareVisitor::Visit;
|
||||
|
||||
void Visit(Element* el) override {
|
||||
if (el->name == "View1") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
} else if (el->name == "View2") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
} else if (el->name == "View3") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("three", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.three", false})));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(XmlDomTest, PackageAwareXmlVisitor) {
|
||||
std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
|
||||
<View1 xmlns:one="http://schemas.android.com/apk/res/com.one">
|
||||
<View2 xmlns:two="http://schemas.android.com/apk/res/com.two">
|
||||
<View3 xmlns:three="http://schemas.android.com/apk/res/com.three" />
|
||||
</View2>
|
||||
</View1>)");
|
||||
|
||||
Debug::DumpXml(doc.get());
|
||||
TestVisitor visitor;
|
||||
doc->root->Accept(&visitor);
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user