| OLD | NEW |
| (Empty) | |
| 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 <google/protobuf/stubs/hash.h> |
| 34 |
| 35 #include <google/protobuf/util/internal/constants.h> |
| 36 #include <google/protobuf/util/internal/utility.h> |
| 37 #include <google/protobuf/stubs/map_util.h> |
| 38 |
| 39 namespace google { |
| 40 namespace protobuf { |
| 41 namespace util { |
| 42 using util::Status; |
| 43 using util::StatusOr; |
| 44 namespace converter { |
| 45 |
| 46 namespace { |
| 47 // Helper function to convert string value to given data type by calling the |
| 48 // passed converter function on the DataPiece created from "value" argument. |
| 49 // If value is empty or if conversion fails, the default_value is returned. |
| 50 template <typename T> |
| 51 T ConvertTo(StringPiece value, StatusOr<T> (DataPiece::*converter_fn)() const, |
| 52 T default_value) { |
| 53 if (value.empty()) return default_value; |
| 54 StatusOr<T> result = (DataPiece(value).*converter_fn)(); |
| 55 return result.ok() ? result.ValueOrDie() : default_value; |
| 56 } |
| 57 } // namespace |
| 58 |
| 59 DefaultValueObjectWriter::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_(NULL), |
| 66 root_(NULL), |
| 67 ow_(ow) {} |
| 68 |
| 69 DefaultValueObjectWriter::~DefaultValueObjectWriter() { |
| 70 for (int i = 0; i < string_values_.size(); ++i) { |
| 71 delete string_values_[i]; |
| 72 } |
| 73 if (own_typeinfo_) { |
| 74 delete typeinfo_; |
| 75 } |
| 76 } |
| 77 |
| 78 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(StringPiece name, |
| 79 bool value) { |
| 80 if (current_ == NULL) { |
| 81 ow_->RenderBool(name, value); |
| 82 } else { |
| 83 RenderDataPiece(name, DataPiece(value)); |
| 84 } |
| 85 return this; |
| 86 } |
| 87 |
| 88 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32( |
| 89 StringPiece name, int32 value) { |
| 90 if (current_ == NULL) { |
| 91 ow_->RenderInt32(name, value); |
| 92 } else { |
| 93 RenderDataPiece(name, DataPiece(value)); |
| 94 } |
| 95 return this; |
| 96 } |
| 97 |
| 98 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32( |
| 99 StringPiece name, uint32 value) { |
| 100 if (current_ == NULL) { |
| 101 ow_->RenderUint32(name, value); |
| 102 } else { |
| 103 RenderDataPiece(name, DataPiece(value)); |
| 104 } |
| 105 return this; |
| 106 } |
| 107 |
| 108 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64( |
| 109 StringPiece name, int64 value) { |
| 110 if (current_ == NULL) { |
| 111 ow_->RenderInt64(name, value); |
| 112 } else { |
| 113 RenderDataPiece(name, DataPiece(value)); |
| 114 } |
| 115 return this; |
| 116 } |
| 117 |
| 118 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64( |
| 119 StringPiece name, uint64 value) { |
| 120 if (current_ == NULL) { |
| 121 ow_->RenderUint64(name, value); |
| 122 } else { |
| 123 RenderDataPiece(name, DataPiece(value)); |
| 124 } |
| 125 return this; |
| 126 } |
| 127 |
| 128 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble( |
| 129 StringPiece name, double value) { |
| 130 if (current_ == NULL) { |
| 131 ow_->RenderDouble(name, value); |
| 132 } else { |
| 133 RenderDataPiece(name, DataPiece(value)); |
| 134 } |
| 135 return this; |
| 136 } |
| 137 |
| 138 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat( |
| 139 StringPiece name, float value) { |
| 140 if (current_ == NULL) { |
| 141 ow_->RenderBool(name, value); |
| 142 } else { |
| 143 RenderDataPiece(name, DataPiece(value)); |
| 144 } |
| 145 return this; |
| 146 } |
| 147 |
| 148 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString( |
| 149 StringPiece name, StringPiece value) { |
| 150 if (current_ == NULL) { |
| 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_.push_back(new string(value.ToString())); |
| 156 RenderDataPiece(name, DataPiece(*string_values_.back())); |
| 157 } |
| 158 return this; |
| 159 } |
| 160 |
| 161 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes( |
| 162 StringPiece name, StringPiece value) { |
| 163 if (current_ == NULL) { |
| 164 ow_->RenderBytes(name, value); |
| 165 } else { |
| 166 RenderDataPiece(name, DataPiece(value)); |
| 167 } |
| 168 return this; |
| 169 } |
| 170 |
| 171 DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull( |
| 172 StringPiece name) { |
| 173 if (current_ == NULL) { |
| 174 ow_->RenderNull(name); |
| 175 } else { |
| 176 RenderDataPiece(name, DataPiece::NullData()); |
| 177 } |
| 178 return this; |
| 179 } |
| 180 |
| 181 DefaultValueObjectWriter::Node::Node(const string& name, |
| 182 const google::protobuf::Type* type, |
| 183 NodeKind kind, const DataPiece& data, |
| 184 bool is_placeholder) |
| 185 : name_(name), |
| 186 type_(type), |
| 187 kind_(kind), |
| 188 is_any_(false), |
| 189 data_(data), |
| 190 is_placeholder_(is_placeholder) {} |
| 191 |
| 192 DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( |
| 193 StringPiece name) { |
| 194 if (name.empty() || kind_ != OBJECT) { |
| 195 return NULL; |
| 196 } |
| 197 for (int i = 0; i < children_.size(); ++i) { |
| 198 Node* child = children_[i]; |
| 199 if (child->name() == name) { |
| 200 return child; |
| 201 } |
| 202 } |
| 203 return NULL; |
| 204 } |
| 205 |
| 206 void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) { |
| 207 if (kind_ == PRIMITIVE) { |
| 208 ObjectWriter::RenderDataPieceTo(data_, name_, ow); |
| 209 return; |
| 210 } |
| 211 |
| 212 // Render maps. Empty maps are rendered as "{}". |
| 213 if (kind_ == MAP) { |
| 214 ow->StartObject(name_); |
| 215 WriteChildren(ow); |
| 216 ow->EndObject(); |
| 217 return; |
| 218 } |
| 219 |
| 220 // Write out lists. If we didn't have any list in response, write out empty |
| 221 // list. |
| 222 if (kind_ == LIST) { |
| 223 ow->StartList(name_); |
| 224 WriteChildren(ow); |
| 225 ow->EndList(); |
| 226 return; |
| 227 } |
| 228 |
| 229 // If is_placeholder_ = true, we didn't see this node in the response, so |
| 230 // skip output. |
| 231 if (is_placeholder_) return; |
| 232 |
| 233 ow->StartObject(name_); |
| 234 WriteChildren(ow); |
| 235 ow->EndObject(); |
| 236 } |
| 237 |
| 238 void DefaultValueObjectWriter::Node::WriteChildren(ObjectWriter* ow) { |
| 239 for (int i = 0; i < children_.size(); ++i) { |
| 240 Node* child = children_[i]; |
| 241 child->WriteTo(ow); |
| 242 } |
| 243 } |
| 244 |
| 245 const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( |
| 246 const google::protobuf::Type& found_type, const TypeInfo* typeinfo) { |
| 247 // If this field is a map, we should use the type of its "Value" as |
| 248 // the type of the child node. |
| 249 for (int i = 0; i < found_type.fields_size(); ++i) { |
| 250 const google::protobuf::Field& sub_field = found_type.fields(i); |
| 251 if (sub_field.number() != 2) { |
| 252 continue; |
| 253 } |
| 254 if (sub_field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE) { |
| 255 // This map's value type is not a message type. We don't need to |
| 256 // get the field_type in this case. |
| 257 break; |
| 258 } |
| 259 util::StatusOr<const google::protobuf::Type*> sub_type = |
| 260 typeinfo->ResolveTypeUrl(sub_field.type_url()); |
| 261 if (!sub_type.ok()) { |
| 262 GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() <<
"'."; |
| 263 } else { |
| 264 return sub_type.ValueOrDie(); |
| 265 } |
| 266 break; |
| 267 } |
| 268 return NULL; |
| 269 } |
| 270 |
| 271 void DefaultValueObjectWriter::Node::PopulateChildren( |
| 272 const TypeInfo* typeinfo) { |
| 273 // Ignores well known types that don't require automatically populating their |
| 274 // primitive children. For type "Any", we only populate its children when the |
| 275 // "@type" field is set. |
| 276 // TODO(tsun): remove "kStructValueType" from the list. It's being checked |
| 277 // now because of a bug in the tool-chain that causes the "oneof_index" |
| 278 // of kStructValueType to not be set correctly. |
| 279 if (type_ == NULL || type_->name() == kAnyType || |
| 280 type_->name() == kStructType || type_->name() == kTimestampType || |
| 281 type_->name() == kDurationType || type_->name() == kStructValueType) { |
| 282 return; |
| 283 } |
| 284 std::vector<Node*> new_children; |
| 285 hash_map<string, int> orig_children_map; |
| 286 |
| 287 // Creates a map of child nodes to speed up lookup. |
| 288 for (int i = 0; i < children_.size(); ++i) { |
| 289 InsertIfNotPresent(&orig_children_map, children_[i]->name_, i); |
| 290 } |
| 291 |
| 292 for (int i = 0; i < type_->fields_size(); ++i) { |
| 293 const google::protobuf::Field& field = type_->fields(i); |
| 294 hash_map<string, int>::iterator found = |
| 295 orig_children_map.find(field.name()); |
| 296 // If the child field has already been set, we just add it to the new list |
| 297 // of children. |
| 298 if (found != orig_children_map.end()) { |
| 299 new_children.push_back(children_[found->second]); |
| 300 children_[found->second] = NULL; |
| 301 continue; |
| 302 } |
| 303 |
| 304 const google::protobuf::Type* field_type = NULL; |
| 305 bool is_map = false; |
| 306 NodeKind kind = PRIMITIVE; |
| 307 |
| 308 if (field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { |
| 309 kind = OBJECT; |
| 310 util::StatusOr<const google::protobuf::Type*> found_result = |
| 311 typeinfo->ResolveTypeUrl(field.type_url()); |
| 312 if (!found_result.ok()) { |
| 313 // "field" is of an unknown type. |
| 314 GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'
."; |
| 315 } else { |
| 316 const google::protobuf::Type* found_type = found_result.ValueOrDie(); |
| 317 is_map = IsMap(field, *found_type); |
| 318 |
| 319 if (!is_map) { |
| 320 field_type = found_type; |
| 321 } else { |
| 322 // If this field is a map, we should use the type of its "Value" as |
| 323 // the type of the child node. |
| 324 field_type = GetMapValueType(*found_type, typeinfo); |
| 325 kind = MAP; |
| 326 } |
| 327 } |
| 328 } |
| 329 |
| 330 if (!is_map && |
| 331 field.cardinality() == |
| 332 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { |
| 333 kind = LIST; |
| 334 } |
| 335 |
| 336 // If oneof_index() != 0, the child field is part of a "oneof", which means |
| 337 // the child field is optional and we shouldn't populate its default value. |
| 338 if (field.oneof_index() != 0) continue; |
| 339 |
| 340 // If the child field is of primitive type, sets its data to the default |
| 341 // value of its type. |
| 342 google::protobuf::scoped_ptr<Node> child(new Node( |
| 343 field.json_name(), field_type, kind, |
| 344 kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo) |
| 345 : DataPiece::NullData(), |
| 346 true)); |
| 347 new_children.push_back(child.release()); |
| 348 } |
| 349 // Adds all leftover nodes in children_ to the beginning of new_child. |
| 350 for (int i = 0; i < children_.size(); ++i) { |
| 351 if (children_[i] == NULL) { |
| 352 continue; |
| 353 } |
| 354 new_children.insert(new_children.begin(), children_[i]); |
| 355 children_[i] = NULL; |
| 356 } |
| 357 children_.swap(new_children); |
| 358 } |
| 359 |
| 360 void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { |
| 361 // If this is an "Any" node with "@type" already given and no other children |
| 362 // have been added, populates its children. |
| 363 if (node != NULL && node->is_any() && node->type() != NULL && |
| 364 node->type()->name() != kAnyType && node->number_of_children() == 1) { |
| 365 node->PopulateChildren(typeinfo_); |
| 366 } |
| 367 } |
| 368 |
| 369 DataPiece DefaultValueObjectWriter::FindEnumDefault( |
| 370 const google::protobuf::Field& field, const TypeInfo* typeinfo) { |
| 371 if (!field.default_value().empty()) return DataPiece(field.default_value()); |
| 372 |
| 373 const google::protobuf::Enum* enum_type = |
| 374 typeinfo->GetEnumByTypeUrl(field.type_url()); |
| 375 if (!enum_type) { |
| 376 GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url() |
| 377 << "'"; |
| 378 return DataPiece::NullData(); |
| 379 } |
| 380 // We treat the first value as the default if none is specified. |
| 381 return enum_type->enumvalue_size() > 0 |
| 382 ? DataPiece(enum_type->enumvalue(0).name()) |
| 383 : DataPiece::NullData(); |
| 384 } |
| 385 |
| 386 DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( |
| 387 const google::protobuf::Field& field, const TypeInfo* typeinfo) { |
| 388 switch (field.kind()) { |
| 389 case google::protobuf::Field_Kind_TYPE_DOUBLE: { |
| 390 return DataPiece(ConvertTo<double>( |
| 391 field.default_value(), &DataPiece::ToDouble, static_cast<double>(0))); |
| 392 } |
| 393 case google::protobuf::Field_Kind_TYPE_FLOAT: { |
| 394 return DataPiece(ConvertTo<float>( |
| 395 field.default_value(), &DataPiece::ToFloat, static_cast<float>(0))); |
| 396 } |
| 397 case google::protobuf::Field_Kind_TYPE_INT64: |
| 398 case google::protobuf::Field_Kind_TYPE_SINT64: |
| 399 case google::protobuf::Field_Kind_TYPE_SFIXED64: { |
| 400 return DataPiece(ConvertTo<int64>( |
| 401 field.default_value(), &DataPiece::ToInt64, static_cast<int64>(0))); |
| 402 } |
| 403 case google::protobuf::Field_Kind_TYPE_UINT64: |
| 404 case google::protobuf::Field_Kind_TYPE_FIXED64: { |
| 405 return DataPiece(ConvertTo<uint64>( |
| 406 field.default_value(), &DataPiece::ToUint64, static_cast<uint64>(0))); |
| 407 } |
| 408 case google::protobuf::Field_Kind_TYPE_INT32: |
| 409 case google::protobuf::Field_Kind_TYPE_SINT32: |
| 410 case google::protobuf::Field_Kind_TYPE_SFIXED32: { |
| 411 return DataPiece(ConvertTo<int32>( |
| 412 field.default_value(), &DataPiece::ToInt32, static_cast<int32>(0))); |
| 413 } |
| 414 case google::protobuf::Field_Kind_TYPE_BOOL: { |
| 415 return DataPiece( |
| 416 ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false)); |
| 417 } |
| 418 case google::protobuf::Field_Kind_TYPE_STRING: { |
| 419 return DataPiece(field.default_value()); |
| 420 } |
| 421 case google::protobuf::Field_Kind_TYPE_BYTES: { |
| 422 return DataPiece(field.default_value(), false); |
| 423 } |
| 424 case google::protobuf::Field_Kind_TYPE_UINT32: |
| 425 case google::protobuf::Field_Kind_TYPE_FIXED32: { |
| 426 return DataPiece(ConvertTo<uint32>( |
| 427 field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0))); |
| 428 } |
| 429 case google::protobuf::Field_Kind_TYPE_ENUM: { |
| 430 return FindEnumDefault(field, typeinfo); |
| 431 } |
| 432 default: { return DataPiece::NullData(); } |
| 433 } |
| 434 } |
| 435 |
| 436 DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( |
| 437 StringPiece name) { |
| 438 if (current_ == NULL) { |
| 439 root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), |
| 440 false)); |
| 441 root_->PopulateChildren(typeinfo_); |
| 442 current_ = root_.get(); |
| 443 return this; |
| 444 } |
| 445 MaybePopulateChildrenOfAny(current_); |
| 446 Node* child = current_->FindChild(name); |
| 447 if (current_->kind() == LIST || current_->kind() == MAP || child == NULL) { |
| 448 // If current_ is a list or a map node, we should create a new child and use |
| 449 // the type of current_ as the type of the new child. |
| 450 google::protobuf::scoped_ptr<Node> node(new Node( |
| 451 name.ToString(), ((current_->kind() == LIST || current_->kind() == MAP) |
| 452 ? current_->type() |
| 453 : NULL), |
| 454 OBJECT, DataPiece::NullData(), false)); |
| 455 child = node.get(); |
| 456 current_->AddChild(node.release()); |
| 457 } |
| 458 |
| 459 child->set_is_placeholder(false); |
| 460 if (child->kind() == OBJECT && child->number_of_children() == 0) { |
| 461 child->PopulateChildren(typeinfo_); |
| 462 } |
| 463 |
| 464 stack_.push(current_); |
| 465 current_ = child; |
| 466 return this; |
| 467 } |
| 468 |
| 469 DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() { |
| 470 if (stack_.empty()) { |
| 471 // The root object ends here. Writes out the tree. |
| 472 WriteRoot(); |
| 473 return this; |
| 474 } |
| 475 current_ = stack_.top(); |
| 476 stack_.pop(); |
| 477 return this; |
| 478 } |
| 479 |
| 480 DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( |
| 481 StringPiece name) { |
| 482 if (current_ == NULL) { |
| 483 root_.reset( |
| 484 new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false)); |
| 485 current_ = root_.get(); |
| 486 return this; |
| 487 } |
| 488 MaybePopulateChildrenOfAny(current_); |
| 489 Node* child = current_->FindChild(name); |
| 490 if (child == NULL || child->kind() != LIST) { |
| 491 google::protobuf::scoped_ptr<Node> node( |
| 492 new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false)); |
| 493 child = node.get(); |
| 494 current_->AddChild(node.release()); |
| 495 } |
| 496 child->set_is_placeholder(false); |
| 497 |
| 498 stack_.push(current_); |
| 499 current_ = child; |
| 500 return this; |
| 501 } |
| 502 |
| 503 void DefaultValueObjectWriter::WriteRoot() { |
| 504 root_->WriteTo(ow_); |
| 505 root_.reset(NULL); |
| 506 current_ = NULL; |
| 507 } |
| 508 |
| 509 DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() { |
| 510 if (stack_.empty()) { |
| 511 WriteRoot(); |
| 512 return this; |
| 513 } |
| 514 current_ = stack_.top(); |
| 515 stack_.pop(); |
| 516 return this; |
| 517 } |
| 518 |
| 519 void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, |
| 520 const DataPiece& data) { |
| 521 MaybePopulateChildrenOfAny(current_); |
| 522 util::StatusOr<string> data_string = data.ToString(); |
| 523 if (current_->type() != NULL && current_->type()->name() == kAnyType && |
| 524 name == "@type" && data_string.ok()) { |
| 525 const string& string_value = data_string.ValueOrDie(); |
| 526 // If the type of current_ is "Any" and its "@type" field is being set here, |
| 527 // sets the type of current_ to be the type specified by the "@type". |
| 528 util::StatusOr<const google::protobuf::Type*> found_type = |
| 529 typeinfo_->ResolveTypeUrl(string_value); |
| 530 if (!found_type.ok()) { |
| 531 GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'."; |
| 532 } else { |
| 533 current_->set_type(found_type.ValueOrDie()); |
| 534 } |
| 535 current_->set_is_any(true); |
| 536 // If the "@type" field is placed after other fields, we should populate |
| 537 // other children of primitive type now. Otherwise, we should wait until the |
| 538 // first value field is rendered before we populate the children, because |
| 539 // the "value" field of a Any message could be omitted. |
| 540 if (current_->number_of_children() > 1 && current_->type() != NULL) { |
| 541 current_->PopulateChildren(typeinfo_); |
| 542 } |
| 543 } |
| 544 Node* child = current_->FindChild(name); |
| 545 if (child == NULL || child->kind() != PRIMITIVE) { |
| 546 // No children are found, creates a new child. |
| 547 google::protobuf::scoped_ptr<Node> node( |
| 548 new Node(name.ToString(), NULL, PRIMITIVE, data, false)); |
| 549 child = node.get(); |
| 550 current_->AddChild(node.release()); |
| 551 } else { |
| 552 child->set_data(data); |
| 553 } |
| 554 } |
| 555 |
| 556 } // namespace converter |
| 557 } // namespace util |
| 558 } // namespace protobuf |
| 559 } // namespace google |
| OLD | NEW |