| // 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 <google/protobuf/util/internal/default_value_objectwriter.h> |
| |
| #include <google/protobuf/stubs/hash.h> |
| |
| #include <google/protobuf/util/internal/constants.h> |
| #include <google/protobuf/util/internal/utility.h> |
| #include <google/protobuf/stubs/map_util.h> |
| |
| namespace google { |
| namespace protobuf { |
| namespace util { |
| using util::Status; |
| using util::StatusOr; |
| 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, StatusOr<T> (DataPiece::*converter_fn)() const, |
| T default_value) { |
| if (value.empty()) return default_value; |
| StatusOr<T> result = (DataPiece(value, true).*converter_fn)(); |
| return result.ok() ? result.ValueOrDie() : 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), |
| field_scrub_callback_(nullptr), |
| ow_(ow) {} |
| |
| DefaultValueObjectWriter::~DefaultValueObjectWriter() { |
| for (int i = 0; i < string_values_.size(); ++i) { |
| delete string_values_[i]; |
| } |
| 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 value) { |
| if (current_ == nullptr) { |
| ow_->RenderInt32(name, value); |
| } else { |
| RenderDataPiece(name, DataPiece(value)); |
| } |
| return this; |
| } |
| |
| DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32( |
| StringPiece name, uint32 value) { |
| if (current_ == nullptr) { |
| ow_->RenderUint32(name, value); |
| } else { |
| RenderDataPiece(name, DataPiece(value)); |
| } |
| return this; |
| } |
| |
| DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64( |
| StringPiece name, int64 value) { |
| if (current_ == nullptr) { |
| ow_->RenderInt64(name, value); |
| } else { |
| RenderDataPiece(name, DataPiece(value)); |
| } |
| return this; |
| } |
| |
| DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64( |
| StringPiece name, uint64 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_.push_back(new string(value.ToString())); |
| 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_.push_back(new string(value.ToString())); |
| 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( |
| FieldScrubCallBackPtr field_scrub_callback) { |
| field_scrub_callback_.reset(field_scrub_callback.release()); |
| } |
| |
| DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode( |
| const string& name, const google::protobuf::Type* type, NodeKind kind, |
| const DataPiece& data, bool is_placeholder, const std::vector<string>& path, |
| bool suppress_empty_list, FieldScrubCallBack* field_scrub_callback) { |
| return new Node(name, type, kind, data, is_placeholder, path, |
| suppress_empty_list, field_scrub_callback); |
| } |
| |
| DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode( |
| const string& name, const google::protobuf::Type* type, NodeKind kind, |
| const DataPiece& data, bool is_placeholder, const std::vector<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, |
| field_scrub_callback); |
| } |
| |
| DefaultValueObjectWriter::Node::Node( |
| const string& name, const google::protobuf::Type* type, NodeKind kind, |
| const DataPiece& data, bool is_placeholder, const std::vector<string>& path, |
| bool suppress_empty_list, 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_(false), |
| use_ints_for_enums_(false), |
| field_scrub_callback_(field_scrub_callback) {} |
| |
| DefaultValueObjectWriter::Node::Node( |
| const string& name, const google::protobuf::Type* type, NodeKind kind, |
| const DataPiece& data, bool is_placeholder, const std::vector<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_(field_scrub_callback) {} |
| |
| DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( |
| StringPiece name) { |
| if (name.empty() || kind_ != OBJECT) { |
| return nullptr; |
| } |
| for (int i = 0; i < children_.size(); ++i) { |
| Node* child = children_[i]; |
| 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 (int i = 0; i < children_.size(); ++i) { |
| Node* child = children_[i]; |
| 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_Kind_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.ValueOrDie(); |
| } |
| 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; |
| hash_map<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<string> path; |
| if (!path_.empty()) { |
| path.insert(path.begin(), path_.begin(), path_.end()); |
| } |
| path.push_back(field.name()); |
| if (field_scrub_callback_ != nullptr && |
| field_scrub_callback_->Run(path, &field)) { |
| continue; |
| } |
| |
| hash_map<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_Kind_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.ValueOrDie(); |
| 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_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) { |
| if (!field.default_value().empty()) |
| return DataPiece(field.default_value(), true); |
| |
| 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(); |
| } |
| // 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_Kind_TYPE_DOUBLE: { |
| return DataPiece(ConvertTo<double>( |
| field.default_value(), &DataPiece::ToDouble, static_cast<double>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_FLOAT: { |
| return DataPiece(ConvertTo<float>( |
| field.default_value(), &DataPiece::ToFloat, static_cast<float>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_INT64: |
| case google::protobuf::Field_Kind_TYPE_SINT64: |
| case google::protobuf::Field_Kind_TYPE_SFIXED64: { |
| return DataPiece(ConvertTo<int64>( |
| field.default_value(), &DataPiece::ToInt64, static_cast<int64>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_UINT64: |
| case google::protobuf::Field_Kind_TYPE_FIXED64: { |
| return DataPiece(ConvertTo<uint64>( |
| field.default_value(), &DataPiece::ToUint64, static_cast<uint64>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_INT32: |
| case google::protobuf::Field_Kind_TYPE_SINT32: |
| case google::protobuf::Field_Kind_TYPE_SFIXED32: { |
| return DataPiece(ConvertTo<int32>( |
| field.default_value(), &DataPiece::ToInt32, static_cast<int32>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_BOOL: { |
| return DataPiece( |
| ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false)); |
| } |
| case google::protobuf::Field_Kind_TYPE_STRING: { |
| return DataPiece(field.default_value(), true); |
| } |
| case google::protobuf::Field_Kind_TYPE_BYTES: { |
| return DataPiece(field.default_value(), false, true); |
| } |
| case google::protobuf::Field_Kind_TYPE_UINT32: |
| case google::protobuf::Field_Kind_TYPE_FIXED32: { |
| return DataPiece(ConvertTo<uint32>( |
| field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0))); |
| } |
| case google::protobuf::Field_Kind_TYPE_ENUM: { |
| return FindEnumDefault(field, typeinfo, use_ints_for_enums); |
| } |
| default: { return DataPiece::NullData(); } |
| } |
| } |
| |
| DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( |
| StringPiece name) { |
| if (current_ == nullptr) { |
| std::vector<string> path; |
| root_.reset(CreateNewNode(string(name), &type_, OBJECT, |
| DataPiece::NullData(), false, path, |
| suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, |
| field_scrub_callback_.get())); |
| 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(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_.get())); |
| 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<string> path; |
| root_.reset(CreateNewNode(string(name), &type_, LIST, DataPiece::NullData(), |
| false, path, suppress_empty_list_, |
| preserve_proto_field_names_, use_ints_for_enums_, |
| field_scrub_callback_.get())); |
| current_ = root_.get(); |
| return this; |
| } |
| MaybePopulateChildrenOfAny(current_); |
| Node* child = current_->FindChild(name); |
| if (child == nullptr || child->kind() != LIST) { |
| std::unique_ptr<Node> node( |
| CreateNewNode(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_.get())); |
| 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<string> data_string = data.ToString(); |
| if (data_string.ok()) { |
| const string& string_value = data_string.ValueOrDie(); |
| // 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.ValueOrDie()); |
| } |
| 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(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_.get())); |
| current_->AddChild(node.release()); |
| } else { |
| child->set_data(data); |
| child->set_is_placeholder(false); |
| } |
| } |
| |
| } // namespace converter |
| } // namespace util |
| } // namespace protobuf |
| } // namespace google |