mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
643 lines
23 KiB
C++
643 lines
23 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2008 Google Inc. All rights reserved.
|
|
// https://developers.google.com/protocol-buffers/
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <thirdparty/protobuf/util/internal/default_value_objectwriter.h>
|
|
|
|
#include <cstdint>
|
|
#include <unordered_map>
|
|
|
|
#include <thirdparty/protobuf/util/internal/constants.h>
|
|
#include <thirdparty/protobuf/util/internal/utility.h>
|
|
#include <thirdparty/protobuf/stubs/map_util.h>
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace util {
|
|
namespace converter {
|
|
|
|
namespace {
|
|
// Helper function to convert string value to given data type by calling the
|
|
// passed converter function on the DataPiece created from "value" argument.
|
|
// If value is empty or if conversion fails, the default_value is returned.
|
|
template <typename T>
|
|
T ConvertTo(StringPiece value,
|
|
util::StatusOr<T> (DataPiece::*converter_fn)() const,
|
|
T default_value) {
|
|
if (value.empty()) return default_value;
|
|
util::StatusOr<T> result = (DataPiece(value, true).*converter_fn)();
|
|
return result.ok() ? result.value() : default_value;
|
|
}
|
|
} // namespace
|
|
|
|
DefaultValueObjectWriter::DefaultValueObjectWriter(
|
|
TypeResolver* type_resolver, const google::protobuf::Type& type,
|
|
ObjectWriter* ow)
|
|
: typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
|
|
own_typeinfo_(true),
|
|
type_(type),
|
|
current_(nullptr),
|
|
root_(nullptr),
|
|
suppress_empty_list_(false),
|
|
preserve_proto_field_names_(false),
|
|
use_ints_for_enums_(false),
|
|
ow_(ow) {}
|
|
|
|
DefaultValueObjectWriter::~DefaultValueObjectWriter() {
|
|
if (own_typeinfo_) {
|
|
delete typeinfo_;
|
|
}
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(
|
|
StringPiece name, bool value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderBool(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32(
|
|
StringPiece name, int32_t value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderInt32(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32(
|
|
StringPiece name, uint32_t value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderUint32(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64(
|
|
StringPiece name, int64_t value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderInt64(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64(
|
|
StringPiece name, uint64_t value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderUint64(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble(
|
|
StringPiece name, double value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderDouble(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat(
|
|
StringPiece name, float value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderBool(name, value);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece(value));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString(
|
|
StringPiece name, StringPiece value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderString(name, value);
|
|
} else {
|
|
// Since StringPiece is essentially a pointer, takes a copy of "value" to
|
|
// avoid ownership issues.
|
|
string_values_.emplace_back(new std::string(value));
|
|
RenderDataPiece(name, DataPiece(*string_values_.back(), true));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes(
|
|
StringPiece name, StringPiece value) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderBytes(name, value);
|
|
} else {
|
|
// Since StringPiece is essentially a pointer, takes a copy of "value" to
|
|
// avoid ownership issues.
|
|
string_values_.emplace_back(new std::string(value));
|
|
RenderDataPiece(name, DataPiece(*string_values_.back(), false, true));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
|
|
StringPiece name) {
|
|
if (current_ == nullptr) {
|
|
ow_->RenderNull(name);
|
|
} else {
|
|
RenderDataPiece(name, DataPiece::NullData());
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void DefaultValueObjectWriter::RegisterFieldScrubCallBack(
|
|
FieldScrubCallBack field_scrub_callback) {
|
|
field_scrub_callback_ = std::move(field_scrub_callback);
|
|
}
|
|
|
|
DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode(
|
|
const std::string& name, const google::protobuf::Type* type, NodeKind kind,
|
|
const DataPiece& data, bool is_placeholder,
|
|
const std::vector<std::string>& path, bool suppress_empty_list,
|
|
bool preserve_proto_field_names, bool use_ints_for_enums,
|
|
FieldScrubCallBack field_scrub_callback) {
|
|
return new Node(name, type, kind, data, is_placeholder, path,
|
|
suppress_empty_list, preserve_proto_field_names,
|
|
use_ints_for_enums, std::move(field_scrub_callback));
|
|
}
|
|
|
|
DefaultValueObjectWriter::Node::Node(
|
|
const std::string& name, const google::protobuf::Type* type, NodeKind kind,
|
|
const DataPiece& data, bool is_placeholder,
|
|
const std::vector<std::string>& path, bool suppress_empty_list,
|
|
bool preserve_proto_field_names, bool use_ints_for_enums,
|
|
FieldScrubCallBack field_scrub_callback)
|
|
: name_(name),
|
|
type_(type),
|
|
kind_(kind),
|
|
is_any_(false),
|
|
data_(data),
|
|
is_placeholder_(is_placeholder),
|
|
path_(path),
|
|
suppress_empty_list_(suppress_empty_list),
|
|
preserve_proto_field_names_(preserve_proto_field_names),
|
|
use_ints_for_enums_(use_ints_for_enums),
|
|
field_scrub_callback_(std::move(field_scrub_callback)) {}
|
|
|
|
DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
|
|
StringPiece name) {
|
|
if (name.empty() || kind_ != OBJECT) {
|
|
return nullptr;
|
|
}
|
|
for (Node* child : children_) {
|
|
if (child->name() == name) {
|
|
return child;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
|
|
if (kind_ == PRIMITIVE) {
|
|
ObjectWriter::RenderDataPieceTo(data_, name_, ow);
|
|
return;
|
|
}
|
|
|
|
// Render maps. Empty maps are rendered as "{}".
|
|
if (kind_ == MAP) {
|
|
ow->StartObject(name_);
|
|
WriteChildren(ow);
|
|
ow->EndObject();
|
|
return;
|
|
}
|
|
|
|
// Write out lists. If we didn't have any list in response, write out empty
|
|
// list.
|
|
if (kind_ == LIST) {
|
|
// Suppress empty lists if requested.
|
|
if (suppress_empty_list_ && is_placeholder_) return;
|
|
|
|
ow->StartList(name_);
|
|
WriteChildren(ow);
|
|
ow->EndList();
|
|
return;
|
|
}
|
|
|
|
// If is_placeholder_ = true, we didn't see this node in the response, so
|
|
// skip output.
|
|
if (is_placeholder_) return;
|
|
|
|
ow->StartObject(name_);
|
|
WriteChildren(ow);
|
|
ow->EndObject();
|
|
}
|
|
|
|
void DefaultValueObjectWriter::Node::WriteChildren(ObjectWriter* ow) {
|
|
for (Node* child : children_) {
|
|
child->WriteTo(ow);
|
|
}
|
|
}
|
|
|
|
const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType(
|
|
const google::protobuf::Type& found_type, const TypeInfo* typeinfo) {
|
|
// If this field is a map, we should use the type of its "Value" as
|
|
// the type of the child node.
|
|
for (int i = 0; i < found_type.fields_size(); ++i) {
|
|
const google::protobuf::Field& sub_field = found_type.fields(i);
|
|
if (sub_field.number() != 2) {
|
|
continue;
|
|
}
|
|
if (sub_field.kind() != google::protobuf::Field::TYPE_MESSAGE) {
|
|
// This map's value type is not a message type. We don't need to
|
|
// get the field_type in this case.
|
|
break;
|
|
}
|
|
util::StatusOr<const google::protobuf::Type*> sub_type =
|
|
typeinfo->ResolveTypeUrl(sub_field.type_url());
|
|
if (!sub_type.ok()) {
|
|
GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'.";
|
|
} else {
|
|
return sub_type.value();
|
|
}
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DefaultValueObjectWriter::Node::PopulateChildren(
|
|
const TypeInfo* typeinfo) {
|
|
// Ignores well known types that don't require automatically populating their
|
|
// primitive children. For type "Any", we only populate its children when the
|
|
// "@type" field is set.
|
|
// TODO(tsun): remove "kStructValueType" from the list. It's being checked
|
|
// now because of a bug in the tool-chain that causes the "oneof_index"
|
|
// of kStructValueType to not be set correctly.
|
|
if (type_ == nullptr || type_->name() == kAnyType ||
|
|
type_->name() == kStructType || type_->name() == kTimestampType ||
|
|
type_->name() == kDurationType || type_->name() == kStructValueType) {
|
|
return;
|
|
}
|
|
std::vector<Node*> new_children;
|
|
std::unordered_map<std::string, int> orig_children_map;
|
|
|
|
// Creates a map of child nodes to speed up lookup.
|
|
for (int i = 0; i < children_.size(); ++i) {
|
|
InsertIfNotPresent(&orig_children_map, children_[i]->name_, i);
|
|
}
|
|
|
|
for (int i = 0; i < type_->fields_size(); ++i) {
|
|
const google::protobuf::Field& field = type_->fields(i);
|
|
|
|
// This code is checking if the field to be added to the tree should be
|
|
// scrubbed or not by calling the field_scrub_callback_ callback function.
|
|
std::vector<std::string> path;
|
|
if (!path_.empty()) {
|
|
path.insert(path.begin(), path_.begin(), path_.end());
|
|
}
|
|
path.push_back(field.name());
|
|
if (field_scrub_callback_ && field_scrub_callback_(path, &field)) {
|
|
continue;
|
|
}
|
|
|
|
std::unordered_map<std::string, int>::iterator found =
|
|
orig_children_map.find(field.name());
|
|
// If the child field has already been set, we just add it to the new list
|
|
// of children.
|
|
if (found != orig_children_map.end()) {
|
|
new_children.push_back(children_[found->second]);
|
|
children_[found->second] = nullptr;
|
|
continue;
|
|
}
|
|
|
|
const google::protobuf::Type* field_type = nullptr;
|
|
bool is_map = false;
|
|
NodeKind kind = PRIMITIVE;
|
|
|
|
if (field.kind() == google::protobuf::Field::TYPE_MESSAGE) {
|
|
kind = OBJECT;
|
|
util::StatusOr<const google::protobuf::Type*> found_result =
|
|
typeinfo->ResolveTypeUrl(field.type_url());
|
|
if (!found_result.ok()) {
|
|
// "field" is of an unknown type.
|
|
GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'.";
|
|
} else {
|
|
const google::protobuf::Type* found_type = found_result.value();
|
|
is_map = IsMap(field, *found_type);
|
|
|
|
if (!is_map) {
|
|
field_type = found_type;
|
|
} else {
|
|
// If this field is a map, we should use the type of its "Value" as
|
|
// the type of the child node.
|
|
field_type = GetMapValueType(*found_type, typeinfo);
|
|
kind = MAP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_map &&
|
|
field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED) {
|
|
kind = LIST;
|
|
}
|
|
|
|
// If oneof_index() != 0, the child field is part of a "oneof", which means
|
|
// the child field is optional and we shouldn't populate its default
|
|
// primitive value.
|
|
if (field.oneof_index() != 0 && kind == PRIMITIVE) continue;
|
|
|
|
// If the child field is of primitive type, sets its data to the default
|
|
// value of its type.
|
|
std::unique_ptr<Node> child(
|
|
new Node(preserve_proto_field_names_ ? field.name() : field.json_name(),
|
|
field_type, kind,
|
|
kind == PRIMITIVE ? CreateDefaultDataPieceForField(
|
|
field, typeinfo, use_ints_for_enums_)
|
|
: DataPiece::NullData(),
|
|
true, path, suppress_empty_list_, preserve_proto_field_names_,
|
|
use_ints_for_enums_, field_scrub_callback_));
|
|
new_children.push_back(child.release());
|
|
}
|
|
// Adds all leftover nodes in children_ to the beginning of new_child.
|
|
for (int i = 0; i < children_.size(); ++i) {
|
|
if (children_[i] == nullptr) {
|
|
continue;
|
|
}
|
|
new_children.insert(new_children.begin(), children_[i]);
|
|
children_[i] = nullptr;
|
|
}
|
|
children_.swap(new_children);
|
|
}
|
|
|
|
void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
|
|
// If this is an "Any" node with "@type" already given and no other children
|
|
// have been added, populates its children.
|
|
if (node != nullptr && node->is_any() && node->type() != nullptr &&
|
|
node->type()->name() != kAnyType && node->number_of_children() == 1) {
|
|
node->PopulateChildren(typeinfo_);
|
|
}
|
|
}
|
|
|
|
DataPiece DefaultValueObjectWriter::FindEnumDefault(
|
|
const google::protobuf::Field& field, const TypeInfo* typeinfo,
|
|
bool use_ints_for_enums) {
|
|
const google::protobuf::Enum* enum_type =
|
|
typeinfo->GetEnumByTypeUrl(field.type_url());
|
|
if (!enum_type) {
|
|
GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url()
|
|
<< "'";
|
|
return DataPiece::NullData();
|
|
}
|
|
if (!field.default_value().empty()) {
|
|
if (!use_ints_for_enums) {
|
|
return DataPiece(field.default_value(), true);
|
|
} else {
|
|
const std::string& enum_default_value_name = field.default_value();
|
|
for (int enum_index = 0; enum_index < enum_type->enumvalue_size();
|
|
++enum_index) {
|
|
auto& enum_value = enum_type->enumvalue(enum_index);
|
|
if (enum_value.name() == enum_default_value_name)
|
|
return DataPiece(enum_value.number());
|
|
}
|
|
GOOGLE_LOG(WARNING) << "Could not find enum value '" << enum_default_value_name
|
|
<< "' with type '" << field.type_url() << "'";
|
|
return DataPiece::NullData();
|
|
}
|
|
}
|
|
// We treat the first value as the default if none is specified.
|
|
return enum_type->enumvalue_size() > 0
|
|
? (use_ints_for_enums
|
|
? DataPiece(enum_type->enumvalue(0).number())
|
|
: DataPiece(enum_type->enumvalue(0).name(), true))
|
|
: DataPiece::NullData();
|
|
}
|
|
|
|
DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
|
|
const google::protobuf::Field& field, const TypeInfo* typeinfo,
|
|
bool use_ints_for_enums) {
|
|
switch (field.kind()) {
|
|
case google::protobuf::Field::TYPE_DOUBLE: {
|
|
return DataPiece(ConvertTo<double>(
|
|
field.default_value(), &DataPiece::ToDouble, static_cast<double>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_FLOAT: {
|
|
return DataPiece(ConvertTo<float>(
|
|
field.default_value(), &DataPiece::ToFloat, static_cast<float>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_INT64:
|
|
case google::protobuf::Field::TYPE_SINT64:
|
|
case google::protobuf::Field::TYPE_SFIXED64: {
|
|
return DataPiece(ConvertTo<int64_t>(
|
|
field.default_value(), &DataPiece::ToInt64, static_cast<int64_t>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_UINT64:
|
|
case google::protobuf::Field::TYPE_FIXED64: {
|
|
return DataPiece(ConvertTo<uint64_t>(field.default_value(),
|
|
&DataPiece::ToUint64,
|
|
static_cast<uint64_t>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_INT32:
|
|
case google::protobuf::Field::TYPE_SINT32:
|
|
case google::protobuf::Field::TYPE_SFIXED32: {
|
|
return DataPiece(ConvertTo<int32_t>(
|
|
field.default_value(), &DataPiece::ToInt32, static_cast<int32_t>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_BOOL: {
|
|
return DataPiece(
|
|
ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false));
|
|
}
|
|
case google::protobuf::Field::TYPE_STRING: {
|
|
return DataPiece(field.default_value(), true);
|
|
}
|
|
case google::protobuf::Field::TYPE_BYTES: {
|
|
return DataPiece(field.default_value(), false, true);
|
|
}
|
|
case google::protobuf::Field::TYPE_UINT32:
|
|
case google::protobuf::Field::TYPE_FIXED32: {
|
|
return DataPiece(ConvertTo<uint32_t>(field.default_value(),
|
|
&DataPiece::ToUint32,
|
|
static_cast<uint32_t>(0)));
|
|
}
|
|
case google::protobuf::Field::TYPE_ENUM: {
|
|
return FindEnumDefault(field, typeinfo, use_ints_for_enums);
|
|
}
|
|
default: {
|
|
return DataPiece::NullData();
|
|
}
|
|
}
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
|
|
StringPiece name) {
|
|
if (current_ == nullptr) {
|
|
std::vector<std::string> path;
|
|
root_.reset(CreateNewNode(std::string(name), &type_, OBJECT,
|
|
DataPiece::NullData(), false, path,
|
|
suppress_empty_list_, preserve_proto_field_names_,
|
|
use_ints_for_enums_, field_scrub_callback_));
|
|
root_->PopulateChildren(typeinfo_);
|
|
current_ = root_.get();
|
|
return this;
|
|
}
|
|
MaybePopulateChildrenOfAny(current_);
|
|
Node* child = current_->FindChild(name);
|
|
if (current_->kind() == LIST || current_->kind() == MAP || child == nullptr) {
|
|
// If current_ is a list or a map node, we should create a new child and use
|
|
// the type of current_ as the type of the new child.
|
|
std::unique_ptr<Node> node(
|
|
CreateNewNode(std::string(name),
|
|
((current_->kind() == LIST || current_->kind() == MAP)
|
|
? current_->type()
|
|
: nullptr),
|
|
OBJECT, DataPiece::NullData(), false,
|
|
child == nullptr ? current_->path() : child->path(),
|
|
suppress_empty_list_, preserve_proto_field_names_,
|
|
use_ints_for_enums_, field_scrub_callback_));
|
|
child = node.get();
|
|
current_->AddChild(node.release());
|
|
}
|
|
|
|
child->set_is_placeholder(false);
|
|
if (child->kind() == OBJECT && child->number_of_children() == 0) {
|
|
child->PopulateChildren(typeinfo_);
|
|
}
|
|
|
|
stack_.push(current_);
|
|
current_ = child;
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() {
|
|
if (stack_.empty()) {
|
|
// The root object ends here. Writes out the tree.
|
|
WriteRoot();
|
|
return this;
|
|
}
|
|
current_ = stack_.top();
|
|
stack_.pop();
|
|
return this;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
|
|
StringPiece name) {
|
|
if (current_ == nullptr) {
|
|
std::vector<std::string> path;
|
|
root_.reset(CreateNewNode(std::string(name), &type_, LIST,
|
|
DataPiece::NullData(), false, path,
|
|
suppress_empty_list_, preserve_proto_field_names_,
|
|
use_ints_for_enums_, field_scrub_callback_));
|
|
current_ = root_.get();
|
|
return this;
|
|
}
|
|
MaybePopulateChildrenOfAny(current_);
|
|
Node* child = current_->FindChild(name);
|
|
if (child == nullptr || child->kind() != LIST) {
|
|
std::unique_ptr<Node> node(CreateNewNode(
|
|
std::string(name), nullptr, LIST, DataPiece::NullData(), false,
|
|
child == nullptr ? current_->path() : child->path(),
|
|
suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_,
|
|
field_scrub_callback_));
|
|
child = node.get();
|
|
current_->AddChild(node.release());
|
|
}
|
|
child->set_is_placeholder(false);
|
|
|
|
stack_.push(current_);
|
|
current_ = child;
|
|
return this;
|
|
}
|
|
|
|
void DefaultValueObjectWriter::WriteRoot() {
|
|
root_->WriteTo(ow_);
|
|
root_.reset(nullptr);
|
|
current_ = nullptr;
|
|
}
|
|
|
|
DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() {
|
|
if (stack_.empty()) {
|
|
WriteRoot();
|
|
return this;
|
|
}
|
|
current_ = stack_.top();
|
|
stack_.pop();
|
|
return this;
|
|
}
|
|
|
|
void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
|
|
const DataPiece& data) {
|
|
MaybePopulateChildrenOfAny(current_);
|
|
if (current_->type() != nullptr && current_->type()->name() == kAnyType &&
|
|
name == "@type") {
|
|
util::StatusOr<std::string> data_string = data.ToString();
|
|
if (data_string.ok()) {
|
|
const std::string& string_value = data_string.value();
|
|
// If the type of current_ is "Any" and its "@type" field is being set
|
|
// here, sets the type of current_ to be the type specified by the
|
|
// "@type".
|
|
util::StatusOr<const google::protobuf::Type*> found_type =
|
|
typeinfo_->ResolveTypeUrl(string_value);
|
|
if (!found_type.ok()) {
|
|
GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'.";
|
|
} else {
|
|
current_->set_type(found_type.value());
|
|
}
|
|
current_->set_is_any(true);
|
|
// If the "@type" field is placed after other fields, we should populate
|
|
// other children of primitive type now. Otherwise, we should wait until
|
|
// the first value field is rendered before we populate the children,
|
|
// because the "value" field of a Any message could be omitted.
|
|
if (current_->number_of_children() > 1 && current_->type() != nullptr) {
|
|
current_->PopulateChildren(typeinfo_);
|
|
}
|
|
}
|
|
}
|
|
Node* child = current_->FindChild(name);
|
|
if (child == nullptr || child->kind() != PRIMITIVE) {
|
|
// No children are found, creates a new child.
|
|
std::unique_ptr<Node> node(
|
|
CreateNewNode(std::string(name), nullptr, PRIMITIVE, data, false,
|
|
child == nullptr ? current_->path() : child->path(),
|
|
suppress_empty_list_, preserve_proto_field_names_,
|
|
use_ints_for_enums_, field_scrub_callback_));
|
|
current_->AddChild(node.release());
|
|
} else {
|
|
child->set_data(data);
|
|
child->set_is_placeholder(false);
|
|
}
|
|
}
|
|
|
|
} // namespace converter
|
|
} // namespace util
|
|
} // namespace protobuf
|
|
} // namespace google
|