diff options
author | Jisi Liu <jisi.liu@gmail.com> | 2015-08-25 16:49:45 -0700 |
---|---|---|
committer | Jisi Liu <jisi.liu@gmail.com> | 2015-08-25 16:51:22 -0700 |
commit | db45aa117a7e7bf90883f98638ef9d0abdbef317 (patch) | |
tree | 621aec6a548092efba3fa9e1eb9410be75b21640 /src/google/protobuf/util/internal | |
parent | 56c4f57bb0d3c76476947a99d6584df62b6d0805 (diff) | |
parent | f4ef8fe3b8e25ff5204bec46f7ad86b933f4ce32 (diff) |
Merge branch 'beta-1' of github.com:google/protobuf into manual-merge
Change-Id: I83a93fdb119a643fbc884e6ec3624493f6270370
Diffstat (limited to 'src/google/protobuf/util/internal')
26 files changed, 697 insertions, 627 deletions
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index 944fb2e3..ea360798 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -79,7 +79,9 @@ StatusOr<To> NumberConvertAndCheck(From before) { // For conversion between double and float only. template <typename To, typename From> StatusOr<To> FloatingPointConvertAndCheck(From before) { - if (MathLimits<From>::IsNaN(before)) return std::numeric_limits<To>::quiet_NaN(); + if (MathLimits<From>::IsNaN(before)) { + return std::numeric_limits<To>::quiet_NaN(); + } To after = static_cast<To>(before); if (MathUtil::AlmostEquals<To>(after, before)) { @@ -167,7 +169,7 @@ StatusOr<string> DataPiece::ToString() const { return str_.ToString(); case TYPE_BYTES: { string base64; - WebSafeBase64Escape(str_, &base64); + Base64Escape(str_, &base64); return base64; } default: diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index 267e2cd3..97b248ff 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -46,6 +46,7 @@ DefaultValueObjectWriter::DefaultValueObjectWriter( TypeResolver* type_resolver, const google::protobuf::Type& type, ObjectWriter* ow) : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), + own_typeinfo_(true), type_(type), disable_normalize_(false), current_(NULL), @@ -56,6 +57,9 @@ 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, @@ -197,33 +201,47 @@ 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. + + // 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) { ow->StartList(name_); - } else { - ow->StartObject(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); } - if (kind_ == LIST) { - ow->EndList(); - } else { - ow->EndObject(); - } } const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( - const google::protobuf::Type& found_type, TypeInfo* typeinfo) { + 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) { @@ -248,7 +266,8 @@ const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( return NULL; } -void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) { +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. @@ -310,15 +329,17 @@ void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) { 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. + if (field.oneof_index() != 0) continue; + + // If the child field is of primitive type, sets its data to the default + // value of its type. google::protobuf::scoped_ptr<Node> child( - new Node(field.name(), field_type, kind, - ((kind == PRIMITIVE && field.oneof_index() == 0) - ? CreateDefaultDataPieceForField(field) - : DataPiece::NullData()), + new Node(field.json_name(), field_type, kind, + kind == PRIMITIVE ? CreateDefaultDataPieceForField(field) + : DataPiece::NullData(), true)); new_children.push_back(child.release()); } @@ -338,7 +359,7 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { // 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()); + node->PopulateChildren(typeinfo_); } } @@ -388,7 +409,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), false)); root_->set_disable_normalize(GetAndResetDisableNormalize()); - root_->PopulateChildren(typeinfo_.get()); + root_->PopulateChildren(typeinfo_); current_ = root_.get(); return this; } @@ -409,7 +430,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( child->set_is_placeholder(false); child->set_disable_normalize(GetAndResetDisableNormalize()); if (child->kind() == OBJECT && child->number_of_children() == 0) { - child->PopulateChildren(typeinfo_.get()); + child->PopulateChildren(typeinfo_); } stack_.push(current_); @@ -492,12 +513,11 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, // 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()); + current_->PopulateChildren(typeinfo_); } } 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)); diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h index 759ba91b..2468c8d9 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.h +++ b/src/google/protobuf/util/internal/default_value_objectwriter.h @@ -57,7 +57,7 @@ namespace converter { // ObjectWriter when EndObject() is called on the root object. It also writes // out all non-repeated primitive fields that haven't been explicitly rendered // with their default values (0 for numbers, "" for strings, etc). -class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { +class DefaultValueObjectWriter : public ObjectWriter { public: DefaultValueObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, @@ -129,7 +129,7 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Populates children of this Node based on its type. If there are already // children created, they will be merged to the result. Caller should pass // in TypeInfo for looking up types of the children. - void PopulateChildren(TypeInfo* typeinfo); + void PopulateChildren(const TypeInfo* typeinfo); // If this node is a leaf (has data), writes the current node to the // ObjectWriter; if not, then recursively writes the children to the @@ -165,7 +165,10 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Returns the Value Type of a map given the Type of the map entry and a // TypeInfo instance. const google::protobuf::Type* GetMapValueType( - const google::protobuf::Type& entry_type, TypeInfo* typeinfo); + const google::protobuf::Type& entry_type, const TypeInfo* typeinfo); + + // Calls WriteTo() on every child in children_. + void WriteChildren(ObjectWriter* ow); // The name of this node. string name_; @@ -210,7 +213,9 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. - google::protobuf::scoped_ptr<TypeInfo> typeinfo_; + const TypeInfo* typeinfo_; + // Whether the TypeInfo object is owned by this class. + bool own_typeinfo_; // google::protobuf::Type of the root message type. const google::protobuf::Type& type_; // Holds copies of strings passed to RenderString. diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc index 593c7105..237d0722 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc @@ -73,15 +73,15 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, TEST_P(DefaultValueObjectWriterTest, Empty) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 0.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") - ->RenderBytes("bytes_value", "") + ->RenderDouble("doubleValue", 0.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") + ->RenderBytes("bytesValue", "") ->EndObject(); // Actual testing @@ -91,42 +91,42 @@ TEST_P(DefaultValueObjectWriterTest, Empty) { TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 1.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") + ->RenderDouble("doubleValue", 1.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") ->EndObject(); // Actual testing - testing_->StartObject("")->RenderDouble("double_value", 1.0)->EndObject(); + testing_->StartObject("")->RenderDouble("doubleValue", 1.0)->EndObject(); } TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 1.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") + ->RenderDouble("doubleValue", 1.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") ->RenderString("unknown", "abc") - ->StartObject("unknown_object") + ->StartObject("unknownObject") ->RenderString("unknown", "def") ->EndObject() ->EndObject(); // Actual testing testing_->StartObject("") - ->RenderDouble("double_value", 1.0) + ->RenderDouble("doubleValue", 1.0) ->RenderString("unknown", "abc") - ->StartObject("unknown_object") + ->StartObject("unknownObject") ->RenderString("unknown", "def") ->EndObject() ->EndObject(); diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h index 9b907df5..2699684d 100644 --- a/src/google/protobuf/util/internal/error_listener.h +++ b/src/google/protobuf/util/internal/error_listener.h @@ -37,7 +37,9 @@ #endif #include <string> +#include <google/protobuf/stubs/callback.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/util/internal/location_tracker.h> #include <google/protobuf/stubs/stringpiece.h> diff --git a/src/google/protobuf/util/internal/field_mask_utility.cc b/src/google/protobuf/util/internal/field_mask_utility.cc index 92468959..f0e8fc88 100644 --- a/src/google/protobuf/util/internal/field_mask_utility.cc +++ b/src/google/protobuf/util/internal/field_mask_utility.cc @@ -34,7 +34,6 @@ #include <google/protobuf/stubs/status_macros.h> namespace google { - namespace protobuf { namespace util { namespace converter { @@ -138,7 +137,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, } // Un-escaped '"' must be followed with a ']'. if (i >= length - 1 || paths[i + 1] != ']') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be represented as [\"some_key\"].")); @@ -150,7 +149,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // Checks whether the key ends at the end of a path segment. if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' && paths[i + 1] != ')' && paths[i + 1] != '(') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be at the end of a path segment.")); @@ -162,7 +161,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // We are not in a map key, look for the start of one. if (paths[i] == '[') { if (i >= length - 1 || paths[i + 1] != '\"') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be represented as [\"some_key\"].")); @@ -198,7 +197,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // Removes the last prefix after seeing a ')'. if (i < length && paths[i] == ')') { if (prefix.empty()) { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Cannot find matching '(' for all ')'.")); @@ -208,16 +207,14 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, previous_position = i + 1; } if (in_map_key) { - return CreatePublicError( - util::error::INVALID_ARGUMENT, - StrCat("Invalid FieldMask '", paths, - "'. Cannot find matching ']' for all '['.")); + return util::Status(util::error::INVALID_ARGUMENT, + StrCat("Invalid FieldMask '", paths, + "'. Cannot find matching ']' for all '['.")); } if (!prefix.empty()) { - return CreatePublicError( - util::error::INVALID_ARGUMENT, - StrCat("Invalid FieldMask '", paths, - "'. Cannot find matching ')' for all '('.")); + return util::Status(util::error::INVALID_ARGUMENT, + StrCat("Invalid FieldMask '", paths, + "'. Cannot find matching ')' for all '('.")); } return util::Status::OK; } diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc index 5ac23421..36dc8ef9 100644 --- a/src/google/protobuf/util/internal/json_escaping.cc +++ b/src/google/protobuf/util/internal/json_escaping.cc @@ -30,6 +30,7 @@ #include <google/protobuf/util/internal/json_escaping.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> namespace google { diff --git a/src/google/protobuf/util/internal/json_objectwriter.cc b/src/google/protobuf/util/internal/json_objectwriter.cc index 0c41515f..d88a81f9 100644 --- a/src/google/protobuf/util/internal/json_objectwriter.cc +++ b/src/google/protobuf/util/internal/json_objectwriter.cc @@ -33,6 +33,7 @@ #include <math.h> #include <google/protobuf/stubs/casts.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/util/internal/utility.h> #include <google/protobuf/util/internal/json_escaping.h> @@ -142,7 +143,7 @@ JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name, StringPiece value) { WritePrefix(name); string base64; - WebSafeBase64EscapeWithPadding(value, &base64); + Base64Escape(value, &base64); WriteChar('"'); // TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes // directly to the stream, rather than first putting them diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc index df9a133e..dcd60601 100644 --- a/src/google/protobuf/util/internal/json_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc @@ -47,7 +47,8 @@ class JsonObjectWriterTest : public ::testing::Test { JsonObjectWriterTest() : str_stream_(new StringOutputStream(&output_)), out_stream_(new CodedOutputStream(str_stream_)), - ow_(NULL) {} + ow_(NULL) { + } virtual ~JsonObjectWriterTest() { delete ow_; @@ -63,34 +64,36 @@ class JsonObjectWriterTest : public ::testing::Test { TEST_F(JsonObjectWriterTest, EmptyRootObject) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("")->EndObject(); + ow_->StartObject("") + ->EndObject(); EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartObject("empty") - ->EndObject() - ->EndObject(); + ->RenderString("test", "value") + ->StartObject("empty") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyRootList) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartList("")->EndList(); + ow_->StartList("") + ->EndList(); EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartList("empty") - ->EndList() - ->EndObject(); + ->RenderString("test", "value") + ->StartList("empty") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}", output_.substr(0, out_stream_->ByteCount())); } @@ -98,10 +101,10 @@ TEST_F(JsonObjectWriterTest, EmptyList) { TEST_F(JsonObjectWriterTest, ObjectInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartObject("nested") - ->RenderString("field", "value") - ->EndObject() - ->EndObject(); + ->StartObject("nested") + ->RenderString("field", "value") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}", output_.substr(0, out_stream_->ByteCount())); } @@ -109,10 +112,10 @@ TEST_F(JsonObjectWriterTest, ObjectInObject) { TEST_F(JsonObjectWriterTest, ListInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartList("nested") - ->RenderString("", "value") - ->EndList() - ->EndObject(); + ->StartList("nested") + ->RenderString("", "value") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"nested\":[\"value\"]}", output_.substr(0, out_stream_->ByteCount())); } @@ -120,10 +123,10 @@ TEST_F(JsonObjectWriterTest, ListInObject) { TEST_F(JsonObjectWriterTest, ObjectInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartObject("") - ->RenderString("field", "value") - ->EndObject() - ->EndList(); + ->StartObject("") + ->RenderString("field", "value") + ->EndObject() + ->EndList(); EXPECT_EQ("[{\"field\":\"value\"}]", output_.substr(0, out_stream_->ByteCount())); } @@ -131,10 +134,10 @@ TEST_F(JsonObjectWriterTest, ObjectInList) { TEST_F(JsonObjectWriterTest, ListInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartList("") - ->RenderString("", "value") - ->EndList() - ->EndList(); + ->StartList("") + ->RenderString("", "value") + ->EndList() + ->EndList(); EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount())); } @@ -164,97 +167,95 @@ TEST_F(JsonObjectWriterTest, RenderPrimitives) { output_.substr(0, out_stream_->ByteCount())); } -TEST_F(JsonObjectWriterTest, BytesEncodesAsWebSafeBase64) { +TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) { string s; s.push_back('\377'); s.push_back('\357'); ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("")->RenderBytes("bytes", s)->EndObject(); // Non-web-safe would encode this as "/+8=" - EXPECT_EQ("{\"bytes\":\"_-8=\"}", + EXPECT_EQ("{\"bytes\":\"/+8=\"}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("items") - ->RenderString("", "item1") - ->RenderString("", "item2") - ->RenderString("", "item3") - ->EndList() - ->StartList("empty") - ->EndList() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"items\": [\n" - " \"item1\",\n" - " \"item2\",\n" - " \"item3\"\n" - " ],\n" - " \"empty\": []\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("items") + ->RenderString("", "item1") + ->RenderString("", "item2") + ->RenderString("", "item3") + ->EndList() + ->StartList("empty") + ->EndList() + ->EndObject(); + EXPECT_EQ("{\n" + " \"items\": [\n" + " \"item1\",\n" + " \"item2\",\n" + " \"item3\"\n" + " ],\n" + " \"empty\": []\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintObject) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartObject("items") - ->RenderString("key1", "item1") - ->RenderString("key2", "item2") - ->RenderString("key3", "item3") - ->EndObject() - ->StartObject("empty") - ->EndObject() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"items\": {\n" - " \"key1\": \"item1\",\n" - " \"key2\": \"item2\",\n" - " \"key3\": \"item3\"\n" - " },\n" - " \"empty\": {}\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartObject("items") + ->RenderString("key1", "item1") + ->RenderString("key2", "item2") + ->RenderString("key3", "item3") + ->EndObject() + ->StartObject("empty") + ->EndObject() + ->EndObject(); + EXPECT_EQ("{\n" + " \"items\": {\n" + " \"key1\": \"item1\",\n" + " \"key2\": \"item2\",\n" + " \"key3\": \"item3\"\n" + " },\n" + " \"empty\": {}\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("list") - ->StartObject("") - ->EndObject() - ->EndList() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"list\": [\n" - " {}\n" - " ]\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("list") + ->StartObject("") + ->EndObject() + ->EndList() + ->EndObject(); + EXPECT_EQ("{\n" + " \"list\": [\n" + " {}\n" + " ]\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->RenderBool("bool", true) - ->RenderInt32("int", 42) - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"bool\": true,\n" - " \"int\": 42\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->RenderBool("bool", true) + ->RenderInt32("int", 42) + ->EndObject(); + EXPECT_EQ("{\n" + " \"bool\": true,\n" + " \"int\": 42\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("")->RenderString("string", "'<>&\\\"\r\n")->EndObject(); + ow_->StartObject("") + ->RenderString("string", "'<>&\\\"\r\n") + ->EndObject(); EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&\\\\\\\"\\r\\n\"}", output_.substr(0, out_stream_->ByteCount())); } @@ -262,13 +263,13 @@ TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { TEST_F(JsonObjectWriterTest, Stringification) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN()) - ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN()) - ->RenderDouble("double_pos", std::numeric_limits<double>::infinity()) - ->RenderFloat("float_pos", std::numeric_limits<float>::infinity()) - ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity()) - ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity()) - ->EndObject(); + ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN()) + ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN()) + ->RenderDouble("double_pos", std::numeric_limits<double>::infinity()) + ->RenderFloat("float_pos", std::numeric_limits<float>::infinity()) + ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity()) + ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity()) + ->EndObject(); EXPECT_EQ( "{\"double_nan\":\"NaN\"," "\"float_nan\":\"NaN\"," diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc index d439a221..a7ef7fe2 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.cc +++ b/src/google/protobuf/util/internal/json_stream_parser.cc @@ -40,6 +40,7 @@ #include <google/protobuf/stubs/shared_ptr.h> #endif +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/util/internal/object_writer.h> @@ -104,16 +105,42 @@ JsonStreamParser::JsonStreamParser(ObjectWriter* ow) parsed_(), parsed_storage_(), string_open_(0), - utf8_storage_(), - utf8_length_(0) { + chunk_storage_(), + coerce_to_utf8_(false) { // Initialize the stack with a single value to be parsed. stack_.push(VALUE); } JsonStreamParser::~JsonStreamParser() {} + util::Status JsonStreamParser::Parse(StringPiece json) { - return ParseChunk(json); + StringPiece chunk = json; + // If we have leftovers from a previous chunk, append the new chunk to it + // and create a new StringPiece pointing at the string's data. This could + // be large but we rely on the chunks to be small, assuming they are + // fragments of a Cord. + if (!leftover_.empty()) { + // Don't point chunk to leftover_ because leftover_ will be updated in + // ParseChunk(chunk). + chunk_storage_.swap(leftover_); + json.AppendToString(&chunk_storage_); + chunk = StringPiece(chunk_storage_); + } + + // Find the structurally valid UTF8 prefix and parse only that. + int n = internal::UTF8SpnStructurallyValid(chunk); + if (n > 0) { + util::Status status = ParseChunk(chunk.substr(0, n)); + + // Any leftover characters are stashed in leftover_ for later parsing when + // there is more data available. + chunk.substr(n).AppendToString(&leftover_); + return status; + } else { + chunk.CopyToString(&leftover_); + return util::Status::OK; + } } util::Status JsonStreamParser::FinishParse() { @@ -122,9 +149,22 @@ util::Status JsonStreamParser::FinishParse() { if (stack_.empty() && leftover_.empty()) { return util::Status::OK; } + + // Storage for UTF8-coerced string. + google::protobuf::scoped_array<char> utf8; + if (coerce_to_utf8_) { + utf8.reset(new char[leftover_.size()]); + char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' '); + p_ = json_ = StringPiece(coerced, leftover_.size()); + } else { + if (!internal::IsStructurallyValidUTF8(leftover_)) { + return ReportFailure("Encountered non UTF-8 code points."); + } + p_ = json_ = leftover_; + } + // Parse the remainder in finishing mode, which reports errors for things like // unterminated strings or unknown tokens that would normally be retried. - p_ = json_ = StringPiece(leftover_); finishing_ = true; util::Status result = RunParser(); if (result.ok()) { @@ -137,16 +177,10 @@ util::Status JsonStreamParser::FinishParse() { } util::Status JsonStreamParser::ParseChunk(StringPiece chunk) { - // If we have leftovers from a previous chunk, append the new chunk to it and - // create a new StringPiece pointing at the string's data. This could be - // large but we rely on the chunks to be small, assuming they are fragments - // of a Cord. - if (!leftover_.empty()) { - chunk.AppendToString(&leftover_); - p_ = json_ = StringPiece(leftover_); - } else { - p_ = json_ = chunk; - } + // Do not do any work if the chunk is empty. + if (chunk.empty()) return util::Status::OK; + + p_ = json_ = chunk; finishing_ = false; util::Status result = RunParser(); diff --git a/src/google/protobuf/util/internal/json_stream_parser.h b/src/google/protobuf/util/internal/json_stream_parser.h index 17b094ae..0278c28f 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.h +++ b/src/google/protobuf/util/internal/json_stream_parser.h @@ -75,12 +75,14 @@ class LIBPROTOBUF_EXPORT JsonStreamParser { explicit JsonStreamParser(ObjectWriter* ow); virtual ~JsonStreamParser(); - // Parse a JSON string (UTF-8 encoded). + // Parses a UTF-8 encoded JSON string from a StringPiece. util::Status Parse(StringPiece json); + // Finish parsing the JSON string. util::Status FinishParse(); + private: enum TokenType { BEGIN_STRING, // " or ' @@ -239,11 +241,11 @@ class LIBPROTOBUF_EXPORT JsonStreamParser { // A value of 0 indicates that string parsing is not in process. char string_open_; - // Storage for utf8-coerced bytes. - google::protobuf::scoped_array<char> utf8_storage_; + // Storage for the chunk that are being parsed in ParseChunk(). + string chunk_storage_; - // Length of the storage for utf8-coerced bytes. - int utf8_length_; + // Whether to allow non UTF-8 encoded input and replace invalid code points. + bool coerce_to_utf8_; GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonStreamParser); }; diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc index b0775a2f..c833ed1f 100644 --- a/src/google/protobuf/util/internal/json_stream_parser_test.cc +++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc @@ -30,6 +30,7 @@ #include <google/protobuf/util/internal/json_stream_parser.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/time.h> #include <google/protobuf/util/internal/expecting_objectwriter.h> @@ -85,7 +86,7 @@ class JsonStreamParserTest : public ::testing::Test { JsonStreamParserTest() : mock_(), ow_(&mock_) {} virtual ~JsonStreamParserTest() {} - util::Status RunTest(StringPiece json, int split) { + util::Status RunTest(StringPiece json, int split, bool coerce_utf8 = false) { JsonStreamParser parser(&mock_); // Special case for split == length, test parsing one character at a time. @@ -115,8 +116,8 @@ class JsonStreamParserTest : public ::testing::Test { return result; } - void DoTest(StringPiece json, int split) { - util::Status result = RunTest(json, split); + void DoTest(StringPiece json, int split, bool coerce_utf8 = false) { + util::Status result = RunTest(json, split, coerce_utf8); if (!result.ok()) { GOOGLE_LOG(WARNING) << result; } @@ -337,14 +338,26 @@ TEST_F(JsonStreamParserTest, ObjectValues) { } } + +TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) { + StringPiece json = "{\"address\":\xFF\"חרושת 23, רעננה, ישראל\"}"; + for (int i = 0; i <= json.length(); ++i) { + DoErrorTest(json, i, "Encountered non UTF-8 code points."); + } + json = "{\"address\": \"חרושת 23,\xFFרעננה, ישראל\"}"; + for (int i = 0; i <= json.length(); ++i) { + DoErrorTest(json, i, "Encountered non UTF-8 code points."); + } +} + #ifndef _MSC_VER // - unicode handling in strings TEST_F(JsonStreamParserTest, UnicodeEscaping) { StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]"; for (int i = 0; i <= str.length(); ++i) { // TODO(xiaofeng): Figure out what default encoding to use for JSON strings. - // In protobuf we use UTF-8 for strings, but for JSON we probably should allow - // different encodings? + // In protobuf we use UTF-8 for strings, but for JSON we probably should + // allow different encodings? ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList(); DoTest(str, i); } diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 53a0e47a..18bb2772 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -33,6 +33,7 @@ #include <utility> #include <google/protobuf/stubs/casts.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/stringprintf.h> #include <google/protobuf/stubs/time.h> @@ -96,7 +97,7 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( } ProtoStreamObjectSource::ProtoStreamObjectSource( - google::protobuf::io::CodedInputStream* stream, TypeInfo* typeinfo, + google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo, const google::protobuf::Type& type) : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; @@ -156,7 +157,7 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, last_tag = tag; field = FindAndVerifyField(type, tag); if (field != NULL) { - field_name = field->name(); + field_name = field->json_name(); } } if (field == NULL) { @@ -214,7 +215,7 @@ StatusOr<uint32> ProtoStreamObjectSource::RenderMap( const google::protobuf::Field* field, StringPiece name, uint32 list_tag, ObjectWriter* ow) const { const google::protobuf::Type* field_type = - typeinfo_->GetType(field->type_url()); + typeinfo_->GetTypeByTypeUrl(field->type_url()); uint32 tag_to_return = 0; if (IsPackable(*field) && list_tag == @@ -784,7 +785,8 @@ Status ProtoStreamObjectSource::RenderField( // Get the nested enum type for this field. // TODO(skarvaje): Avoid string manipulation. Find ways to speed this // up. - const google::protobuf::Enum* en = typeinfo_->GetEnum(field->type_url()); + const google::protobuf::Enum* en = + typeinfo_->GetEnumByTypeUrl(field->type_url()); // Lookup the name of the enum, and render that. Skips unknown enums. if (en != NULL) { const google::protobuf::EnumValue* enum_value = @@ -819,7 +821,7 @@ Status ProtoStreamObjectSource::RenderField( int old_limit = stream_->PushLimit(buffer32); // Get the nested message type for this field. const google::protobuf::Type* type = - typeinfo_->GetType(field->type_url()); + typeinfo_->GetTypeByTypeUrl(field->type_url()); if (type == NULL) { return Status(util::error::INTERNAL, StrCat("Invalid configuration. Could not find the type: ", @@ -928,7 +930,8 @@ const string ProtoStreamObjectSource::ReadFieldValueAsString( // Get the nested enum type for this field. // TODO(skarvaje): Avoid string manipulation. Find ways to speed this // up. - const google::protobuf::Enum* en = typeinfo_->GetEnum(field.type_url()); + const google::protobuf::Enum* en = + typeinfo_->GetEnumByTypeUrl(field.type_url()); // Lookup the name of the enum, and render that. Skips unknown enums. if (en != NULL) { const google::protobuf::EnumValue* enum_value = @@ -962,7 +965,7 @@ const string ProtoStreamObjectSource::ReadFieldValueAsString( bool ProtoStreamObjectSource::IsMap( const google::protobuf::Field& field) const { const google::protobuf::Type* field_type = - typeinfo_->GetType(field.type_url()); + typeinfo_->GetTypeByTypeUrl(field.type_url()); // TODO(xiaofeng): Unify option names. return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE && diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index 4a4e6bbf..845437f5 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -46,7 +46,6 @@ #include <google/protobuf/stubs/statusor.h> - namespace google { namespace protobuf { class Field; @@ -61,7 +60,10 @@ namespace converter { class TypeInfo; // An ObjectSource that can parse a stream of bytes as a protocol buffer. -// This implementation uses a tech Type for tag lookup. +// Its WriteTo() method can be given an ObjectWriter. +// This implementation uses a google.protobuf.Type for tag and name lookup. +// The field names are converted into lower camel-case when writing to the +// ObjectWriter. // // Sample usage: (suppose input is: string proto) // ArrayInputStream arr_stream(proto.data(), proto.size()); @@ -93,7 +95,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { private: ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream, - TypeInfo* typeinfo, + const TypeInfo* typeinfo, const google::protobuf::Type& type); // Function that renders a well known type with a modified behavior. typedef util::Status (*TypeRenderer)(const ProtoStreamObjectSource*, @@ -226,7 +228,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Whether this class owns the typeinfo_ object. If true the typeinfo_ object // should be deleted in the destructor. bool own_typeinfo_; diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc index 4cc62410..f6e5ee7a 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc @@ -117,61 +117,61 @@ class ProtostreamObjectSourceTest void PrepareExpectingObjectWriterForRepeatedPrimitive() { ow_.StartObject("") - ->StartList("rep_fix32") + ->StartList("repFix32") ->RenderUint32("", bit_cast<uint32>(3201)) ->RenderUint32("", bit_cast<uint32>(0)) ->RenderUint32("", bit_cast<uint32>(3202)) ->EndList() - ->StartList("rep_u32") + ->StartList("repU32") ->RenderUint32("", bit_cast<uint32>(3203)) ->RenderUint32("", bit_cast<uint32>(0)) ->EndList() - ->StartList("rep_i32") + ->StartList("repI32") ->RenderInt32("", 0) ->RenderInt32("", 3204) ->RenderInt32("", 3205) ->EndList() - ->StartList("rep_sf32") + ->StartList("repSf32") ->RenderInt32("", 3206) ->RenderInt32("", 0) ->EndList() - ->StartList("rep_s32") + ->StartList("repS32") ->RenderInt32("", 0) ->RenderInt32("", 3207) ->RenderInt32("", 3208) ->EndList() - ->StartList("rep_fix64") + ->StartList("repFix64") ->RenderUint64("", bit_cast<uint64>(6401LL)) ->RenderUint64("", bit_cast<uint64>(0LL)) ->EndList() - ->StartList("rep_u64") + ->StartList("repU64") ->RenderUint64("", bit_cast<uint64>(0LL)) ->RenderUint64("", bit_cast<uint64>(6402LL)) ->RenderUint64("", bit_cast<uint64>(6403LL)) ->EndList() - ->StartList("rep_i64") + ->StartList("repI64") ->RenderInt64("", 6404L) ->RenderInt64("", 0L) ->EndList() - ->StartList("rep_sf64") + ->StartList("repSf64") ->RenderInt64("", 0L) ->RenderInt64("", 6405L) ->RenderInt64("", 6406L) ->EndList() - ->StartList("rep_s64") + ->StartList("repS64") ->RenderInt64("", 6407L) ->RenderInt64("", 0L) ->EndList() - ->StartList("rep_float") + ->StartList("repFloat") ->RenderFloat("", 0.0f) ->RenderFloat("", 32.1f) ->RenderFloat("", 32.2f) ->EndList() - ->StartList("rep_double") + ->StartList("repDouble") ->RenderDouble("", 64.1L) ->RenderDouble("", 0.0L) ->EndList() - ->StartList("rep_bool") + ->StartList("repBool") ->RenderBool("", true) ->RenderBool("", false) ->EndList() @@ -317,11 +317,11 @@ TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) { primitive.add_rep_str("String Two"); primitive.add_rep_bytes("Some Bytes"); - ow_.StartList("rep_str") + ow_.StartList("repStr") ->RenderString("", "String One") ->RenderString("", "String Two") ->EndList() - ->StartList("rep_bytes") + ->StartList("repBytes") ->RenderBytes("", "Some Bytes") ->EndList(); DoTest(primitive, Primitive::descriptor()); @@ -794,16 +794,16 @@ TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) { ow_.StartObject("") ->RenderString("id", "1") - ->RenderString("single_mask", "path1,snakeCasePath2") - ->StartList("repeated_mask") + ->RenderString("singleMask", "path1,snakeCasePath2") + ->StartList("repeatedMask") ->RenderString("", "path3") ->RenderString("", "snakeCasePath4,path5") ->EndList() - ->StartList("nested_mask") + ->StartList("nestedMask") ->StartObject("") ->RenderString("data", "data") - ->RenderString("single_mask", "nested.path1,nestedField.snakeCasePath2") - ->StartList("repeated_mask") + ->RenderString("singleMask", "nested.path1,nestedField.snakeCasePath2") + ->StartList("repeatedMask") ->RenderString("", "nestedField.path3,nested.snakeCasePath4") ->RenderString("", "nested.path5") ->RenderString("", diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index f9ddbf32..87f504e0 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -74,7 +74,7 @@ ProtoStreamObjectWriter::ProtoStreamObjectWriter( tracker_(new ObjectLocationTracker()) {} ProtoStreamObjectWriter::ProtoStreamObjectWriter( - TypeInfo* typeinfo, const google::protobuf::Type& type, + const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) : master_type_(type), typeinfo_(typeinfo), @@ -91,14 +91,19 @@ ProtoStreamObjectWriter::ProtoStreamObjectWriter( tracker_(new ObjectLocationTracker()) {} ProtoStreamObjectWriter::~ProtoStreamObjectWriter() { - // Cleanup explicitly in order to avoid destructor stack overflow when input - // is deeply nested. - while (element_ != NULL) { - element_.reset(element_->pop()); - } if (own_typeinfo_) { delete typeinfo_; } + if (element_ == NULL) return; + // Cleanup explicitly in order to avoid destructor stack overflow when input + // is deeply nested. + // Cast to BaseElement to avoid doing additional checks (like missing fields) + // during pop(). + google::protobuf::scoped_ptr<BaseElement> element( + static_cast<BaseElement*>(element_.get())->pop<BaseElement>()); + while (element != NULL) { + element.reset(element->pop<BaseElement>()); + } } namespace { @@ -454,7 +459,7 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() { } ProtoStreamObjectWriter::ProtoElement::ProtoElement( - TypeInfo* typeinfo, const google::protobuf::Type& type, + const TypeInfo* typeinfo, const google::protobuf::Type& type, ProtoStreamObjectWriter* enclosing) : BaseElement(NULL), ow_(enclosing), @@ -586,6 +591,14 @@ string ProtoStreamObjectWriter::ProtoElement::ToString() const { return loc.empty() ? "." : loc; } +bool ProtoStreamObjectWriter::ProtoElement::OneofIndexTaken(int32 index) { + return ContainsKey(oneof_indices_, index); +} + +void ProtoStreamObjectWriter::ProtoElement::TakeOneofIndex(int32 index) { + InsertIfNotPresent(&oneof_indices_, index); +} + inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name, StringPiece message) { listener_->InvalidName(location(), ToSnakeCase(unknown_name), message); @@ -655,6 +668,13 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( return this; } + // Check to see if this field is a oneof and that no oneof in that group has + // already been set. + if (!ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + if (field->type_url() == GetFullTypeWithUrl(kStructType)) { // Start a struct object. StartStruct(field); @@ -932,6 +952,14 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { // Also we ignore if the field is not found here as it is caught later. field = typeinfo_->FindField(&element_->type(), name); + // Only check for oneof collisions on the first StartList call. We identify + // the first call with !name.empty() check. Subsequent list element calls + // will not have the name filled. + if (!name.empty() && field && !ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + // It is an error to try to bind to map, which behind the scenes is a list. if (field && IsMap(*field)) { // Push field to stack for error location tracking & reporting. @@ -1080,9 +1108,9 @@ Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow, data.ValueAsStringOrDefault(""))); } - // TODO(tsun): figure out how to do proto descriptor based snake case - // conversions as much as possible. Because ToSnakeCase sometimes returns the - // wrong value. +// TODO(tsun): figure out how to do proto descriptor based snake case +// conversions as much as possible. Because ToSnakeCase sometimes returns the +// wrong value. google::protobuf::scoped_ptr<ResultCallback1<util::Status, StringPiece> > callback( NewPermanentCallback(&RenderOneFieldPath, ow)); return DecodeCompactFieldMaskPaths(data.str(), callback.get()); @@ -1154,6 +1182,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( const google::protobuf::Field* field = NULL; string type_url; bool is_map_entry = false; + // We are at the root when element_ == NULL. if (element_ == NULL) { type_url = GetFullTypeWithUrl(master_type_.name()); } else { @@ -1166,6 +1195,11 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( if (field == NULL) { return this; } + + // Check to see if this field is a oneof and that no oneof in that group has + // already been set. + if (!ValidOneof(*field, name)) return this; + type_url = field->type_url(); } @@ -1314,7 +1348,8 @@ void ProtoStreamObjectWriter::RenderSimpleDataPiece( } case google::protobuf::Field_Kind_TYPE_ENUM: { status = WriteEnum(field.number(), data, - typeinfo_->GetEnum(field.type_url()), stream_.get()); + typeinfo_->GetEnumByTypeUrl(field.type_url()), + stream_.get()); break; } default: // TYPE_GROUP or TYPE_MESSAGE @@ -1401,6 +1436,24 @@ ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) { } } +bool ProtoStreamObjectWriter::ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name) { + if (element_ == NULL) return true; + + if (field.oneof_index() > 0) { + if (element_->OneofIndexTaken(field.oneof_index())) { + InvalidValue( + "oneof", + StrCat("oneof field '", + element_->type().oneofs(field.oneof_index() - 1), + "' is already set. Cannot set '", unnormalized_name, "'")); + return false; + } + element_->TakeOneofIndex(field.oneof_index()); + } + return true; +} + const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed( StringPiece name, bool is_list) { if (invalid_depth_ > 0) { @@ -1450,7 +1503,7 @@ const google::protobuf::Field* ProtoStreamObjectWriter::Lookup( const google::protobuf::Type* ProtoStreamObjectWriter::LookupType( const google::protobuf::Field* field) { return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE - ? typeinfo_->GetType(field->type_url()) + ? typeinfo_->GetTypeByTypeUrl(field->type_url()) : &element_->type()); } @@ -1539,7 +1592,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { return false; } const google::protobuf::Type* field_type = - typeinfo_->GetType(field.type_url()); + typeinfo_->GetTypeByTypeUrl(field.type_url()); return GetBoolOptionOrDefault(field_type->options(), "google.protobuf.MessageOptions.map_entry", false); diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h index eb4a59f9..f11c47c0 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.h +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -71,7 +71,7 @@ class ObjectLocationTracker; // It also supports streaming. class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter { public: - // Constructor. Does not take ownership of any parameter passed in. +// Constructor. Does not take ownership of any parameter passed in. ProtoStreamObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener); @@ -82,20 +82,17 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter virtual ProtoStreamObjectWriter* EndObject(); virtual ProtoStreamObjectWriter* StartList(StringPiece name); virtual ProtoStreamObjectWriter* EndList(); - virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, - bool value) { + virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, bool value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, - int32 value) { + virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, int32 value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name, uint32 value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, - int64 value) { + virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, int64 value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name, @@ -106,8 +103,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter double value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, - float value) { + virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, float value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderString(StringPiece name, @@ -217,7 +213,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter }; // Constructor for the root element. No parent nor field. - ProtoElement(TypeInfo* typeinfo, const google::protobuf::Type& type, + ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type, ProtoStreamObjectWriter* enclosing); // Constructor for a field of an element. @@ -256,6 +252,13 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter return static_cast<ProtoElement*>(BaseElement::parent()); } + // Returns true if the index is already taken by a preceeding oneof input. + bool OneofIndexTaken(int32 index); + + // Marks the oneof 'index' as taken. Future inputs to this oneof will + // generate an error. + void TakeOneofIndex(int32 index); + private: // Used for access to variables of the enclosing instance of // ProtoStreamObjectWriter. @@ -269,7 +272,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter const google::protobuf::Field* field_; // TypeInfo to lookup types. - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Additional variables if this element is a message: // (Root element is always a message). @@ -289,6 +292,10 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter // The type of this element, see enum for permissible types. ElementType element_type_; + // Set of oneof indices already seen for the type_. Used to validate + // incoming messages so no more than one oneof is set. + hash_set<int32> oneof_indices_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); }; @@ -298,7 +305,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter int size; }; - ProtoStreamObjectWriter(TypeInfo* typeinfo, + ProtoStreamObjectWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener); @@ -407,11 +414,19 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter static ProtoElement::ElementType GetElementType( const google::protobuf::Type& type); + // Returns true if the field for type_ can be set as a oneof. If field is not + // a oneof type, this function does nothing and returns true. + // If another field for this oneof is already set, this function returns + // false. It also calls the appropriate error callback. + // unnormalized_name is used for error string. + bool ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name); + // Variables for describing the structure of the input tree: // master_type_: descriptor for the whole protobuf message. // typeinfo_ : the TypeInfo object to lookup types. const google::protobuf::Type& master_type_; - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Whether we own the typeinfo_ object. bool own_typeinfo_; diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc index bd4f29f5..96e5ccfb 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc @@ -49,6 +49,7 @@ #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/util/internal/testdata/anys.pb.h> #include <google/protobuf/util/internal/testdata/maps.pb.h> +#include <google/protobuf/util/internal/testdata/oneofs.pb.h> #include <google/protobuf/util/internal/testdata/struct.pb.h> #include <google/protobuf/util/internal/testdata/timestamp_duration.pb.h> #include <gtest/gtest.h> @@ -75,6 +76,7 @@ using ::testing::_; using ::testing::Args; using google::protobuf::testing::anys::AnyM; using google::protobuf::testing::anys::AnyOut; +using google::protobuf::testing::oneofs::OneOfsRequest; using google::protobuf::testing::FieldMaskTest; using google::protobuf::testing::maps::MapIn; using google::protobuf::testing::structs::StructType; @@ -143,7 +145,7 @@ class BaseProtoStreamObjectWriterTest void CheckOutput(const Message& expected) { CheckOutput(expected, -1); } const google::protobuf::Type* GetType(const Descriptor* descriptor) { - return helper_.GetTypeInfo()->GetType(GetTypeUrl(descriptor)); + return helper_.GetTypeInfo()->GetTypeByTypeUrl(GetTypeUrl(descriptor)); } testing::TypeInfoTestHelper helper_; @@ -854,11 +856,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Timestamp"), - StringPiece( - "Field 'ts', Illegal timestamp format; timestamps " - "must end with 'Z'"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Illegal timestamp format; timestamps " + "must end with 'Z'"))); ow_->StartObject("")->RenderString("ts", "")->EndObject(); CheckOutput(timestamp); @@ -883,11 +884,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError3) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Timestamp"), - StringPiece( - "Field 'ts', Invalid time format, failed to parse nano " - "seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format, failed to parse nano " + "seconds"))); ow_->StartObject("") ->RenderString("ts", "1970-01-01T00:00:00.ABZ") @@ -919,11 +919,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Illegal duration format; duration must " - "end with 's'"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Illegal duration format; duration must " + "end with 's'"))); ow_->StartObject("")->RenderString("dur", "")->EndObject(); CheckOutput(duration); @@ -934,11 +933,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError2) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Invalid duration format, failed to parse " - "seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Invalid duration format, failed to parse " + "seconds"))); ow_->StartObject("")->RenderString("dur", "s")->EndObject(); CheckOutput(duration); @@ -949,11 +947,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Invalid duration format, failed to " - "parse nanos seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Invalid duration format, failed to " + "parse nanos seconds"))); ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject(); CheckOutput(duration); @@ -1174,10 +1171,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, EmptyAnyFromEmptyObject) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1192,10 +1189,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1210,10 +1207,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1227,13 +1224,12 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithInvalidTypeUrlFails) { AnyOut any; - EXPECT_CALL( - listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Invalid type URL, type URLs must be of the form " - "'type.googleapis.com/<typename>', got: " - "type.other.com/some.Type"))); + EXPECT_CALL(listener_, + InvalidValue( + _, StringPiece("Any"), + StringPiece("Invalid type URL, type URLs must be of the form " + "'type.googleapis.com/<typename>', got: " + "type.other.com/some.Type"))); ow_->StartObject("") ->StartObject("any") @@ -1401,11 +1397,10 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MaskUsingApiaryStyleShouldWork) { TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreCloseThanOpenParentheses) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask 'a(b,c))'. " - "Cannot find matching '(' for all ')'."))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), + StringPiece("Field 'single_mask', Invalid FieldMask 'a(b,c))'. " + "Cannot find matching '(' for all ')'."))); ow_->StartObject(""); ow_->RenderString("id", "1"); @@ -1448,12 +1443,11 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeAtTheEndOfAPathSegment) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. " - "Map keys should be at the end of a path segment."))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. " + "Map keys should be at the end of a path segment."))); ow_->StartObject(""); ow_->RenderString("single_mask", @@ -1466,10 +1460,9 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustEnd) { listener_, InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"key1\"'. Map keys should be " - "represented as [\"some_key\"]."))); + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"key1\"'. Map keys should be " + "represented as [\"some_key\"]."))); ow_->StartObject(""); ow_->RenderString("single_mask", "path.to.map[\"key1\""); @@ -1481,10 +1474,9 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeEscapedCorrectly) { listener_, InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"ke\"y1\"]'. Map keys should be " - "represented as [\"some_key\"]."))); + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"ke\"y1\"]'. Map keys should be " + "represented as [\"some_key\"]."))); ow_->StartObject(""); ow_->RenderString("single_mask", "path.to.map[\"ke\"y1\"]"); @@ -1507,6 +1499,192 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyCanContainAnyChars) { CheckOutput(expected); } +class ProtoStreamObjectWriterOneOfsTest + : public BaseProtoStreamObjectWriterTest { + protected: + ProtoStreamObjectWriterOneOfsTest() { + vector<const Descriptor*> descriptors; + descriptors.push_back(OneOfsRequest::descriptor()); + descriptors.push_back(google::protobuf::Struct::descriptor()); + ResetTypeInfo(descriptors); + } +}; + +INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, + ProtoStreamObjectWriterOneOfsTest, + ::testing::Values( + testing::USE_TYPE_RESOLVER)); + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForPrimitiveTypesTest) { + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("oneof"), + StringPiece( + "oneof field 'data' is already set. Cannot set 'intData'"))); + + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->RenderString("intData", "123"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForMessageTypesPrimitiveFirstTest) { + // Test for setting primitive oneof field first and then message field. + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'messageData'"))); + + // JSON: { "strData": "blah", "messageData": { "dataValue": 123 } } + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForMessageTypesMessageFirstTest) { + // Test for setting message oneof field first and then primitive field. + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'strData'"))); + + // JSON: { "messageData": { "dataValue": 123 }, "strData": "blah" } + ow_->StartObject(""); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->RenderString("strData", "blah"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructTypesPrimitiveFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'structData'"))); + + // JSON: { "strData": "blah", "structData": { "a": "b" } } + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->StartObject("structData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructTypesStructFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'strData'"))); + + // JSON: { "structData": { "a": "b" }, "strData": "blah" } + ow_->StartObject(""); + ow_->StartObject("structData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->RenderString("strData", "blah"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructValueTypesTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'valueData'"))); + + // JSON: { "messageData": { "dataValue": 123 }, "valueData": { "a": "b" } } + ow_->StartObject(""); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->StartObject("valueData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesPrimitiveFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'tsData'"))); + + // JSON: { "intData": 123, "tsData": "1970-01-02T01:00:00.000Z" } + ow_->StartObject(""); + ow_->RenderInt32("intData", 123); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesWktFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'intData'"))); + + // JSON: { "tsData": "1970-01-02T01:00:00.000Z", "intData": 123 } + ow_->StartObject(""); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->RenderInt32("intData", 123); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesAndMessageTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'messageData'"))); + + // JSON: { "tsData": "1970-01-02T01:00:00.000Z", + // "messageData": { "dataValue": 123 } } + ow_->StartObject(""); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForOneofWithinAnyTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'intData'"))); + + using google::protobuf::testing::oneofs::OneOfsRequest; + // JSON: + // { "anyData": + // { "@type": + // "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest", + // "strData": "blah", + // "intData": 123 + // } + // } + ow_->StartObject(""); + ow_->StartObject("anyData"); + ow_->RenderString( + "@type", + "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest"); + ow_->RenderString("strData", "blah"); + ow_->RenderInt32("intData", 123); + ow_->EndObject(); +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter.h b/src/google/protobuf/util/internal/snake2camel_objectwriter.h index 1a32bc56..9b4ab8a3 100644 --- a/src/google/protobuf/util/internal/snake2camel_objectwriter.h +++ b/src/google/protobuf/util/internal/snake2camel_objectwriter.h @@ -58,9 +58,7 @@ class Snake2CamelObjectWriter : public ObjectWriter { // ObjectWriter methods. virtual Snake2CamelObjectWriter* StartObject(StringPiece name) { - ow_->StartObject(ShouldNormalizeCase(name) - ? StringPiece(StringPiece(ToCamelCase(name))) - : name); + ow_->StartObject(name); return this; } @@ -70,8 +68,7 @@ class Snake2CamelObjectWriter : public ObjectWriter { } virtual Snake2CamelObjectWriter* StartList(StringPiece name) { - ow_->StartList(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) - : name); + ow_->StartList(name); return this; } @@ -81,76 +78,57 @@ class Snake2CamelObjectWriter : public ObjectWriter { } virtual Snake2CamelObjectWriter* RenderBool(StringPiece name, bool value) { - ow_->RenderBool( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderBool(name, value); return this; } virtual Snake2CamelObjectWriter* RenderInt32(StringPiece name, int32 value) { - ow_->RenderInt32( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderInt32(name, value); return this; } virtual Snake2CamelObjectWriter* RenderUint32(StringPiece name, uint32 value) { - ow_->RenderUint32( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderUint32(name, value); return this; } virtual Snake2CamelObjectWriter* RenderInt64(StringPiece name, int64 value) { - ow_->RenderInt64( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderInt64(name, value); return this; } virtual Snake2CamelObjectWriter* RenderUint64(StringPiece name, uint64 value) { - ow_->RenderUint64( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderUint64(name, value); return this; } virtual Snake2CamelObjectWriter* RenderDouble(StringPiece name, double value) { - ow_->RenderDouble( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderDouble(name, value); return this; } virtual Snake2CamelObjectWriter* RenderFloat(StringPiece name, float value) { - ow_->RenderFloat( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderFloat(name, value); return this; } virtual Snake2CamelObjectWriter* RenderString(StringPiece name, StringPiece value) { - ow_->RenderString( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderString(name, value); return this; } virtual Snake2CamelObjectWriter* RenderBytes(StringPiece name, StringPiece value) { - ow_->RenderBytes( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderBytes(name, value); return this; } virtual Snake2CamelObjectWriter* RenderNull(StringPiece name) { - ow_->RenderNull(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) - : name); + ow_->RenderNull(name); return this; } diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc index 67388c3b..e5db844c 100644 --- a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc @@ -47,263 +47,9 @@ class Snake2CamelObjectWriterTest : public ::testing::Test { Snake2CamelObjectWriter testing_; }; -TEST_F(Snake2CamelObjectWriterTest, Empty) { - // Set expectation - expects_.StartObject("")->EndObject(); - - // Actual testing - testing_.StartObject("")->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, UnderscoresOnly) { - // Set expectation - expects_.StartObject("") - ->RenderInt32("", 1) - ->RenderInt32("", 2) - ->RenderInt32("", 3) - ->RenderInt32("", 4) - ->RenderInt32("", 5) - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderInt32("_", 1) - ->RenderInt32("__", 2) - ->RenderInt32("___", 3) - ->RenderInt32("____", 4) - ->RenderInt32("_____", 5) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LowercaseOnly) { - // Set expectation - expects_.StartObject("") - ->RenderString("key", "value") - ->RenderString("abracadabra", "magic") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("key", "value") - ->RenderString("abracadabra", "magic") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, UppercaseOnly) { - // Set expectation - expects_.StartObject("") - ->RenderString("key", "VALUE") - ->RenderString("abracadabra", "MAGIC") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("KEY", "VALUE") - ->RenderString("ABRACADABRA", "MAGIC") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, CamelCase) { - // Set expectation - expects_.StartObject("") - ->RenderString("camelCase", "camelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "theQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("camelCase", "camelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "theQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, FirstCapCamelCase) { - // Sets expectation - expects_.StartObject("camel") - ->RenderString("camelCase", "CamelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "TheQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); - - // Actual testing - testing_.StartObject("Camel") - ->RenderString("CamelCase", "CamelCase") - ->RenderString("TheQuickBrownFoxJumpsOverTheLazyDog", - "TheQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LastCapCamelCase) { - // Sets expectation - expects_.StartObject("lastCapCamelCasE")->EndObject(); - - // Actual testing - testing_.StartObject("lastCapCamelCasE")->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, MixedCapCamelCase) { - // Sets expectation - expects_.StartObject("googleIsTheBest") - ->RenderFloat("iLoveGOOGLE", 1.61803f) - ->RenderFloat("goGoogleGO", 2.71828f) - ->RenderFloat("gBikeISCool", 3.14159f) - ->EndObject(); - - // Actual testing - testing_.StartObject("GOOGLEIsTheBest") - ->RenderFloat("ILoveGOOGLE", 1.61803f) - ->RenderFloat("GOGoogleGO", 2.71828f) - ->RenderFloat("GBikeISCool", 3.14159f) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, MixedCase) { - // Sets expectation - expects_.StartObject("snakeCaseCamelCase") - ->RenderBool("camelCaseSnakeCase", false) - ->RenderBool("mixedCamelAndUnderScores", false) - ->RenderBool("goGOOGLEGo", true) - ->EndObject(); - - // Actual testing - testing_.StartObject("snake_case_camelCase") - ->RenderBool("camelCase_snake_case", false) - ->RenderBool("MixedCamel_And_UnderScores", false) - ->RenderBool("Go_GOOGLEGo", true) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, SnakeCase) { - // Sets expectation - expects_.StartObject("") - ->RenderString("snakeCase", "snake_case") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("snake_case", "snake_case") - ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, FirstCapSnakeCase) { - // Sets expectation - expects_.StartObject("firstCapSnakeCase") - ->RenderBool("helloWorld", true) - ->EndObject(); - - // Actual testing - testing_.StartObject("First_Cap_Snake_Case") - ->RenderBool("Hello_World", true) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, AllCapSnakeCase) { - // Sets expectation - expects_.StartObject("allCAPSNAKECASE") - ->RenderDouble("nyseGOOGL", 600.0L) - ->RenderDouble("aBCDE", 1.0L) - ->RenderDouble("klMNOP", 2.0L) - ->RenderDouble("abcIJKPQRXYZ", 3.0L) - ->EndObject(); - - // Actual testing - testing_.StartObject("ALL_CAP_SNAKE_CASE") - ->RenderDouble("NYSE_GOOGL", 600.0L) - ->RenderDouble("A_B_C_D_E", 1.0L) - ->RenderDouble("KL_MN_OP", 2.0L) - ->RenderDouble("ABC_IJK_PQR_XYZ", 3.0L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, RepeatedUnderScoreSnakeCase) { - // Sets expectation - expects_.StartObject("") - ->RenderInt32("doubleUnderscoreSnakeCase", 2) - ->RenderInt32("tripleUnderscoreFirstCap", 3) - ->RenderInt32("quadrupleUNDERSCOREALLCAP", 4) - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderInt32("double__underscore__snake__case", 2) - ->RenderInt32("Triple___Underscore___First___Cap", 3) - ->RenderInt32("QUADRUPLE____UNDERSCORE____ALL____CAP", 4) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LeadingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("leadingUnderscoreSnakeCase") - ->RenderUint32("leadingDoubleUnderscore", 2) - ->RenderUint32("leadingTripleUnderscoreFirstCap", 3) - ->RenderUint32("leadingQUADRUPLEUNDERSCOREALLCAP", 4) - ->EndObject(); - - // Actual testing - testing_.StartObject("_leading_underscore_snake_case") - ->RenderUint32("__leading_double_underscore", 2) - ->RenderUint32("___Leading_Triple_Underscore_First_Cap", 3) - ->RenderUint32("____LEADING_QUADRUPLE_UNDERSCORE_ALL_CAP", 4) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, TrailingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("trailingUnderscoreSnakeCase") - ->RenderInt64("trailingDoubleUnderscore", 2L) - ->RenderInt64("trailingTripleUnderscoreFirstCap", 3L) - ->RenderInt64("trailingQUADRUPLEUNDERSCOREALLCAP", 4L) - ->EndObject(); - - // Actual testing - testing_.StartObject("trailing_underscore_snake_case") - ->RenderInt64("trailing_double_underscore__", 2L) - ->RenderInt64("Trailing_Triple_Underscore_First_Cap___", 3L) - ->RenderInt64("TRAILING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, EnclosingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("enclosingUnderscoreSnakeCase") - ->RenderUint64("enclosingDoubleUnderscore", 2L) - ->RenderUint64("enclosingTripleUnderscoreFirstCap", 3L) - ->RenderUint64("enclosingQUADRUPLEUNDERSCOREALLCAP", 4L) - ->EndObject(); - - // Actual testing - testing_.StartObject("_enclosing_underscore_snake_case_") - ->RenderUint64("__enclosing_double_underscore__", 2L) - ->RenderUint64("___Enclosing_Triple_Underscore_First_Cap___", 3L) - ->RenderUint64("____ENCLOSING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, DisableCaseNormalizationOnlyDisablesFirst) { - // Sets expectation - expects_.StartObject("") - ->RenderString("snakeCase", "snake_case") - ->RenderString( - "the_quick_brown_fox_jumps_over_the_lazy_dog", // case retained - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->RenderBool("theSlowFox", true) // disable case not in effect - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("snake_case", "snake_case") - ->DisableCaseNormalizationForNextKey() - ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->RenderBool("the_slow_fox", true) - ->EndObject(); -} +// All tests are deleted as they are no longer needed. This file will be removed +// after the component dependecies are cleaned up. +// TODO(skarvaje): Remove this file. } // namespace converter } // namespace util diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto index ecfc8119..ebbdf6ab 100644 --- a/src/google/protobuf/util/internal/testdata/default_value.proto +++ b/src/google/protobuf/util/internal/testdata/default_value.proto @@ -43,6 +43,7 @@ message DefaultValueTestCases { DoubleMessage repeated_double = 4; DoubleMessage nested_message = 5; DoubleMessage repeated_nested_message = 6; + DoubleMessage double_message_with_oneof = 7; StructMessage empty_struct = 201; StructMessage empty_struct2 = 202; StructMessage struct_with_null_value = 203; @@ -75,6 +76,8 @@ message DefaultValueTestCases { MixedMap mixed1 = 404; MixedMap2 mixed2 = 405; MessageMap map_of_objects = 406; + MixedMap mixed_empty = 407; + MessageMap message_map_empty = 408; DoubleValueMessage double_value = 501; DoubleValueMessage double_value_default = 502; } @@ -85,6 +88,10 @@ message DoubleMessage { DoubleMessage nested_message = 3; repeated DoubleMessage repeated_nested_message = 4; google.protobuf.DoubleValue double_wrapper = 100; + oneof value { + string str_value = 112; + int64 num_value = 113; + } } message StructMessage { diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc index 6392e18c..a45a76e3 100644 --- a/src/google/protobuf/util/internal/type_info.cc +++ b/src/google/protobuf/util/internal/type_info.cc @@ -35,11 +35,11 @@ #include <google/protobuf/stubs/common.h> #include <google/protobuf/type.pb.h> +#include <google/protobuf/util/internal/utility.h> #include <google/protobuf/stubs/stringpiece.h> #include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/status.h> #include <google/protobuf/stubs/statusor.h> -#include <google/protobuf/util/internal/utility.h> namespace google { namespace protobuf { @@ -47,7 +47,6 @@ namespace util { namespace converter { namespace { - // A TypeInfo that looks up information provided by a TypeResolver. class TypeInfoForTypeResolver : public TypeInfo { public: @@ -60,7 +59,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl( - StringPiece type_url) { + StringPiece type_url) const { map<StringPiece, StatusOrType>::iterator it = cached_types_.find(type_url); if (it != cached_types_.end()) { return it->second; @@ -78,12 +77,14 @@ class TypeInfoForTypeResolver : public TypeInfo { return result; } - virtual const google::protobuf::Type* GetType(StringPiece type_url) { + virtual const google::protobuf::Type* GetTypeByTypeUrl( + StringPiece type_url) const { StatusOrType result = ResolveTypeUrl(type_url); return result.ok() ? result.ValueOrDie() : NULL; } - virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) { + virtual const google::protobuf::Enum* GetEnumByTypeUrl( + StringPiece type_url) const { map<StringPiece, StatusOrEnum>::iterator it = cached_enums_.find(type_url); if (it != cached_enums_.end()) { return it->second.ok() ? it->second.ValueOrDie() : NULL; @@ -103,7 +104,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } virtual const google::protobuf::Field* FindField( - const google::protobuf::Type* type, StringPiece camel_case_name) { + const google::protobuf::Type* type, StringPiece camel_case_name) const { if (indexed_types_.find(type) == indexed_types_.end()) { PopulateNameLookupTable(type); indexed_types_.insert(type); @@ -131,7 +132,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } } - void PopulateNameLookupTable(const google::protobuf::Type* type) { + void PopulateNameLookupTable(const google::protobuf::Type* type) const { for (int i = 0; i < type->fields_size(); ++i) { const google::protobuf::Field& field = type->fields(i); StringPiece name = field.name(); @@ -151,13 +152,13 @@ class TypeInfoForTypeResolver : public TypeInfo { // Stores string values that will be referenced by StringPieces in // cached_types_, cached_enums_ and camel_case_name_table_. - set<string> string_storage_; + mutable set<string> string_storage_; - map<StringPiece, StatusOrType> cached_types_; - map<StringPiece, StatusOrEnum> cached_enums_; + mutable map<StringPiece, StatusOrType> cached_types_; + mutable map<StringPiece, StatusOrEnum> cached_enums_; - set<const google::protobuf::Type*> indexed_types_; - map<StringPiece, StringPiece> camel_case_name_table_; + mutable set<const google::protobuf::Type*> indexed_types_; + mutable map<StringPiece, StringPiece> camel_case_name_table_; }; } // namespace diff --git a/src/google/protobuf/util/internal/type_info.h b/src/google/protobuf/util/internal/type_info.h index 67403fff..e394e8cf 100644 --- a/src/google/protobuf/util/internal/type_info.h +++ b/src/google/protobuf/util/internal/type_info.h @@ -44,7 +44,7 @@ namespace util { namespace converter { // Internal helper class for type resolving. Note that this class is not // thread-safe and should only be accessed in one thread. -class LIBPROTOBUF_EXPORT TypeInfo { +class TypeInfo { public: TypeInfo() {} virtual ~TypeInfo() {} @@ -55,24 +55,29 @@ class LIBPROTOBUF_EXPORT TypeInfo { // // This TypeInfo class retains the ownership of the returned pointer. virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl( - StringPiece type_url) = 0; + StringPiece type_url) const = 0; // Resolves a type url into a Type. Like ResolveTypeUrl() but returns // NULL if the type url is invalid or the type cannot be found. // // This TypeInfo class retains the ownership of the returned pointer. - virtual const google::protobuf::Type* GetType(StringPiece type_url) = 0; + virtual const google::protobuf::Type* GetTypeByTypeUrl( + StringPiece type_url) const = 0; // Resolves a type url for an enum. Returns NULL if the type url is // invalid or the type cannot be found. // // This TypeInfo class retains the ownership of the returned pointer. - virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) = 0; + virtual const google::protobuf::Enum* GetEnumByTypeUrl( + StringPiece type_url) const = 0; // Looks up a field in the specified type given a CamelCase name. virtual const google::protobuf::Field* FindField( - const google::protobuf::Type* type, StringPiece camel_case_name) = 0; + const google::protobuf::Type* type, + StringPiece camel_case_name) const = 0; + // Creates a TypeInfo object that looks up type information from a + // TypeResolver. Caller takes ownership of the returned pointer. static TypeInfo* NewTypeInfo(TypeResolver* type_resolver); private: diff --git a/src/google/protobuf/util/internal/type_info_test_helper.cc b/src/google/protobuf/util/internal/type_info_test_helper.cc index 177b96e2..1b9c5154 100644 --- a/src/google/protobuf/util/internal/type_info_test_helper.cc +++ b/src/google/protobuf/util/internal/type_info_test_helper.cc @@ -36,6 +36,7 @@ #endif #include <vector> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/util/internal/default_value_objectwriter.h> @@ -89,7 +90,7 @@ TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); } ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( io::CodedInputStream* coded_input, const string& type_url) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new ProtoStreamObjectSource(coded_input, type_resolver_.get(), @@ -103,7 +104,7 @@ ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( const string& type_url, strings::ByteSink* output, ErrorListener* listener) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output, @@ -116,7 +117,7 @@ ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( DefaultValueObjectWriter* TypeInfoTestHelper::NewDefaultValueWriter( const string& type_url, ObjectWriter* writer) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new DefaultValueObjectWriter(type_resolver_.get(), *type, writer); diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc index dfc4add2..9d80fa08 100644 --- a/src/google/protobuf/util/internal/utility.cc +++ b/src/google/protobuf/util/internal/utility.cc @@ -30,7 +30,9 @@ #include <google/protobuf/util/internal/utility.h> +#include <google/protobuf/stubs/callback.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/wrappers.pb.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.h> diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h index d0d88c19..87f7602a 100644 --- a/src/google/protobuf/util/internal/utility.h +++ b/src/google/protobuf/util/internal/utility.h @@ -39,6 +39,7 @@ #include <utility> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/type.pb.h> #include <google/protobuf/repeated_field.h> #include <google/protobuf/stubs/stringpiece.h> @@ -117,23 +118,23 @@ LIBPROTOBUF_EXPORT const string GetFullTypeWithUrl(StringPiece simple_type); // Finds and returns option identified by name and option_name within the // provided map. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::Option* FindOptionOrNull( +const google::protobuf::Option* FindOptionOrNull( const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, const string& option_name); // Finds and returns the field identified by field_name in the passed tech Type // object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::Field* FindFieldInTypeOrNull( +const google::protobuf::Field* FindFieldInTypeOrNull( const google::protobuf::Type* type, StringPiece field_name); // Finds and returns the EnumValue identified by enum_name in the passed tech // Enum object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNameOrNull( +const google::protobuf::EnumValue* FindEnumValueByNameOrNull( const google::protobuf::Enum* enum_type, StringPiece enum_name); // Finds and returns the EnumValue identified by value in the passed tech // Enum object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( +const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( const google::protobuf::Enum* enum_type, int32 value); // Converts input to camel-case and returns it. @@ -153,7 +154,7 @@ LIBPROTOBUF_EXPORT bool IsWellKnownType(const string& type_name); LIBPROTOBUF_EXPORT bool IsValidBoolString(const string& bool_string); // Returns true if "field" is a protobuf map field based on its type. -bool IsMap(const google::protobuf::Field& field, +LIBPROTOBUF_EXPORT bool IsMap(const google::protobuf::Field& field, const google::protobuf::Type& type); // Infinity/NaN-aware conversion to string. |