Files
frameworks_base/tools/aapt2/format/proto/ProtoSerialize.cpp
Ryan Mitchell 9634efb6f4 Prepare aapt2 for multiple ids per type
For the SDK finalization changes, aapt2 must be able to handle
resources of the same type having different type ids. The
ResourceTable data structure currently stores package ids and type ids
on ResourceTablePackage and ResourceTableType respectively. This
prevents resource entries of the same type from having different type
ids without having to create another ResourceTableType structure.

JavaClassGenerator assumes each type only appears once in the
ResourceTable and it would need to dedupe the types to ensure one class
containing all the resource types ids is generated. TableFlattener on
the other hand needs a separate ResourceTableType for each type/id
combination so that the types are flattened into separate
ResTable_types.

This change simplifies aapt2's ResourceTable data structure:
- Resource ids are stored exclusively on ResourceEntry structures
  meaning multiple entries can have different type ids while being
  stored in the same ResourceTableType. Classes like JavaClassGenerator
  can simply iterate over a type to see all the resources of the type
  regardless of what their type id is.

- ResourceTable::GetPartitionedView() retrieves a list of resources
  sorted and partitioned by package id, type id, and entry id. Classes
  like TableFlattener can use this view to get separate
  ResourceTavleTypes for each different type id that a type has.

These changes will also make it easy to have a resource span multiple
type ids if it exhausts all of the entry ids in one type id.

The new NewResourcesBuilder replaces the numerous setter methods on
ResourceTable.

Bug: 183102797
Test: aapt2_tests
Change-Id: I60dbcb24143bb958333899cafa7d41faa226d203
2021-03-29 10:22:08 -07:00

751 lines
27 KiB
C++

/*
* 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.
*/
#include "format/proto/ProtoSerialize.h"
#include "ValueVisitor.h"
#include "util/BigBuffer.h"
using android::ConfigDescription;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) {
BigBuffer buffer(1024);
StringPool::FlattenUtf8(&buffer, pool, diag);
std::string* data = out_pb_pool->mutable_data();
data->reserve(buffer.size());
size_t offset = 0;
for (const BigBuffer::Block& block : buffer) {
data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
offset += block.size;
}
}
void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
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->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
}
}
static pb::Visibility::Level SerializeVisibilityToPb(Visibility::Level state) {
switch (state) {
case Visibility::Level::kPrivate:
return pb::Visibility::PRIVATE;
case Visibility::Level::kPublic:
return pb::Visibility::PUBLIC;
default:
break;
}
return pb::Visibility::UNKNOWN;
}
void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
out_pb_config->set_mcc(config.mcc);
out_pb_config->set_mnc(config.mnc);
out_pb_config->set_locale(config.GetBcp47LanguageTag());
switch (config.screenLayout & ConfigDescription::MASK_LAYOUTDIR) {
case ConfigDescription::LAYOUTDIR_LTR:
out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR);
break;
case ConfigDescription::LAYOUTDIR_RTL:
out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL);
break;
}
out_pb_config->set_screen_width(config.screenWidth);
out_pb_config->set_screen_height(config.screenHeight);
out_pb_config->set_screen_width_dp(config.screenWidthDp);
out_pb_config->set_screen_height_dp(config.screenHeightDp);
out_pb_config->set_smallest_screen_width_dp(config.smallestScreenWidthDp);
switch (config.screenLayout & ConfigDescription::MASK_SCREENSIZE) {
case ConfigDescription::SCREENSIZE_SMALL:
out_pb_config->set_screen_layout_size(
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL);
break;
case ConfigDescription::SCREENSIZE_NORMAL:
out_pb_config->set_screen_layout_size(
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL);
break;
case ConfigDescription::SCREENSIZE_LARGE:
out_pb_config->set_screen_layout_size(
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE);
break;
case ConfigDescription::SCREENSIZE_XLARGE:
out_pb_config->set_screen_layout_size(
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE);
break;
}
switch (config.screenLayout & ConfigDescription::MASK_SCREENLONG) {
case ConfigDescription::SCREENLONG_YES:
out_pb_config->set_screen_layout_long(
pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG);
break;
case ConfigDescription::SCREENLONG_NO:
out_pb_config->set_screen_layout_long(
pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG);
break;
}
switch (config.screenLayout2 & ConfigDescription::MASK_SCREENROUND) {
case ConfigDescription::SCREENROUND_YES:
out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND);
break;
case ConfigDescription::SCREENROUND_NO:
out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND);
break;
}
switch (config.colorMode & ConfigDescription::MASK_WIDE_COLOR_GAMUT) {
case ConfigDescription::WIDE_COLOR_GAMUT_YES:
out_pb_config->set_wide_color_gamut(pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG);
break;
case ConfigDescription::WIDE_COLOR_GAMUT_NO:
out_pb_config->set_wide_color_gamut(
pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG);
break;
}
switch (config.colorMode & ConfigDescription::MASK_HDR) {
case ConfigDescription::HDR_YES:
out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_HIGHDR);
break;
case ConfigDescription::HDR_NO:
out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_LOWDR);
break;
}
switch (config.orientation) {
case ConfigDescription::ORIENTATION_PORT:
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_PORT);
break;
case ConfigDescription::ORIENTATION_LAND:
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_LAND);
break;
case ConfigDescription::ORIENTATION_SQUARE:
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_SQUARE);
break;
}
switch (config.uiMode & ConfigDescription::MASK_UI_MODE_TYPE) {
case ConfigDescription::UI_MODE_TYPE_NORMAL:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL);
break;
case ConfigDescription::UI_MODE_TYPE_DESK:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_DESK);
break;
case ConfigDescription::UI_MODE_TYPE_CAR:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_CAR);
break;
case ConfigDescription::UI_MODE_TYPE_TELEVISION:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION);
break;
case ConfigDescription::UI_MODE_TYPE_APPLIANCE:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE);
break;
case ConfigDescription::UI_MODE_TYPE_WATCH:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH);
break;
case ConfigDescription::UI_MODE_TYPE_VR_HEADSET:
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET);
break;
}
switch (config.uiMode & ConfigDescription::MASK_UI_MODE_NIGHT) {
case ConfigDescription::UI_MODE_NIGHT_YES:
out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT);
break;
case ConfigDescription::UI_MODE_NIGHT_NO:
out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT);
break;
}
out_pb_config->set_density(config.density);
switch (config.touchscreen) {
case ConfigDescription::TOUCHSCREEN_NOTOUCH:
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH);
break;
case ConfigDescription::TOUCHSCREEN_STYLUS:
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS);
break;
case ConfigDescription::TOUCHSCREEN_FINGER:
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER);
break;
}
switch (config.inputFlags & ConfigDescription::MASK_KEYSHIDDEN) {
case ConfigDescription::KEYSHIDDEN_NO:
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED);
break;
case ConfigDescription::KEYSHIDDEN_YES:
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN);
break;
case ConfigDescription::KEYSHIDDEN_SOFT:
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT);
break;
}
switch (config.keyboard) {
case ConfigDescription::KEYBOARD_NOKEYS:
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_NOKEYS);
break;
case ConfigDescription::KEYBOARD_QWERTY:
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_QWERTY);
break;
case ConfigDescription::KEYBOARD_12KEY:
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY);
break;
}
switch (config.inputFlags & ConfigDescription::MASK_NAVHIDDEN) {
case ConfigDescription::NAVHIDDEN_NO:
out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED);
break;
case ConfigDescription::NAVHIDDEN_YES:
out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN);
break;
}
switch (config.navigation) {
case ConfigDescription::NAVIGATION_NONAV:
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_NONAV);
break;
case ConfigDescription::NAVIGATION_DPAD:
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_DPAD);
break;
case ConfigDescription::NAVIGATION_TRACKBALL:
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_TRACKBALL);
break;
case ConfigDescription::NAVIGATION_WHEEL:
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_WHEEL);
break;
}
out_pb_config->set_sdk_version(config.sdkVersion);
}
static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
std::vector<Overlayable*>& serialized_overlayables,
StringPool* source_pool, pb::Entry* pb_entry,
pb::ResourceTable* pb_table) {
// Retrieve the index of the overlayable in the list of groups that have already been serialized.
size_t i;
for (i = 0 ; i < serialized_overlayables.size(); i++) {
if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
break;
}
}
// Serialize the overlayable if it has not been serialized already.
if (i == serialized_overlayables.size()) {
serialized_overlayables.push_back(overlayable_item.overlayable.get());
pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
pb_overlayable->set_name(overlayable_item.overlayable->name);
pb_overlayable->set_actor(overlayable_item.overlayable->actor);
if (source_pool != nullptr) {
SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
pb_overlayable->mutable_source());
}
}
pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
pb_overlayable_item->set_overlayable_idx(i);
if (overlayable_item.policies & PolicyFlags::PUBLIC) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
}
if (overlayable_item.policies & PolicyFlags::PRODUCT_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
}
if (overlayable_item.policies & PolicyFlags::SYSTEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
}
if (overlayable_item.policies & PolicyFlags::VENDOR_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
}
if (overlayable_item.policies & PolicyFlags::SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
}
if (overlayable_item.policies & PolicyFlags::ODM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::ODM);
}
if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
}
if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
}
if (overlayable_item.policies & PolicyFlags::CONFIG_SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::CONFIG_SIGNATURE);
}
if (source_pool != nullptr) {
SerializeSourceToPb(overlayable_item.source, source_pool,
pb_overlayable_item->mutable_source());
}
pb_overlayable_item->set_comment(overlayable_item.comment);
}
void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
IDiagnostics* diag, SerializeTableOptions options) {
auto source_pool = (options.exclude_sources) ? nullptr : util::make_unique<StringPool>();
pb::ToolFingerprint* pb_fingerprint = out_table->add_tool_fingerprint();
pb_fingerprint->set_tool(util::GetToolName());
pb_fingerprint->set_version(util::GetToolFingerprint());
std::vector<Overlayable*> overlayables;
auto table_view = table.GetPartitionedView();
for (const auto& package : table_view.packages) {
pb::Package* pb_package = out_table->add_package();
if (package.id) {
pb_package->mutable_package_id()->set_id(package.id.value());
}
pb_package->set_package_name(package.name);
for (const auto& type : package.types) {
pb::Type* pb_type = pb_package->add_type();
if (type.id) {
pb_type->mutable_type_id()->set_id(type.id.value());
}
pb_type->set_name(to_string(type.type).to_string());
// hardcoded string uses characters which make it an invalid resource name
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
for (const auto& entry : type.entries) {
pb::Entry* pb_entry = pb_type->add_entry();
if (entry->id) {
pb_entry->mutable_entry_id()->set_id(entry->id.value().entry_id());
}
ResourceName resource_name({}, type.type, entry->name);
if (options.collapse_key_stringpool &&
options.name_collapse_exemptions.find(resource_name) ==
options.name_collapse_exemptions.end()) {
pb_entry->set_name(obfuscated_resource_name);
} else {
pb_entry->set_name(entry->name);
}
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
if (source_pool != nullptr) {
SerializeSourceToPb(entry->visibility.source, source_pool.get(),
pb_visibility->mutable_source());
}
pb_visibility->set_comment(entry->visibility.comment);
if (entry->allow_new) {
pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
if (source_pool != nullptr) {
SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
pb_allow_new->mutable_source());
}
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
if (entry->overlayable_item) {
SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
source_pool.get(), pb_entry, out_table);
}
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
SerializeConfig(config_value->config, pb_config_value->mutable_config());
pb_config_value->mutable_config()->set_product(config_value->product);
SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
source_pool.get());
}
}
}
}
if (source_pool != nullptr) {
SerializeStringPoolToPb(*source_pool, out_table->mutable_source_pool(), diag);
}
}
static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
switch (type) {
case Reference::Type::kResource:
return pb::Reference_Type_REFERENCE;
case Reference::Type::kAttribute:
return pb::Reference_Type_ATTRIBUTE;
default:
break;
}
return pb::Reference_Type_REFERENCE;
}
static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
if (ref.name) {
pb_ref->set_name(ref.name.value().to_string());
}
pb_ref->set_private_(ref.private_reference);
pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
if (ref.is_dynamic) {
pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
}
}
template <typename T>
static void SerializeItemMetaDataToPb(const Item& item, T* pb_item, StringPool* src_pool) {
if (src_pool != nullptr) {
SerializeSourceToPb(item.GetSource(), src_pool, pb_item->mutable_source());
}
pb_item->set_comment(item.GetComment());
}
static pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
switch (plural_idx) {
case Plural::Zero:
return pb::Plural_Arity_ZERO;
case Plural::One:
return pb::Plural_Arity_ONE;
case Plural::Two:
return pb::Plural_Arity_TWO;
case Plural::Few:
return pb::Plural_Arity_FEW;
case Plural::Many:
return pb::Plural_Arity_MANY;
default:
break;
}
return pb::Plural_Arity_OTHER;
}
static pb::FileReference::Type SerializeFileReferenceTypeToPb(const ResourceFile::Type& type) {
switch (type) {
case ResourceFile::Type::kBinaryXml:
return pb::FileReference::BINARY_XML;
case ResourceFile::Type::kProtoXml:
return pb::FileReference::PROTO_XML;
case ResourceFile::Type::kPng:
return pb::FileReference::PNG;
default:
return pb::FileReference::UNKNOWN;
}
}
namespace {
class ValueSerializer : public ConstValueVisitor {
public:
using ConstValueVisitor::Visit;
ValueSerializer(pb::Value* out_value, StringPool* src_pool)
: out_value_(out_value), src_pool_(src_pool) {
}
void Visit(const Reference* ref) override {
SerializeReferenceToPb(*ref, out_value_->mutable_item()->mutable_ref());
}
void Visit(const String* str) override {
out_value_->mutable_item()->mutable_str()->set_value(*str->value);
}
void Visit(const RawString* str) override {
out_value_->mutable_item()->mutable_raw_str()->set_value(*str->value);
}
void Visit(const StyledString* str) override {
pb::StyledString* pb_str = out_value_->mutable_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(const FileReference* file) override {
pb::FileReference* pb_file = out_value_->mutable_item()->mutable_file();
pb_file->set_path(*file->path);
pb_file->set_type(SerializeFileReferenceTypeToPb(file->type));
}
void Visit(const Id* /*id*/) override {
out_value_->mutable_item()->mutable_id();
}
void Visit(const BinaryPrimitive* prim) override {
android::Res_value val = {};
prim->Flatten(&val);
pb::Primitive* pb_prim = out_value_->mutable_item()->mutable_prim();
switch (val.dataType) {
case android::Res_value::TYPE_NULL: {
if (val.data == android::Res_value::DATA_NULL_UNDEFINED) {
pb_prim->set_allocated_null_value(new pb::Primitive_NullType());
} else if (val.data == android::Res_value::DATA_NULL_EMPTY) {
pb_prim->set_allocated_empty_value(new pb::Primitive_EmptyType());
} else {
LOG(FATAL) << "Unexpected data value for TYPE_NULL BinaryPrimitive: " << val.data;
}
} break;
case android::Res_value::TYPE_FLOAT: {
pb_prim->set_float_value(*(float*)&val.data);
} break;
case android::Res_value::TYPE_DIMENSION: {
pb_prim->set_dimension_value(val.data);
} break;
case android::Res_value::TYPE_FRACTION: {
pb_prim->set_fraction_value(val.data);
} break;
case android::Res_value::TYPE_INT_DEC: {
pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
} break;
case android::Res_value::TYPE_INT_HEX: {
pb_prim->set_int_hexadecimal_value(val.data);
} break;
case android::Res_value::TYPE_INT_BOOLEAN: {
pb_prim->set_boolean_value(static_cast<bool>(val.data));
} break;
case android::Res_value::TYPE_INT_COLOR_ARGB8: {
pb_prim->set_color_argb8_value(val.data);
} break;
case android::Res_value::TYPE_INT_COLOR_RGB8: {
pb_prim->set_color_rgb8_value(val.data);
} break;
case android::Res_value::TYPE_INT_COLOR_ARGB4: {
pb_prim->set_color_argb4_value(val.data);
} break;
case android::Res_value::TYPE_INT_COLOR_RGB4: {
pb_prim->set_color_rgb4_value(val.data);
} break;
default:
LOG(FATAL) << "Unexpected BinaryPrimitive type: " << val.dataType;
break;
}
}
void Visit(const Attribute* attr) override {
pb::Attribute* pb_attr = out_value_->mutable_compound_value()->mutable_attr();
pb_attr->set_format_flags(attr->type_mask);
pb_attr->set_min_int(attr->min_int);
pb_attr->set_max_int(attr->max_int);
for (auto& symbol : attr->symbols) {
pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
SerializeItemMetaDataToPb(symbol.symbol, pb_symbol, src_pool_);
SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
pb_symbol->set_value(symbol.value);
pb_symbol->set_type(symbol.type);
}
}
void Visit(const Style* style) override {
pb::Style* pb_style = out_value_->mutable_compound_value()->mutable_style();
if (style->parent) {
const Reference& parent = style->parent.value();
SerializeReferenceToPb(parent, pb_style->mutable_parent());
if (src_pool_ != nullptr) {
SerializeSourceToPb(parent.GetSource(), src_pool_, pb_style->mutable_parent_source());
}
}
for (const Style::Entry& entry : style->entries) {
pb::Style_Entry* pb_entry = pb_style->add_entry();
SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
SerializeItemMetaDataToPb(entry.key, pb_entry, src_pool_);
SerializeItemToPb(*entry.value, pb_entry->mutable_item());
}
}
void Visit(const Styleable* styleable) override {
pb::Styleable* pb_styleable = out_value_->mutable_compound_value()->mutable_styleable();
for (const Reference& entry : styleable->entries) {
pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
SerializeItemMetaDataToPb(entry, pb_entry, src_pool_);
SerializeReferenceToPb(entry, pb_entry->mutable_attr());
}
}
void Visit(const Array* array) override {
pb::Array* pb_array = out_value_->mutable_compound_value()->mutable_array();
for (const std::unique_ptr<Item>& element : array->elements) {
pb::Array_Element* pb_element = pb_array->add_element();
SerializeItemMetaDataToPb(*element, pb_element, src_pool_);
SerializeItemToPb(*element, pb_element->mutable_item());
}
}
void Visit(const Plural* plural) override {
pb::Plural* pb_plural = out_value_->mutable_compound_value()->mutable_plural();
const size_t count = plural->values.size();
for (size_t i = 0; i < count; i++) {
if (!plural->values[i]) {
// No plural value set here.
continue;
}
pb::Plural_Entry* pb_entry = pb_plural->add_entry();
pb_entry->set_arity(SerializePluralEnumToPb(i));
SerializeItemMetaDataToPb(*plural->values[i], pb_entry, src_pool_);
SerializeItemToPb(*plural->values[i], pb_entry->mutable_item());
}
}
void VisitAny(const Value* unknown) override {
LOG(FATAL) << "unimplemented value: " << *unknown;
}
private:
pb::Value* out_value_;
StringPool* src_pool_;
};
} // namespace
void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool) {
ValueSerializer serializer(out_value, src_pool);
value.Accept(&serializer);
// Serialize the meta-data of the Value.
out_value->set_comment(value.GetComment());
out_value->set_weak(value.IsWeak());
if (src_pool != nullptr) {
SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
}
}
void SerializeItemToPb(const Item& item, pb::Item* out_item) {
pb::Value value;
ValueSerializer serializer(&value, nullptr);
item.Accept(&serializer);
out_item->MergeFrom(value.item());
}
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
out_file->set_resource_name(file.name.to_string());
out_file->set_source_path(file.source.path);
out_file->set_type(SerializeFileReferenceTypeToPb(file.type));
SerializeConfig(file.config, out_file->mutable_config());
for (const SourcedResourceName& exported : file.exported_symbols) {
pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
pb_symbol->set_resource_name(exported.name.to_string());
pb_symbol->mutable_source()->set_line_number(exported.line);
}
}
static void SerializeXmlCommon(const xml::Node& node, pb::XmlNode* out_node) {
pb::SourcePosition* pb_src = out_node->mutable_source();
pb_src->set_line_number(node.line_number);
pb_src->set_column_number(node.column_number);
}
void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node,
const SerializeXmlOptions options) {
SerializeXmlCommon(el, out_node);
pb::XmlElement* pb_element = out_node->mutable_element();
pb_element->set_name(el.name);
pb_element->set_namespace_uri(el.namespace_uri);
for (const xml::NamespaceDecl& ns : el.namespace_decls) {
pb::XmlNamespace* pb_ns = pb_element->add_namespace_declaration();
pb_ns->set_prefix(ns.prefix);
pb_ns->set_uri(ns.uri);
pb::SourcePosition* pb_src = pb_ns->mutable_source();
pb_src->set_line_number(ns.line_number);
pb_src->set_column_number(ns.column_number);
}
for (const xml::Attribute& attr : el.attributes) {
pb::XmlAttribute* pb_attr = pb_element->add_attribute();
pb_attr->set_name(attr.name);
pb_attr->set_namespace_uri(attr.namespace_uri);
pb_attr->set_value(attr.value);
if (attr.compiled_attribute) {
const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
pb_attr->set_resource_id(attr_id.id);
}
if (attr.compiled_value != nullptr) {
SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
pb::SourcePosition* pb_src = pb_attr->mutable_source();
pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
}
}
for (const std::unique_ptr<xml::Node>& child : el.children) {
if (const xml::Element* child_el = xml::NodeCast<xml::Element>(child.get())) {
SerializeXmlToPb(*child_el, pb_element->add_child());
} else if (const xml::Text* text_el = xml::NodeCast<xml::Text>(child.get())) {
if (options.remove_empty_text_nodes && util::TrimWhitespace(text_el->text).empty()) {
// Do not serialize whitespace text nodes if told not to
continue;
}
pb::XmlNode *pb_child_node = pb_element->add_child();
SerializeXmlCommon(*text_el, pb_child_node);
pb_child_node->set_text(text_el->text);
} else {
LOG(FATAL) << "unhandled XmlNode type";
}
}
}
void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node,
const SerializeXmlOptions options) {
SerializeXmlToPb(*resource.root, out_node, options);
}
} // namespace aapt