1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#include <google/protobuf/util/internal/default_value_objectwriter.h>
32
33#include <cstdint>
34#include <unordered_map>
35
36#include <google/protobuf/util/internal/constants.h>
37#include <google/protobuf/util/internal/utility.h>
38#include <google/protobuf/stubs/map_util.h>
39
40namespace google {
41namespace protobuf {
42namespace util {
43namespace converter {
44
45namespace {
46// Helper function to convert string value to given data type by calling the
47// passed converter function on the DataPiece created from "value" argument.
48// If value is empty or if conversion fails, the default_value is returned.
49template <typename T>
50T ConvertTo(StringPiece value,
51 util::StatusOr<T> (DataPiece::*converter_fn)() const,
52 T default_value) {
53 if (value.empty()) return default_value;
54 util::StatusOr<T> result = (DataPiece(value, true).*converter_fn)();
55 return result.ok() ? result.value() : default_value;
56}
57} // namespace
58
59DefaultValueObjectWriter::DefaultValueObjectWriter(
60 TypeResolver* type_resolver, const google::protobuf::Type& type,
61 ObjectWriter* ow)
62 : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
63 own_typeinfo_(true),
64 type_(type),
65 current_(nullptr),
66 root_(nullptr),
67 suppress_empty_list_(false),
68 preserve_proto_field_names_(false),
69 use_ints_for_enums_(false),
70 ow_(ow) {}
71
72DefaultValueObjectWriter::~DefaultValueObjectWriter() {
73 if (own_typeinfo_) {
74 delete typeinfo_;
75 }
76}
77
78DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(
79 StringPiece name, bool value) {
80 if (current_ == nullptr) {
81 ow_->RenderBool(name, value);
82 } else {
83 RenderDataPiece(name, data: DataPiece(value));
84 }
85 return this;
86}
87
88DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32(
89 StringPiece name, int32_t value) {
90 if (current_ == nullptr) {
91 ow_->RenderInt32(name, value);
92 } else {
93 RenderDataPiece(name, data: DataPiece(value));
94 }
95 return this;
96}
97
98DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32(
99 StringPiece name, uint32_t value) {
100 if (current_ == nullptr) {
101 ow_->RenderUint32(name, value);
102 } else {
103 RenderDataPiece(name, data: DataPiece(value));
104 }
105 return this;
106}
107
108DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64(
109 StringPiece name, int64_t value) {
110 if (current_ == nullptr) {
111 ow_->RenderInt64(name, value);
112 } else {
113 RenderDataPiece(name, data: DataPiece(value));
114 }
115 return this;
116}
117
118DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64(
119 StringPiece name, uint64_t value) {
120 if (current_ == nullptr) {
121 ow_->RenderUint64(name, value);
122 } else {
123 RenderDataPiece(name, data: DataPiece(value));
124 }
125 return this;
126}
127
128DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble(
129 StringPiece name, double value) {
130 if (current_ == nullptr) {
131 ow_->RenderDouble(name, value);
132 } else {
133 RenderDataPiece(name, data: DataPiece(value));
134 }
135 return this;
136}
137
138DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat(
139 StringPiece name, float value) {
140 if (current_ == nullptr) {
141 ow_->RenderBool(name, value);
142 } else {
143 RenderDataPiece(name, data: DataPiece(value));
144 }
145 return this;
146}
147
148DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString(
149 StringPiece name, StringPiece value) {
150 if (current_ == nullptr) {
151 ow_->RenderString(name, value);
152 } else {
153 // Since StringPiece is essentially a pointer, takes a copy of "value" to
154 // avoid ownership issues.
155 string_values_.emplace_back(args: new std::string(value));
156 RenderDataPiece(name, data: DataPiece(*string_values_.back(), true));
157 }
158 return this;
159}
160
161DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes(
162 StringPiece name, StringPiece value) {
163 if (current_ == nullptr) {
164 ow_->RenderBytes(name, value);
165 } else {
166 // Since StringPiece is essentially a pointer, takes a copy of "value" to
167 // avoid ownership issues.
168 string_values_.emplace_back(args: new std::string(value));
169 RenderDataPiece(name, data: DataPiece(*string_values_.back(), false, true));
170 }
171 return this;
172}
173
174DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
175 StringPiece name) {
176 if (current_ == nullptr) {
177 ow_->RenderNull(name);
178 } else {
179 RenderDataPiece(name, data: DataPiece::NullData());
180 }
181 return this;
182}
183
184void DefaultValueObjectWriter::RegisterFieldScrubCallBack(
185 FieldScrubCallBack field_scrub_callback) {
186 field_scrub_callback_ = std::move(field_scrub_callback);
187}
188
189DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode(
190 const std::string& name, const google::protobuf::Type* type, NodeKind kind,
191 const DataPiece& data, bool is_placeholder,
192 const std::vector<std::string>& path, bool suppress_empty_list,
193 bool preserve_proto_field_names, bool use_ints_for_enums,
194 FieldScrubCallBack field_scrub_callback) {
195 return new Node(name, type, kind, data, is_placeholder, path,
196 suppress_empty_list, preserve_proto_field_names,
197 use_ints_for_enums, std::move(field_scrub_callback));
198}
199
200DefaultValueObjectWriter::Node::Node(
201 const std::string& name, const google::protobuf::Type* type, NodeKind kind,
202 const DataPiece& data, bool is_placeholder,
203 const std::vector<std::string>& path, bool suppress_empty_list,
204 bool preserve_proto_field_names, bool use_ints_for_enums,
205 FieldScrubCallBack field_scrub_callback)
206 : name_(name),
207 type_(type),
208 kind_(kind),
209 is_any_(false),
210 data_(data),
211 is_placeholder_(is_placeholder),
212 path_(path),
213 suppress_empty_list_(suppress_empty_list),
214 preserve_proto_field_names_(preserve_proto_field_names),
215 use_ints_for_enums_(use_ints_for_enums),
216 field_scrub_callback_(std::move(field_scrub_callback)) {}
217
218DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
219 StringPiece name) {
220 if (name.empty() || kind_ != OBJECT) {
221 return nullptr;
222 }
223 for (Node* child : children_) {
224 if (child->name() == name) {
225 return child;
226 }
227 }
228 return nullptr;
229}
230
231void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
232 if (kind_ == PRIMITIVE) {
233 ObjectWriter::RenderDataPieceTo(data: data_, name: name_, ow);
234 return;
235 }
236
237 // Render maps. Empty maps are rendered as "{}".
238 if (kind_ == MAP) {
239 ow->StartObject(name: name_);
240 WriteChildren(ow);
241 ow->EndObject();
242 return;
243 }
244
245 // Write out lists. If we didn't have any list in response, write out empty
246 // list.
247 if (kind_ == LIST) {
248 // Suppress empty lists if requested.
249 if (suppress_empty_list_ && is_placeholder_) return;
250
251 ow->StartList(name: name_);
252 WriteChildren(ow);
253 ow->EndList();
254 return;
255 }
256
257 // If is_placeholder_ = true, we didn't see this node in the response, so
258 // skip output.
259 if (is_placeholder_) return;
260
261 ow->StartObject(name: name_);
262 WriteChildren(ow);
263 ow->EndObject();
264}
265
266void DefaultValueObjectWriter::Node::WriteChildren(ObjectWriter* ow) {
267 for (Node* child : children_) {
268 child->WriteTo(ow);
269 }
270}
271
272const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType(
273 const google::protobuf::Type& found_type, const TypeInfo* typeinfo) {
274 // If this field is a map, we should use the type of its "Value" as
275 // the type of the child node.
276 for (int i = 0; i < found_type.fields_size(); ++i) {
277 const google::protobuf::Field& sub_field = found_type.fields(index: i);
278 if (sub_field.number() != 2) {
279 continue;
280 }
281 if (sub_field.kind() != google::protobuf::Field::TYPE_MESSAGE) {
282 // This map's value type is not a message type. We don't need to
283 // get the field_type in this case.
284 break;
285 }
286 util::StatusOr<const google::protobuf::Type*> sub_type =
287 typeinfo->ResolveTypeUrl(type_url: sub_field.type_url());
288 if (!sub_type.ok()) {
289 GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'.";
290 } else {
291 return sub_type.value();
292 }
293 break;
294 }
295 return nullptr;
296}
297
298void DefaultValueObjectWriter::Node::PopulateChildren(
299 const TypeInfo* typeinfo) {
300 // Ignores well known types that don't require automatically populating their
301 // primitive children. For type "Any", we only populate its children when the
302 // "@type" field is set.
303 // TODO(tsun): remove "kStructValueType" from the list. It's being checked
304 // now because of a bug in the tool-chain that causes the "oneof_index"
305 // of kStructValueType to not be set correctly.
306 if (type_ == nullptr || type_->name() == kAnyType ||
307 type_->name() == kStructType || type_->name() == kTimestampType ||
308 type_->name() == kDurationType || type_->name() == kStructValueType) {
309 return;
310 }
311 std::vector<Node*> new_children;
312 std::unordered_map<std::string, int> orig_children_map;
313
314 // Creates a map of child nodes to speed up lookup.
315 for (int i = 0; i < children_.size(); ++i) {
316 InsertIfNotPresent(collection: &orig_children_map, key: children_[i]->name_, value: i);
317 }
318
319 for (int i = 0; i < type_->fields_size(); ++i) {
320 const google::protobuf::Field& field = type_->fields(index: i);
321
322 // This code is checking if the field to be added to the tree should be
323 // scrubbed or not by calling the field_scrub_callback_ callback function.
324 std::vector<std::string> path;
325 if (!path_.empty()) {
326 path.insert(position: path.begin(), first: path_.begin(), last: path_.end());
327 }
328 path.push_back(x: field.name());
329 if (field_scrub_callback_ && field_scrub_callback_(path, &field)) {
330 continue;
331 }
332
333 std::unordered_map<std::string, int>::iterator found =
334 orig_children_map.find(x: field.name());
335 // If the child field has already been set, we just add it to the new list
336 // of children.
337 if (found != orig_children_map.end()) {
338 new_children.push_back(x: children_[found->second]);
339 children_[found->second] = nullptr;
340 continue;
341 }
342
343 const google::protobuf::Type* field_type = nullptr;
344 bool is_map = false;
345 NodeKind kind = PRIMITIVE;
346
347 if (field.kind() == google::protobuf::Field::TYPE_MESSAGE) {
348 kind = OBJECT;
349 util::StatusOr<const google::protobuf::Type*> found_result =
350 typeinfo->ResolveTypeUrl(type_url: field.type_url());
351 if (!found_result.ok()) {
352 // "field" is of an unknown type.
353 GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'.";
354 } else {
355 const google::protobuf::Type* found_type = found_result.value();
356 is_map = IsMap(field, type: *found_type);
357
358 if (!is_map) {
359 field_type = found_type;
360 } else {
361 // If this field is a map, we should use the type of its "Value" as
362 // the type of the child node.
363 field_type = GetMapValueType(found_type: *found_type, typeinfo);
364 kind = MAP;
365 }
366 }
367 }
368
369 if (!is_map &&
370 field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED) {
371 kind = LIST;
372 }
373
374 // If oneof_index() != 0, the child field is part of a "oneof", which means
375 // the child field is optional and we shouldn't populate its default
376 // primitive value.
377 if (field.oneof_index() != 0 && kind == PRIMITIVE) continue;
378
379 // If the child field is of primitive type, sets its data to the default
380 // value of its type.
381 std::unique_ptr<Node> child(
382 new Node(preserve_proto_field_names_ ? field.name() : field.json_name(),
383 field_type, kind,
384 kind == PRIMITIVE ? CreateDefaultDataPieceForField(
385 field, typeinfo, use_ints_for_enums: use_ints_for_enums_)
386 : DataPiece::NullData(),
387 true, path, suppress_empty_list_, preserve_proto_field_names_,
388 use_ints_for_enums_, field_scrub_callback_));
389 new_children.push_back(x: child.release());
390 }
391 // Adds all leftover nodes in children_ to the beginning of new_child.
392 for (int i = 0; i < children_.size(); ++i) {
393 if (children_[i] == nullptr) {
394 continue;
395 }
396 new_children.insert(position: new_children.begin(), x: children_[i]);
397 children_[i] = nullptr;
398 }
399 children_.swap(x&: new_children);
400}
401
402void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
403 // If this is an "Any" node with "@type" already given and no other children
404 // have been added, populates its children.
405 if (node != nullptr && node->is_any() && node->type() != nullptr &&
406 node->type()->name() != kAnyType && node->number_of_children() == 1) {
407 node->PopulateChildren(typeinfo: typeinfo_);
408 }
409}
410
411DataPiece DefaultValueObjectWriter::FindEnumDefault(
412 const google::protobuf::Field& field, const TypeInfo* typeinfo,
413 bool use_ints_for_enums) {
414 const google::protobuf::Enum* enum_type =
415 typeinfo->GetEnumByTypeUrl(type_url: field.type_url());
416 if (!enum_type) {
417 GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url()
418 << "'";
419 return DataPiece::NullData();
420 }
421 if (!field.default_value().empty()) {
422 if (!use_ints_for_enums) {
423 return DataPiece(field.default_value(), true);
424 } else {
425 const std::string& enum_default_value_name = field.default_value();
426 for (int enum_index = 0; enum_index < enum_type->enumvalue_size();
427 ++enum_index) {
428 auto& enum_value = enum_type->enumvalue(index: enum_index);
429 if (enum_value.name() == enum_default_value_name)
430 return DataPiece(enum_value.number());
431 }
432 GOOGLE_LOG(WARNING) << "Could not find enum value '" << enum_default_value_name
433 << "' with type '" << field.type_url() << "'";
434 return DataPiece::NullData();
435 }
436 }
437 // We treat the first value as the default if none is specified.
438 return enum_type->enumvalue_size() > 0
439 ? (use_ints_for_enums
440 ? DataPiece(enum_type->enumvalue(index: 0).number())
441 : DataPiece(enum_type->enumvalue(index: 0).name(), true))
442 : DataPiece::NullData();
443}
444
445DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
446 const google::protobuf::Field& field, const TypeInfo* typeinfo,
447 bool use_ints_for_enums) {
448 switch (field.kind()) {
449 case google::protobuf::Field::TYPE_DOUBLE: {
450 return DataPiece(ConvertTo<double>(
451 value: field.default_value(), converter_fn: &DataPiece::ToDouble, default_value: static_cast<double>(0)));
452 }
453 case google::protobuf::Field::TYPE_FLOAT: {
454 return DataPiece(ConvertTo<float>(
455 value: field.default_value(), converter_fn: &DataPiece::ToFloat, default_value: static_cast<float>(0)));
456 }
457 case google::protobuf::Field::TYPE_INT64:
458 case google::protobuf::Field::TYPE_SINT64:
459 case google::protobuf::Field::TYPE_SFIXED64: {
460 return DataPiece(ConvertTo<int64_t>(
461 value: field.default_value(), converter_fn: &DataPiece::ToInt64, default_value: static_cast<int64_t>(0)));
462 }
463 case google::protobuf::Field::TYPE_UINT64:
464 case google::protobuf::Field::TYPE_FIXED64: {
465 return DataPiece(ConvertTo<uint64_t>(value: field.default_value(),
466 converter_fn: &DataPiece::ToUint64,
467 default_value: static_cast<uint64_t>(0)));
468 }
469 case google::protobuf::Field::TYPE_INT32:
470 case google::protobuf::Field::TYPE_SINT32:
471 case google::protobuf::Field::TYPE_SFIXED32: {
472 return DataPiece(ConvertTo<int32_t>(
473 value: field.default_value(), converter_fn: &DataPiece::ToInt32, default_value: static_cast<int32_t>(0)));
474 }
475 case google::protobuf::Field::TYPE_BOOL: {
476 return DataPiece(
477 ConvertTo<bool>(value: field.default_value(), converter_fn: &DataPiece::ToBool, default_value: false));
478 }
479 case google::protobuf::Field::TYPE_STRING: {
480 return DataPiece(field.default_value(), true);
481 }
482 case google::protobuf::Field::TYPE_BYTES: {
483 return DataPiece(field.default_value(), false, true);
484 }
485 case google::protobuf::Field::TYPE_UINT32:
486 case google::protobuf::Field::TYPE_FIXED32: {
487 return DataPiece(ConvertTo<uint32_t>(value: field.default_value(),
488 converter_fn: &DataPiece::ToUint32,
489 default_value: static_cast<uint32_t>(0)));
490 }
491 case google::protobuf::Field::TYPE_ENUM: {
492 return FindEnumDefault(field, typeinfo, use_ints_for_enums);
493 }
494 default: {
495 return DataPiece::NullData();
496 }
497 }
498}
499
500DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
501 StringPiece name) {
502 if (current_ == nullptr) {
503 std::vector<std::string> path;
504 root_.reset(p: CreateNewNode(std::string(name), type: &type_, kind: OBJECT,
505 data: DataPiece::NullData(), is_placeholder: false, path,
506 suppress_empty_list: suppress_empty_list_, preserve_proto_field_names: preserve_proto_field_names_,
507 use_ints_for_enums: use_ints_for_enums_, field_scrub_callback: field_scrub_callback_));
508 root_->PopulateChildren(typeinfo: typeinfo_);
509 current_ = root_.get();
510 return this;
511 }
512 MaybePopulateChildrenOfAny(node: current_);
513 Node* child = current_->FindChild(name);
514 if (current_->kind() == LIST || current_->kind() == MAP || child == nullptr) {
515 // If current_ is a list or a map node, we should create a new child and use
516 // the type of current_ as the type of the new child.
517 std::unique_ptr<Node> node(
518 CreateNewNode(std::string(name),
519 type: ((current_->kind() == LIST || current_->kind() == MAP)
520 ? current_->type()
521 : nullptr),
522 kind: OBJECT, data: DataPiece::NullData(), is_placeholder: false,
523 path: child == nullptr ? current_->path() : child->path(),
524 suppress_empty_list: suppress_empty_list_, preserve_proto_field_names: preserve_proto_field_names_,
525 use_ints_for_enums: use_ints_for_enums_, field_scrub_callback: field_scrub_callback_));
526 child = node.get();
527 current_->AddChild(child: node.release());
528 }
529
530 child->set_is_placeholder(false);
531 if (child->kind() == OBJECT && child->number_of_children() == 0) {
532 child->PopulateChildren(typeinfo: typeinfo_);
533 }
534
535 stack_.push(x: current_);
536 current_ = child;
537 return this;
538}
539
540DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() {
541 if (stack_.empty()) {
542 // The root object ends here. Writes out the tree.
543 WriteRoot();
544 return this;
545 }
546 current_ = stack_.top();
547 stack_.pop();
548 return this;
549}
550
551DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
552 StringPiece name) {
553 if (current_ == nullptr) {
554 std::vector<std::string> path;
555 root_.reset(p: CreateNewNode(std::string(name), type: &type_, kind: LIST,
556 data: DataPiece::NullData(), is_placeholder: false, path,
557 suppress_empty_list: suppress_empty_list_, preserve_proto_field_names: preserve_proto_field_names_,
558 use_ints_for_enums: use_ints_for_enums_, field_scrub_callback: field_scrub_callback_));
559 current_ = root_.get();
560 return this;
561 }
562 MaybePopulateChildrenOfAny(node: current_);
563 Node* child = current_->FindChild(name);
564 if (child == nullptr || child->kind() != LIST) {
565 std::unique_ptr<Node> node(CreateNewNode(
566 std::string(name), type: nullptr, kind: LIST, data: DataPiece::NullData(), is_placeholder: false,
567 path: child == nullptr ? current_->path() : child->path(),
568 suppress_empty_list: suppress_empty_list_, preserve_proto_field_names: preserve_proto_field_names_, use_ints_for_enums: use_ints_for_enums_,
569 field_scrub_callback: field_scrub_callback_));
570 child = node.get();
571 current_->AddChild(child: node.release());
572 }
573 child->set_is_placeholder(false);
574
575 stack_.push(x: current_);
576 current_ = child;
577 return this;
578}
579
580void DefaultValueObjectWriter::WriteRoot() {
581 root_->WriteTo(ow: ow_);
582 root_.reset(p: nullptr);
583 current_ = nullptr;
584}
585
586DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() {
587 if (stack_.empty()) {
588 WriteRoot();
589 return this;
590 }
591 current_ = stack_.top();
592 stack_.pop();
593 return this;
594}
595
596void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
597 const DataPiece& data) {
598 MaybePopulateChildrenOfAny(node: current_);
599 if (current_->type() != nullptr && current_->type()->name() == kAnyType &&
600 name == "@type") {
601 util::StatusOr<std::string> data_string = data.ToString();
602 if (data_string.ok()) {
603 const std::string& string_value = data_string.value();
604 // If the type of current_ is "Any" and its "@type" field is being set
605 // here, sets the type of current_ to be the type specified by the
606 // "@type".
607 util::StatusOr<const google::protobuf::Type*> found_type =
608 typeinfo_->ResolveTypeUrl(type_url: string_value);
609 if (!found_type.ok()) {
610 GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'.";
611 } else {
612 current_->set_type(found_type.value());
613 }
614 current_->set_is_any(true);
615 // If the "@type" field is placed after other fields, we should populate
616 // other children of primitive type now. Otherwise, we should wait until
617 // the first value field is rendered before we populate the children,
618 // because the "value" field of a Any message could be omitted.
619 if (current_->number_of_children() > 1 && current_->type() != nullptr) {
620 current_->PopulateChildren(typeinfo: typeinfo_);
621 }
622 }
623 }
624 Node* child = current_->FindChild(name);
625 if (child == nullptr || child->kind() != PRIMITIVE) {
626 // No children are found, creates a new child.
627 std::unique_ptr<Node> node(
628 CreateNewNode(std::string(name), type: nullptr, kind: PRIMITIVE, data, is_placeholder: false,
629 path: child == nullptr ? current_->path() : child->path(),
630 suppress_empty_list: suppress_empty_list_, preserve_proto_field_names: preserve_proto_field_names_,
631 use_ints_for_enums: use_ints_for_enums_, field_scrub_callback: field_scrub_callback_));
632 current_->AddChild(child: node.release());
633 } else {
634 child->set_data(data);
635 child->set_is_placeholder(false);
636 }
637}
638
639} // namespace converter
640} // namespace util
641} // namespace protobuf
642} // namespace google
643