diff options
Diffstat (limited to 'src/google/protobuf/util/internal/default_value_objectwriter.cc')
-rw-r--r-- | src/google/protobuf/util/internal/default_value_objectwriter.cc | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc new file mode 100644 index 00000000..267e2cd3 --- /dev/null +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -0,0 +1,515 @@ +// 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/stubs/map_util.h> + +namespace google { +namespace protobuf { +namespace util { +using util::Status; +using util::StatusOr; +namespace converter { + +DefaultValueObjectWriter::DefaultValueObjectWriter( + TypeResolver* type_resolver, const google::protobuf::Type& type, + ObjectWriter* ow) + : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), + type_(type), + disable_normalize_(false), + current_(NULL), + root_(NULL), + ow_(ow) {} + +DefaultValueObjectWriter::~DefaultValueObjectWriter() { + for (int i = 0; i < string_values_.size(); ++i) { + delete string_values_[i]; + } +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(StringPiece name, + bool value) { + if (current_ == NULL) { + ow_->RenderBool(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32( + StringPiece name, int32 value) { + if (current_ == NULL) { + ow_->RenderInt32(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32( + StringPiece name, uint32 value) { + if (current_ == NULL) { + ow_->RenderUint32(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64( + StringPiece name, int64 value) { + if (current_ == NULL) { + ow_->RenderInt64(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64( + StringPiece name, uint64 value) { + if (current_ == NULL) { + ow_->RenderUint64(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble( + StringPiece name, double value) { + if (current_ == NULL) { + ow_->RenderDouble(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat( + StringPiece name, float value) { + if (current_ == NULL) { + ow_->RenderBool(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString( + StringPiece name, StringPiece value) { + if (current_ == NULL) { + 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())); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes( + StringPiece name, StringPiece value) { + if (current_ == NULL) { + ow_->RenderBytes(name, value); + } else { + RenderDataPiece(name, DataPiece(value)); + } + return this; +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull( + StringPiece name) { + if (current_ == NULL) { + ow_->RenderNull(name); + } else { + RenderDataPiece(name, DataPiece::NullData()); + } + return this; +} + +DefaultValueObjectWriter* +DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() { + disable_normalize_ = true; + return this; +} + +DefaultValueObjectWriter::Node::Node(const string& name, + const google::protobuf::Type* type, + NodeKind kind, const DataPiece& data, + bool is_placeholder) + : name_(name), + type_(type), + kind_(kind), + disable_normalize_(false), + is_any_(false), + data_(data), + is_placeholder_(is_placeholder) {} + +DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( + StringPiece name) { + if (name.empty() || kind_ != OBJECT) { + return NULL; + } + for (int i = 0; i < children_.size(); ++i) { + Node* child = children_[i]; + if (child->name() == name) { + return child; + } + } + return NULL; +} + +void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) { + if (disable_normalize_) { + ow->DisableCaseNormalizationForNextKey(); + } + if (kind_ == PRIMITIVE) { + ObjectWriter::RenderDataPieceTo(data_, name_, ow); + return; + } + if (is_placeholder_) { + // If is_placeholder_ = true, we didn't see this node in the response, so + // skip output. + return; + } + if (kind_ == LIST) { + ow->StartList(name_); + } else { + ow->StartObject(name_); + } + for (int i = 0; i < children_.size(); ++i) { + Node* child = children_[i]; + child->WriteTo(ow); + } + if (kind_ == LIST) { + ow->EndList(); + } else { + ow->EndObject(); + } +} + +const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( + const google::protobuf::Type& found_type, 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 NULL; +} + +void DefaultValueObjectWriter::Node::PopulateChildren(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_ == NULL || 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); + 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] = NULL; + continue; + } + + const google::protobuf::Type* field_type = NULL; + 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 the child field is of primitive type, sets its data to the default + // value of its type. + // 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 value. + google::protobuf::scoped_ptr<Node> child( + new Node(field.name(), field_type, kind, + ((kind == PRIMITIVE && field.oneof_index() == 0) + ? CreateDefaultDataPieceForField(field) + : DataPiece::NullData()), + true)); + 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] == NULL) { + continue; + } + new_children.insert(new_children.begin(), children_[i]); + children_[i] = NULL; + } + 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 != NULL && node->is_any() && node->type() != NULL && + node->type()->name() != kAnyType && node->number_of_children() == 1) { + node->PopulateChildren(typeinfo_.get()); + } +} + +DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( + const google::protobuf::Field& field) { + switch (field.kind()) { + case google::protobuf::Field_Kind_TYPE_DOUBLE: { + return DataPiece(static_cast<double>(0)); + } + case google::protobuf::Field_Kind_TYPE_FLOAT: { + return DataPiece(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(static_cast<int64>(0)); + } + case google::protobuf::Field_Kind_TYPE_UINT64: + case google::protobuf::Field_Kind_TYPE_FIXED64: { + return DataPiece(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(static_cast<int32>(0)); + } + case google::protobuf::Field_Kind_TYPE_BOOL: { + return DataPiece(false); + } + case google::protobuf::Field_Kind_TYPE_STRING: { + return DataPiece(string()); + } + case google::protobuf::Field_Kind_TYPE_BYTES: { + return DataPiece("", false); + } + case google::protobuf::Field_Kind_TYPE_UINT32: + case google::protobuf::Field_Kind_TYPE_FIXED32: { + return DataPiece(static_cast<uint32>(0)); + } + default: { return DataPiece::NullData(); } + } +} + +DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( + StringPiece name) { + if (current_ == NULL) { + root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), + false)); + root_->set_disable_normalize(GetAndResetDisableNormalize()); + root_->PopulateChildren(typeinfo_.get()); + current_ = root_.get(); + return this; + } + MaybePopulateChildrenOfAny(current_); + Node* child = current_->FindChild(name); + if (current_->kind() == LIST || current_->kind() == MAP || child == NULL) { + // 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. + google::protobuf::scoped_ptr<Node> node(new Node( + name.ToString(), ((current_->kind() == LIST || current_->kind() == MAP) + ? current_->type() + : NULL), + OBJECT, DataPiece::NullData(), false)); + child = node.get(); + current_->AddChild(node.release()); + } + + child->set_is_placeholder(false); + child->set_disable_normalize(GetAndResetDisableNormalize()); + if (child->kind() == OBJECT && child->number_of_children() == 0) { + child->PopulateChildren(typeinfo_.get()); + } + + 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_ == NULL) { + root_.reset( + new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false)); + root_->set_disable_normalize(GetAndResetDisableNormalize()); + current_ = root_.get(); + return this; + } + MaybePopulateChildrenOfAny(current_); + Node* child = current_->FindChild(name); + if (child == NULL || child->kind() != LIST) { + GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'."; + google::protobuf::scoped_ptr<Node> node( + new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false)); + child = node.get(); + current_->AddChild(node.release()); + } + child->set_is_placeholder(false); + child->set_disable_normalize(GetAndResetDisableNormalize()); + + stack_.push(current_); + current_ = child; + return this; +} + +void DefaultValueObjectWriter::WriteRoot() { + root_->WriteTo(ow_); + root_.reset(NULL); + current_ = NULL; +} + +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_); + util::StatusOr<string> data_string = data.ToString(); + if (current_->type() != NULL && current_->type()->name() == kAnyType && + name == "@type" && 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() != NULL) { + current_->PopulateChildren(typeinfo_.get()); + } + } + Node* child = current_->FindChild(name); + if (child == NULL || child->kind() != PRIMITIVE) { + GOOGLE_LOG(WARNING) << "Cannot find primitive field '" << name << "'."; + // No children are found, creates a new child. + google::protobuf::scoped_ptr<Node> node( + new Node(name.ToString(), NULL, PRIMITIVE, data, false)); + child = node.get(); + current_->AddChild(node.release()); + } else { + child->set_data(data); + } + child->set_disable_normalize(GetAndResetDisableNormalize()); +} + +} // namespace converter +} // namespace util +} // namespace protobuf +} // namespace google |