blob: 786bf0be20d6446bc5767dac56c3dd6e30ab6f10 [file] [log] [blame]
Feng Xiaoe96ff302015-06-15 18:21:48 -07001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#include <google/protobuf/util/internal/protostream_objectwriter.h>
32
33#include <functional>
34#include <stack>
35
Jisi Liu46e8ff62015-10-05 11:59:43 -070036#include <google/protobuf/stubs/once.h>
Feng Xiaoe96ff302015-06-15 18:21:48 -070037#include <google/protobuf/stubs/time.h>
38#include <google/protobuf/wire_format_lite.h>
39#include <google/protobuf/util/internal/field_mask_utility.h>
40#include <google/protobuf/util/internal/object_location_tracker.h>
41#include <google/protobuf/util/internal/constants.h>
42#include <google/protobuf/util/internal/utility.h>
43#include <google/protobuf/stubs/strutil.h>
44#include <google/protobuf/stubs/map_util.h>
45#include <google/protobuf/stubs/statusor.h>
46
47
48namespace google {
49namespace protobuf {
50namespace util {
51namespace converter {
52
53using google::protobuf::internal::WireFormatLite;
Feng Xiaoe96ff302015-06-15 18:21:48 -070054using util::error::INVALID_ARGUMENT;
55using util::Status;
56using util::StatusOr;
57
58
59ProtoStreamObjectWriter::ProtoStreamObjectWriter(
60 TypeResolver* type_resolver, const google::protobuf::Type& type,
61 strings::ByteSink* output, ErrorListener* listener)
Feng Xiaoe841bac2015-12-11 17:09:20 -080062 : ProtoWriter(type_resolver, type, output, listener),
63 master_type_(type),
64 current_(NULL) {}
Feng Xiaoe96ff302015-06-15 18:21:48 -070065
66ProtoStreamObjectWriter::ProtoStreamObjectWriter(
Feng Xiaoeee38b02015-08-22 18:25:48 -070067 const TypeInfo* typeinfo, const google::protobuf::Type& type,
Feng Xiaoe96ff302015-06-15 18:21:48 -070068 strings::ByteSink* output, ErrorListener* listener)
Feng Xiaoe841bac2015-12-11 17:09:20 -080069 : ProtoWriter(typeinfo, type, output, listener),
70 master_type_(type),
71 current_(NULL) {}
Feng Xiaoe96ff302015-06-15 18:21:48 -070072
73ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
Feng Xiaoe841bac2015-12-11 17:09:20 -080074 if (current_ == NULL) return;
Feng Xiaoeee38b02015-08-22 18:25:48 -070075 // Cleanup explicitly in order to avoid destructor stack overflow when input
76 // is deeply nested.
77 // Cast to BaseElement to avoid doing additional checks (like missing fields)
78 // during pop().
79 google::protobuf::scoped_ptr<BaseElement> element(
Feng Xiaoe841bac2015-12-11 17:09:20 -080080 static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
Feng Xiaoeee38b02015-08-22 18:25:48 -070081 while (element != NULL) {
82 element.reset(element->pop<BaseElement>());
83 }
Feng Xiaoe96ff302015-06-15 18:21:48 -070084}
85
86namespace {
Feng Xiaoe96ff302015-06-15 18:21:48 -070087// Utility method to split a string representation of Timestamp or Duration and
88// return the parts.
89void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
90 StringPiece* nanos) {
91 size_t idx = input.rfind('.');
92 if (idx != string::npos) {
93 *seconds = input.substr(0, idx);
94 *nanos = input.substr(idx + 1);
95 } else {
96 *seconds = input;
97 *nanos = StringPiece();
98 }
99}
100
Feng Xiaoe841bac2015-12-11 17:09:20 -0800101Status GetNanosFromStringPiece(StringPiece s_nanos,
102 const char* parse_failure_message,
103 const char* exceeded_limit_message,
104 int32* nanos) {
105 *nanos = 0;
106
107 // Count the number of leading 0s and consume them.
108 int num_leading_zeros = 0;
109 while (s_nanos.Consume("0")) {
110 num_leading_zeros++;
111 }
112 int32 i_nanos = 0;
113 // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
114 // "0." + s_nanos.ToString() seconds. An int32 is used for the
115 // conversion to 'nanos', rather than a double, so that there is no
116 // loss of precision.
117 if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) {
118 return Status(INVALID_ARGUMENT, parse_failure_message);
119 }
120 if (i_nanos > kNanosPerSecond || i_nanos < 0) {
121 return Status(INVALID_ARGUMENT, exceeded_limit_message);
122 }
123 // s_nanos should only have digits. No whitespace.
124 if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
125 return Status(INVALID_ARGUMENT, parse_failure_message);
126 }
127
128 if (i_nanos > 0) {
129 // 'scale' is the number of digits to the right of the decimal
130 // point in "0." + s_nanos.ToString()
131 int32 scale = num_leading_zeros + s_nanos.size();
132 // 'conversion' converts i_nanos into nanoseconds.
133 // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
134 // For efficiency, we precompute the conversion factor.
135 int32 conversion = 0;
136 switch (scale) {
137 case 1:
138 conversion = 100000000;
139 break;
140 case 2:
141 conversion = 10000000;
142 break;
143 case 3:
144 conversion = 1000000;
145 break;
146 case 4:
147 conversion = 100000;
148 break;
149 case 5:
150 conversion = 10000;
151 break;
152 case 6:
153 conversion = 1000;
154 break;
155 case 7:
156 conversion = 100;
157 break;
158 case 8:
159 conversion = 10;
160 break;
161 case 9:
162 conversion = 1;
163 break;
164 default:
165 return Status(INVALID_ARGUMENT, exceeded_limit_message);
166 }
167 *nanos = i_nanos * conversion;
168 }
169
170 return Status::OK;
171}
172
Feng Xiaoe96ff302015-06-15 18:21:48 -0700173} // namespace
174
175ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
176 : parent_(parent),
177 ow_(),
178 invalid_(false),
179 data_(),
180 output_(&data_),
181 depth_(0),
182 has_injected_value_message_(false) {}
183
184ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
185
186void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
187 ++depth_;
188 // If an object writer is absent, that means we have not called StartAny()
189 // before reaching here. This is an invalid state. StartAny() gets called
190 // whenever we see an "@type" being rendered (see AnyWriter::RenderDataPiece).
191 if (ow_ == NULL) {
192 // Make sure we are not already in an invalid state. This avoids making
193 // multiple unnecessary InvalidValue calls.
194 if (!invalid_) {
195 parent_->InvalidValue("Any",
196 StrCat("Missing or invalid @type for any field in ",
197 parent_->master_type_.name()));
198 invalid_ = true;
199 }
200 } else if (!has_injected_value_message_ || depth_ != 1 || name != "value") {
201 // We don't propagate to ow_ StartObject("value") calls for nested Anys or
202 // Struct at depth 1 as they are nested one level deep with an injected
203 // "value" field.
204 ow_->StartObject(name);
205 }
206}
207
208bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
209 --depth_;
210 // As long as depth_ >= 0, we know we haven't reached the end of Any.
211 // Propagate these EndObject() calls to the contained ow_. If we are in a
212 // nested Any or Struct type, ignore the second to last EndObject call (depth_
213 // == -1)
214 if (ow_ != NULL && (!has_injected_value_message_ || depth_ >= 0)) {
215 ow_->EndObject();
216 }
217 // A negative depth_ implies that we have reached the end of Any
218 // object. Now we write out its contents.
219 if (depth_ < 0) {
220 WriteAny();
221 return false;
222 }
223 return true;
224}
225
226void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
227 ++depth_;
228 // We expect ow_ to be present as this call only makes sense inside an Any.
229 if (ow_ == NULL) {
230 if (!invalid_) {
231 parent_->InvalidValue("Any",
232 StrCat("Missing or invalid @type for any field in ",
233 parent_->master_type_.name()));
234 invalid_ = true;
235 }
236 } else {
237 ow_->StartList(name);
238 }
239}
240
241void ProtoStreamObjectWriter::AnyWriter::EndList() {
242 --depth_;
243 if (depth_ < 0) {
244 GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
245 depth_ = 0;
246 }
247 // We don't write an error on the close, only on the open
248 if (ow_ != NULL) {
249 ow_->EndList();
250 }
251}
252
253void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
254 StringPiece name, const DataPiece& value) {
255 // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
256 // should go to the contained ow_ as they indicate nested Anys.
257 if (depth_ == 0 && ow_ == NULL && name == "@type") {
258 StartAny(value);
259 } else if (ow_ == NULL) {
260 if (!invalid_) {
261 parent_->InvalidValue("Any",
262 StrCat("Missing or invalid @type for any field in ",
263 parent_->master_type_.name()));
264 invalid_ = true;
265 }
266 } else {
267 // Check to see if the data needs to be rendered with well-known-type
268 // renderer.
269 const TypeRenderer* type_renderer =
270 FindTypeRenderer(GetFullTypeWithUrl(ow_->master_type_.name()));
271 if (type_renderer) {
Jisi Liu46e8ff62015-10-05 11:59:43 -0700272 Status status = (*type_renderer)(ow_.get(), value);
273 if (!status.ok()) ow_->InvalidValue("Any", status.error_message());
Feng Xiaoe96ff302015-06-15 18:21:48 -0700274 } else {
275 ow_->RenderDataPiece(name, value);
276 }
277 }
278}
279
280void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
281 // Figure out the type url. This is a copy-paste from WriteString but we also
282 // need the value, so we can't just call through to that.
283 if (value.type() == DataPiece::TYPE_STRING) {
284 type_url_ = value.str().ToString();
285 } else {
286 StatusOr<string> s = value.ToString();
287 if (!s.ok()) {
288 parent_->InvalidValue("String", s.status().error_message());
289 invalid_ = true;
290 return;
291 }
292 type_url_ = s.ValueOrDie();
293 }
294 // Resolve the type url, and report an error if we failed to resolve it.
295 StatusOr<const google::protobuf::Type*> resolved_type =
Feng Xiaoe841bac2015-12-11 17:09:20 -0800296 parent_->typeinfo()->ResolveTypeUrl(type_url_);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700297 if (!resolved_type.ok()) {
298 parent_->InvalidValue("Any", resolved_type.status().error_message());
299 invalid_ = true;
300 return;
301 }
302 // At this point, type is never null.
303 const google::protobuf::Type* type = resolved_type.ValueOrDie();
304
305 // If this is the case of an Any in an Any or Struct in an Any, we need to
306 // expect a StartObject call with "value" while we're at depth_ 0, which we
307 // should ignore (not propagate to our nested object writer). We also need to
308 // ignore the second-to-last EndObject call, and not propagate that either.
309 if (type->name() == kAnyType || type->name() == kStructType) {
310 has_injected_value_message_ = true;
311 }
312
313 // Create our object writer and initialize it with the first StartObject
314 // call.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800315 ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
316 parent_->listener()));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700317 ow_->StartObject("");
318}
319
320void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
321 if (ow_ == NULL) {
322 // If we had no object writer, we never got any content, so just return
323 // immediately, which is equivalent to writing an empty Any.
324 return;
325 }
326 // Render the type_url and value fields directly to the stream.
327 // type_url has tag 1 and value has tag 2.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800328 WireFormatLite::WriteString(1, type_url_, parent_->stream());
Feng Xiaoe96ff302015-06-15 18:21:48 -0700329 if (!data_.empty()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800330 WireFormatLite::WriteBytes(2, data_, parent_->stream());
Feng Xiaoe96ff302015-06-15 18:21:48 -0700331 }
332}
333
Feng Xiaoe841bac2015-12-11 17:09:20 -0800334ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
335 ItemType item_type, bool is_placeholder,
336 bool is_list)
Feng Xiaoe96ff302015-06-15 18:21:48 -0700337 : BaseElement(NULL),
338 ow_(enclosing),
339 any_(),
Feng Xiaoe841bac2015-12-11 17:09:20 -0800340 item_type_(item_type),
341 is_placeholder_(is_placeholder),
342 is_list_(is_list) {
343 if (item_type_ == ANY) {
Feng Xiaoe96ff302015-06-15 18:21:48 -0700344 any_.reset(new AnyWriter(ow_));
345 }
346}
347
Feng Xiaoe841bac2015-12-11 17:09:20 -0800348ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
349 ItemType item_type, bool is_placeholder,
350 bool is_list)
Feng Xiaoe96ff302015-06-15 18:21:48 -0700351 : BaseElement(parent),
352 ow_(this->parent()->ow_),
353 any_(),
Feng Xiaoe841bac2015-12-11 17:09:20 -0800354 item_type_(item_type),
355 is_placeholder_(is_placeholder),
356 is_list_(is_list) {
357 if (item_type == ANY) {
Feng Xiaoe96ff302015-06-15 18:21:48 -0700358 any_.reset(new AnyWriter(ow_));
359 }
360}
361
Feng Xiaoe841bac2015-12-11 17:09:20 -0800362bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
Jisi Liu46e8ff62015-10-05 11:59:43 -0700363 StringPiece map_key) {
Feng Xiao9659ea92015-11-02 12:39:27 -0800364 return InsertIfNotPresent(&map_keys_, map_key.ToString());
Jisi Liu46e8ff62015-10-05 11:59:43 -0700365}
366
Feng Xiaoe96ff302015-06-15 18:21:48 -0700367ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
368 StringPiece name) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800369 if (invalid_depth() > 0) {
370 IncrementInvalidDepth();
371 return this;
372 }
373
374 // Starting the root message. Create the root Item and return.
375 // ANY message type does not need special handling, just set the ItemType
376 // to ANY.
377 if (current_ == NULL) {
378 ProtoWriter::StartObject(name);
379 current_.reset(new Item(
380 this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
381 false, false));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700382
383 // If master type is a special type that needs extra values to be written to
384 // stream, we write those values.
385 if (master_type_.name() == kStructType) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800386 // Struct has a map<string, Value> field called "fields".
387 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
388 // "fields": [
389 Push("fields", Item::MAP, true, true);
Jisi Liu46e8ff62015-10-05 11:59:43 -0700390 return this;
391 }
392
Feng Xiaoe841bac2015-12-11 17:09:20 -0800393 if (master_type_.name() == kStructValueType) {
394 // We got a StartObject call with google.protobuf.Value field. The only
395 // object within that type is a struct type. So start a struct.
396 //
397 // The struct field in Value type is named "struct_value"
398 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
399 // Also start the map field "fields" within the struct.
400 // "struct_value": {
401 // "fields": [
402 Push("struct_value", Item::MESSAGE, true, false);
403 Push("fields", Item::MAP, true, true);
404 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700405 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800406
407 if (master_type_.name() == kStructListValueType) {
408 InvalidValue(kStructListValueType,
409 "Cannot start root message with ListValue.");
410 }
411
Feng Xiaoe96ff302015-06-15 18:21:48 -0700412 return this;
413 }
414
Feng Xiaoe841bac2015-12-11 17:09:20 -0800415 // Send all ANY events to AnyWriter.
416 if (current_->IsAny()) {
417 current_->any()->StartObject(name);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700418 return this;
419 }
420
Feng Xiaoe841bac2015-12-11 17:09:20 -0800421 // If we are within a map, we render name as keys and send StartObject to the
422 // value field.
423 if (current_->IsMap()) {
424 if (!ValidMapKey(name)) {
425 IncrementInvalidDepth();
426 return this;
427 }
428
429 // Map is a repeated field of message type with a "key" and a "value" field.
430 // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
431 // message MapFieldEntry {
432 // key_type key = 1;
433 // value_type value = 2;
434 // }
435 //
436 // repeated MapFieldEntry map_field = N;
437 //
438 // That means, we render the following element within a list (hence no
439 // name):
440 // { "key": "<name>", "value": {
441 Push("", Item::MESSAGE, false, false);
442 ProtoWriter::RenderDataPiece("key", DataPiece(name));
443 Push("value", Item::MESSAGE, true, false);
444
445 // Make sure we are valid so far after starting map fields.
446 if (invalid_depth() > 0) return this;
447
448 // If top of stack is g.p.Struct type, start the struct the map field within
449 // it.
450 if (element() != NULL && IsStruct(*element()->parent_field())) {
451 // Render "fields": [
452 Push("fields", Item::MAP, true, true);
453 return this;
454 }
455
456 // If top of stack is g.p.Value type, start the Struct within it.
457 if (element() != NULL && IsStructValue(*element()->parent_field())) {
458 // Render
459 // "struct_value": {
460 // "fields": [
461 Push("struct_value", Item::MESSAGE, true, false);
462 Push("fields", Item::MAP, true, true);
463 }
Feng Xiaoeee38b02015-08-22 18:25:48 -0700464 return this;
465 }
466
Feng Xiaoe841bac2015-12-11 17:09:20 -0800467 const google::protobuf::Field* field = BeginNamed(name, false);
468 if (field == NULL) return this;
469
470 if (IsStruct(*field)) {
Feng Xiaoe96ff302015-06-15 18:21:48 -0700471 // Start a struct object.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800472 // Render
473 // "<name>": {
474 // "fields": {
475 Push(name, Item::MESSAGE, false, false);
476 Push("fields", Item::MAP, true, true);
477 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700478 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800479
480 if (IsStructValue(*field)) {
481 // We got a StartObject call with google.protobuf.Value field. The only
482 // object within that type is a struct type. So start a struct.
483 // Render
484 // "<name>": {
485 // "struct_value": {
486 // "fields": {
487 Push(name, Item::MESSAGE, false, false);
488 Push("struct_value", Item::MESSAGE, true, false);
489 Push("fields", Item::MAP, true, true);
490 return this;
491 }
492
493 if (IsMap(*field)) {
494 // Begin a map. A map is triggered by a StartObject() call if the current
495 // field has a map type.
496 // A map type is always repeated, hence set is_list to true.
497 // Render
498 // "<name>": [
499 Push(name, Item::MAP, false, true);
500 return this;
501 }
502
503 // A regular message type. Pass it directly to ProtoWriter.
504 // Render
505 // "<name>": {
506 Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700507 return this;
508}
509
Feng Xiaoe96ff302015-06-15 18:21:48 -0700510ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800511 if (invalid_depth() > 0) {
512 DecrementInvalidDepth();
Feng Xiaoe96ff302015-06-15 18:21:48 -0700513 return this;
514 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800515
516 if (current_ == NULL) return this;
517
518 if (current_->IsAny()) {
519 if (current_->any()->EndObject()) return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700520 }
521
Feng Xiaoe841bac2015-12-11 17:09:20 -0800522 Pop();
Feng Xiaoe96ff302015-06-15 18:21:48 -0700523
Feng Xiaoe96ff302015-06-15 18:21:48 -0700524 return this;
525}
526
527ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800528 if (invalid_depth() > 0) {
529 IncrementInvalidDepth();
530 return this;
531 }
532
Feng Xiaoe96ff302015-06-15 18:21:48 -0700533 // Since we cannot have a top-level repeated item in protobuf, the only way
Feng Xiaoe841bac2015-12-11 17:09:20 -0800534 // this is valid is if we start a special type google.protobuf.ListValue or
535 // google.protobuf.Value.
536 if (current_ == NULL) {
Feng Xiaoe96ff302015-06-15 18:21:48 -0700537 if (!name.empty()) {
538 InvalidName(name, "Root element should not be named.");
Feng Xiaoe841bac2015-12-11 17:09:20 -0800539 IncrementInvalidDepth();
540 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700541 }
Feng Xiaoe96ff302015-06-15 18:21:48 -0700542
543 // If master type is a special type that needs extra values to be written to
544 // stream, we write those values.
545 if (master_type_.name() == kStructValueType) {
546 // We got a StartList with google.protobuf.Value master type. This means
547 // we have to start the "list_value" within google.protobuf.Value.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800548 //
549 // See
550 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
551 //
552 // Render
553 // "<name>": {
554 // "list_value": {
555 // "values": [ // Start this list.
556 ProtoWriter::StartObject(name);
557 current_.reset(new Item(this, Item::MESSAGE, false, false));
558 Push("list_value", Item::MESSAGE, true, false);
559 Push("values", Item::MESSAGE, true, true);
560 return this;
561 }
562
563 if (master_type_.name() == kStructListValueType) {
Feng Xiaoe96ff302015-06-15 18:21:48 -0700564 // We got a StartList with google.protobuf.ListValue master type. This
565 // means we have to start the "values" within google.protobuf.ListValue.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800566 //
567 // Render
568 // "<name>": {
569 // "values": [ // Start this list.
570 ProtoWriter::StartObject(name);
571 current_.reset(new Item(this, Item::MESSAGE, false, false));
572 Push("values", Item::MESSAGE, true, true);
573 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700574 }
575
Feng Xiaoe841bac2015-12-11 17:09:20 -0800576 // Send the event to ProtoWriter so proper errors can be reported.
577 //
578 // Render a regular list:
579 // "<name>": [
580 ProtoWriter::StartList(name);
581 current_.reset(new Item(this, Item::MESSAGE, false, true));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700582 return this;
583 }
584
Feng Xiaoe841bac2015-12-11 17:09:20 -0800585 if (current_->IsAny()) {
586 current_->any()->StartList(name);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700587 return this;
588 }
Feng Xiaoe96ff302015-06-15 18:21:48 -0700589
Feng Xiaoe841bac2015-12-11 17:09:20 -0800590 // If the top of stack is a map, we are starting a list value within a map.
591 // Since map does not allow repeated values, this can only happen when the map
592 // value is of a special type that renders a list in JSON. These can be one
593 // of 3 cases:
594 // i. We are rendering a list value within google.protobuf.Struct
595 // ii. We are rendering a list value within google.protobuf.Value
596 // iii. We are rendering a list value with type google.protobuf.ListValue.
597 if (current_->IsMap()) {
Jisi Liu46e8ff62015-10-05 11:59:43 -0700598 if (!ValidMapKey(name)) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800599 IncrementInvalidDepth();
Jisi Liu46e8ff62015-10-05 11:59:43 -0700600 return this;
601 }
602
Feng Xiaoe841bac2015-12-11 17:09:20 -0800603 // Start the repeated map entry object.
604 // Render
605 // { "key": "<name>", "value": {
606 Push("", Item::MESSAGE, false, false);
607 ProtoWriter::RenderDataPiece("key", DataPiece(name));
608 Push("value", Item::MESSAGE, true, false);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700609
Feng Xiaoe841bac2015-12-11 17:09:20 -0800610 // Make sure we are valid after pushing all above items.
611 if (invalid_depth() > 0) return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700612
Feng Xiaoe841bac2015-12-11 17:09:20 -0800613 // case i and ii above. Start "list_value" field within g.p.Value
614 if (element() != NULL && element()->parent_field() != NULL) {
615 // Render
616 // "list_value": {
617 // "values": [ // Start this list
618 if (IsStructValue(*element()->parent_field())) {
619 Push("list_value", Item::MESSAGE, true, false);
620 Push("values", Item::MESSAGE, true, true);
621 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700622 }
Feng Xiaoe96ff302015-06-15 18:21:48 -0700623
Feng Xiaoe841bac2015-12-11 17:09:20 -0800624 // Render
625 // "values": [
626 if (IsStructListValue(*element()->parent_field())) {
627 // case iii above. Bind directly to g.p.ListValue
628 Push("values", Item::MESSAGE, true, true);
629 return this;
630 }
631 }
632
633 // Report an error.
634 InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
635 "') within a map."));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700636 return this;
637 }
638
Feng Xiaoe841bac2015-12-11 17:09:20 -0800639 // When name is empty and stack is not empty, we are rendering an item within
640 // a list.
641 if (name.empty()) {
642 if (element() != NULL && element()->parent_field() != NULL) {
643 if (IsStructValue(*element()->parent_field())) {
644 // Since it is g.p.Value, we bind directly to the list_value.
645 // Render
646 // { // g.p.Value item within the list
647 // "list_value": {
648 // "values": [
649 Push("", Item::MESSAGE, false, false);
650 Push("list_value", Item::MESSAGE, true, false);
651 Push("values", Item::MESSAGE, true, true);
652 return this;
653 }
654
655 if (IsStructListValue(*element()->parent_field())) {
656 // Since it is g.p.ListValue, we bind to it directly.
657 // Render
658 // { // g.p.ListValue item within the list
659 // "values": [
660 Push("", Item::MESSAGE, false, false);
661 Push("values", Item::MESSAGE, true, true);
662 return this;
663 }
664 }
665
666 // Pass the event to underlying ProtoWriter.
667 Push(name, Item::MESSAGE, false, true);
668 return this;
669 }
670
671 // name is not empty
672 const google::protobuf::Field* field = Lookup(name);
673 if (field == NULL) {
674 IncrementInvalidDepth();
675 return this;
676 }
677
678 if (IsStructValue(*field)) {
679 // If g.p.Value is repeated, start that list. Otherwise, start the
680 // "list_value" within it.
681 if (IsRepeated(*field)) {
682 // Render it just like a regular repeated field.
683 // "<name>": [
684 Push(name, Item::MESSAGE, false, true);
685 return this;
686 }
687
688 // Start the "list_value" field.
689 // Render
690 // "<name>": {
691 // "list_value": {
692 // "values": [
693 Push(name, Item::MESSAGE, false, false);
694 Push("list_value", Item::MESSAGE, true, false);
695 Push("values", Item::MESSAGE, true, true);
696 return this;
697 }
698
699 if (IsStructListValue(*field)) {
700 // If g.p.ListValue is repeated, start that list. Otherwise, start the
701 // "values" within it.
702 if (IsRepeated(*field)) {
703 // Render it just like a regular repeated field.
704 // "<name>": [
705 Push(name, Item::MESSAGE, false, true);
706 return this;
707 }
708
709 // Start the "values" field within g.p.ListValue.
710 // Render
711 // "<name>": {
712 // "values": [
713 Push(name, Item::MESSAGE, false, false);
714 Push("values", Item::MESSAGE, true, true);
715 return this;
716 }
717
718 // If we are here, the field should be repeated. Report an error otherwise.
719 if (!IsRepeated(*field)) {
720 IncrementInvalidDepth();
721 InvalidName(name, "Proto field is not repeating, cannot start list.");
722 return this;
723 }
724
725 if (IsMap(*field)) {
726 InvalidValue("Map",
727 StrCat("Cannot bind a list to map for field '", name, "'."));
728 IncrementInvalidDepth();
729 return this;
730 }
731
732 // Pass the event to ProtoWriter.
733 // Render
734 // "<name>": [
735 Push(name, Item::MESSAGE, false, true);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700736 return this;
737}
738
739ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800740 if (invalid_depth() > 0) {
741 DecrementInvalidDepth();
742 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700743 }
744
Feng Xiaoe841bac2015-12-11 17:09:20 -0800745 if (current_ == NULL) return this;
746
747 if (current_->IsAny()) {
748 current_->any()->EndList();
749 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700750 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800751
752 Pop();
Feng Xiaoe96ff302015-06-15 18:21:48 -0700753 return this;
754}
755
756Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
757 const DataPiece& data) {
758 string struct_field_name;
759 switch (data.type()) {
760 // Our JSON parser parses numbers as either int64, uint64, or double.
761 case DataPiece::TYPE_INT64:
762 case DataPiece::TYPE_UINT64:
763 case DataPiece::TYPE_DOUBLE: {
764 struct_field_name = "number_value";
765 break;
766 }
767 case DataPiece::TYPE_STRING: {
768 struct_field_name = "string_value";
769 break;
770 }
771 case DataPiece::TYPE_BOOL: {
772 struct_field_name = "bool_value";
773 break;
774 }
775 case DataPiece::TYPE_NULL: {
776 struct_field_name = "null_value";
777 break;
778 }
779 default: {
780 return Status(INVALID_ARGUMENT,
781 "Invalid struct data type. Only number, string, boolean or "
782 "null values are supported.");
783 }
784 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800785 ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700786 return Status::OK;
787}
788
789Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
790 const DataPiece& data) {
791 if (data.type() != DataPiece::TYPE_STRING) {
792 return Status(INVALID_ARGUMENT,
793 StrCat("Invalid data type for timestamp, value is ",
794 data.ValueAsStringOrDefault("")));
795 }
796
797 StringPiece value(data.str());
798
799 int64 seconds;
800 int32 nanos;
801 if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
802 &nanos)) {
803 return Status(INVALID_ARGUMENT, StrCat("Invalid time format: ", value));
804 }
805
806
Feng Xiaoe841bac2015-12-11 17:09:20 -0800807 ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
808 ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700809 return Status::OK;
810}
811
812static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
813 StringPiece path) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800814 ow->ProtoWriter::RenderDataPiece(
815 "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700816 return Status::OK;
817}
818
819Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
820 const DataPiece& data) {
821 if (data.type() != DataPiece::TYPE_STRING) {
822 return Status(INVALID_ARGUMENT,
823 StrCat("Invalid data type for field mask, value is ",
824 data.ValueAsStringOrDefault("")));
825 }
826
Feng Xiaoeee38b02015-08-22 18:25:48 -0700827// TODO(tsun): figure out how to do proto descriptor based snake case
828// conversions as much as possible. Because ToSnakeCase sometimes returns the
829// wrong value.
Feng Xiaoe96ff302015-06-15 18:21:48 -0700830 google::protobuf::scoped_ptr<ResultCallback1<util::Status, StringPiece> > callback(
Bo Yang7c14dc82015-09-15 18:25:02 -0700831 google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700832 return DecodeCompactFieldMaskPaths(data.str(), callback.get());
833}
834
835Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
836 const DataPiece& data) {
837 if (data.type() != DataPiece::TYPE_STRING) {
838 return Status(INVALID_ARGUMENT,
839 StrCat("Invalid data type for duration, value is ",
840 data.ValueAsStringOrDefault("")));
841 }
842
843 StringPiece value(data.str());
844
845 if (!value.ends_with("s")) {
846 return Status(INVALID_ARGUMENT,
847 "Illegal duration format; duration must end with 's'");
848 }
849 value = value.substr(0, value.size() - 1);
850 int sign = 1;
851 if (value.starts_with("-")) {
852 sign = -1;
853 value = value.substr(1);
854 }
855
856 StringPiece s_secs, s_nanos;
857 SplitSecondsAndNanos(value, &s_secs, &s_nanos);
858 uint64 unsigned_seconds;
859 if (!safe_strtou64(s_secs, &unsigned_seconds)) {
860 return Status(INVALID_ARGUMENT,
861 "Invalid duration format, failed to parse seconds");
862 }
863
Feng Xiaoe841bac2015-12-11 17:09:20 -0800864 int32 nanos = 0;
865 Status nanos_status = GetNanosFromStringPiece(
866 s_nanos, "Invalid duration format, failed to parse nano seconds",
867 "Duration value exceeds limits", &nanos);
868 if (!nanos_status.ok()) {
869 return nanos_status;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700870 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800871 nanos = sign * nanos;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700872
Feng Xiaoe96ff302015-06-15 18:21:48 -0700873 int64 seconds = sign * unsigned_seconds;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700874 if (seconds > kMaxSeconds || seconds < kMinSeconds ||
875 nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
876 return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
877 }
878
Feng Xiaoe841bac2015-12-11 17:09:20 -0800879 ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
880 ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
Feng Xiaoe96ff302015-06-15 18:21:48 -0700881 return Status::OK;
882}
883
884Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
885 const DataPiece& data) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800886 ow->ProtoWriter::RenderDataPiece("value", data);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700887 return Status::OK;
888}
889
890ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
891 StringPiece name, const DataPiece& data) {
892 Status status;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800893 if (invalid_depth() > 0) return this;
894
895 if (current_ == NULL) {
896 const TypeRenderer* type_renderer =
897 FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
898 if (type_renderer == NULL) {
899 InvalidName(name, "Root element must be a message.");
900 return this;
901 }
902 // Render the special type.
903 // "<name>": {
904 // ... Render special type ...
905 // }
906 ProtoWriter::StartObject(name);
907 status = (*type_renderer)(this, data);
908 if (!status.ok()) {
909 InvalidValue(master_type_.name(),
910 StrCat("Field '", name, "', ", status.error_message()));
911 }
912 ProtoWriter::EndObject();
913 return this;
914 }
915
916 if (current_->IsAny()) {
917 current_->any()->RenderDataPiece(name, data);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700918 return this;
919 }
920
921 const google::protobuf::Field* field = NULL;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800922 if (current_->IsMap()) {
923 if (!ValidMapKey(name)) return this;
924
925 // Render an item in repeated map list.
926 // { "key": "<name>", "value":
927 Push("", Item::MESSAGE, false, false);
928 ProtoWriter::RenderDataPiece("key", DataPiece(name));
929 field = Lookup("value");
Feng Xiaoe96ff302015-06-15 18:21:48 -0700930 if (field == NULL) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800931 GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
Feng Xiaoe96ff302015-06-15 18:21:48 -0700932 return this;
933 }
Feng Xiaoeee38b02015-08-22 18:25:48 -0700934
Feng Xiaoe841bac2015-12-11 17:09:20 -0800935 const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
936 if (type_renderer != NULL) {
937 // Map's value type is a special type. Render it like a message:
938 // "value": {
939 // ... Render special type ...
940 // }
941 Push("value", Item::MESSAGE, true, false);
942 status = (*type_renderer)(this, data);
943 if (!status.ok()) {
944 InvalidValue(field->type_url(),
945 StrCat("Field '", name, "', ", status.error_message()));
946 }
947 Pop();
948 return this;
949 }
Feng Xiaoeee38b02015-08-22 18:25:48 -0700950
Feng Xiaoe841bac2015-12-11 17:09:20 -0800951 // If we are rendering explicit null values and the backend proto field is
952 // not of the google.protobuf.NullType type, we do nothing.
953 if (data.type() == DataPiece::TYPE_NULL &&
954 field->type_url() != kStructNullValueTypeUrl) {
955 return this;
956 }
957
958 // Render the map value as a primitive type.
959 ProtoWriter::RenderDataPiece("value", data);
960 Pop();
961 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700962 }
963
Feng Xiaoe841bac2015-12-11 17:09:20 -0800964 field = Lookup(name);
965 if (field == NULL) return this;
966
967 // Check if the field is of special type. Render it accordingly if so.
968 const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
Feng Xiaoe96ff302015-06-15 18:21:48 -0700969 if (type_renderer != NULL) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800970 Push(name, Item::MESSAGE, false, false);
Feng Xiaoe96ff302015-06-15 18:21:48 -0700971 status = (*type_renderer)(this, data);
972 if (!status.ok()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800973 InvalidValue(field->type_url(),
Feng Xiaoe96ff302015-06-15 18:21:48 -0700974 StrCat("Field '", name, "', ", status.error_message()));
975 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800976 Pop();
Feng Xiaoe96ff302015-06-15 18:21:48 -0700977 return this;
978 }
979
Feng Xiaoe841bac2015-12-11 17:09:20 -0800980 // If we are rendering explicit null values and the backend proto field is
981 // not of the google.protobuf.NullType type, we do nothing.
Feng Xiaoe96ff302015-06-15 18:21:48 -0700982 if (data.type() == DataPiece::TYPE_NULL &&
Feng Xiaoe841bac2015-12-11 17:09:20 -0800983 field->type_url() != kStructNullValueTypeUrl) {
984 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700985 }
986
Feng Xiaoe841bac2015-12-11 17:09:20 -0800987 ProtoWriter::RenderDataPiece(name, data);
988 return this;
Feng Xiaoe96ff302015-06-15 18:21:48 -0700989}
990
991// Map of functions that are responsible for rendering well known type
992// represented by the key.
993hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
Jisi Liu09778152015-08-25 22:01:12 -0700994 ProtoStreamObjectWriter::renderers_ = NULL;
995GOOGLE_PROTOBUF_DECLARE_ONCE(writer_renderers_init_);
996
997void ProtoStreamObjectWriter::InitRendererMap() {
998 renderers_ = new hash_map<string, ProtoStreamObjectWriter::TypeRenderer>();
999 (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001000 &ProtoStreamObjectWriter::RenderTimestamp;
Jisi Liu09778152015-08-25 22:01:12 -07001001 (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001002 &ProtoStreamObjectWriter::RenderDuration;
Jisi Liu09778152015-08-25 22:01:12 -07001003 (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001004 &ProtoStreamObjectWriter::RenderFieldMask;
Jisi Liu09778152015-08-25 22:01:12 -07001005 (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001006 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001007 (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001008 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001009 (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001010 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001011 (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001012 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001013 (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001014 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001015 (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001016 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001017 (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001018 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001019 (*renderers_)["type.googleapis.com/google.protobuf.String"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001020 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001021 (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001022 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001023 (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001024 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001025 (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001026 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001027 (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001028 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001029 (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001030 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001031 (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001032 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001033 (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001034 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001035 (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001036 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001037 (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001038 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001039 (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001040 &ProtoStreamObjectWriter::RenderWrapperType;
Jisi Liu09778152015-08-25 22:01:12 -07001041 (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
Feng Xiaoe96ff302015-06-15 18:21:48 -07001042 &ProtoStreamObjectWriter::RenderStructValue;
Jisi Liu09778152015-08-25 22:01:12 -07001043 ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
1044}
1045
1046void ProtoStreamObjectWriter::DeleteRendererMap() {
1047 delete ProtoStreamObjectWriter::renderers_;
1048 renderers_ = NULL;
Feng Xiaoe96ff302015-06-15 18:21:48 -07001049}
1050
1051ProtoStreamObjectWriter::TypeRenderer*
1052ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
Jisi Liu09778152015-08-25 22:01:12 -07001053 ::google::protobuf::GoogleOnceInit(&writer_renderers_init_, &InitRendererMap);
1054 return FindOrNull(*renderers_, type_url);
Feng Xiaoe96ff302015-06-15 18:21:48 -07001055}
1056
Jisi Liu46e8ff62015-10-05 11:59:43 -07001057bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001058 if (current_ == NULL) return true;
Jisi Liu46e8ff62015-10-05 11:59:43 -07001059
Feng Xiaoe841bac2015-12-11 17:09:20 -08001060 if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
1061 listener()->InvalidName(
1062 location(), unnormalized_name,
Jisi Liu46e8ff62015-10-05 11:59:43 -07001063 StrCat("Repeated map key: '", unnormalized_name, "' is already set."));
1064 return false;
1065 }
1066
1067 return true;
1068}
1069
Feng Xiaoe841bac2015-12-11 17:09:20 -08001070void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type,
1071 bool is_placeholder, bool is_list) {
1072 is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
1073
1074 // invalid_depth == 0 means it is a successful StartObject or StartList.
1075 if (invalid_depth() == 0)
1076 current_.reset(
1077 new Item(current_.release(), item_type, is_placeholder, is_list));
Feng Xiaoe96ff302015-06-15 18:21:48 -07001078}
1079
Feng Xiaoe841bac2015-12-11 17:09:20 -08001080void ProtoStreamObjectWriter::Pop() {
1081 // Pop all placeholder items sending StartObject or StartList events to
1082 // ProtoWriter according to is_list value.
1083 while (current_ != NULL && current_->is_placeholder()) {
1084 PopOneElement();
Feng Xiaoe96ff302015-06-15 18:21:48 -07001085 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001086 if (current_ != NULL) {
1087 PopOneElement();
Feng Xiaoe96ff302015-06-15 18:21:48 -07001088 }
Feng Xiaoe96ff302015-06-15 18:21:48 -07001089}
1090
Feng Xiaoe841bac2015-12-11 17:09:20 -08001091void ProtoStreamObjectWriter::PopOneElement() {
1092 current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
1093 current_.reset(current_->pop<Item>());
Feng Xiaoe96ff302015-06-15 18:21:48 -07001094}
1095
1096bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
1097 if (field.type_url().empty() ||
1098 field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE ||
1099 field.cardinality() !=
1100 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
1101 return false;
1102 }
1103 const google::protobuf::Type* field_type =
Feng Xiaoe841bac2015-12-11 17:09:20 -08001104 typeinfo()->GetTypeByTypeUrl(field.type_url());
Feng Xiaoe96ff302015-06-15 18:21:48 -07001105
Feng Xiaobd111982015-08-11 15:07:12 -07001106 // TODO(xiaofeng): Unify option names.
Feng Xiaoe96ff302015-06-15 18:21:48 -07001107 return GetBoolOptionOrDefault(field_type->options(),
Feng Xiaobd111982015-08-11 15:07:12 -07001108 "google.protobuf.MessageOptions.map_entry", false) ||
1109 GetBoolOptionOrDefault(field_type->options(), "map_entry", false);
Feng Xiaoe96ff302015-06-15 18:21:48 -07001110}
1111
Feng Xiaoe841bac2015-12-11 17:09:20 -08001112bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
1113 return GetTypeWithoutUrl(field.type_url()) == kAnyType;
Feng Xiaoe96ff302015-06-15 18:21:48 -07001114}
1115
Feng Xiaoe841bac2015-12-11 17:09:20 -08001116bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
1117 return GetTypeWithoutUrl(field.type_url()) == kStructType;
1118}
1119
1120bool ProtoStreamObjectWriter::IsStructValue(
1121 const google::protobuf::Field& field) {
1122 return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
1123}
1124
1125bool ProtoStreamObjectWriter::IsStructListValue(
1126 const google::protobuf::Field& field) {
1127 return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
1128}
Jisi Liu46e8ff62015-10-05 11:59:43 -07001129
Feng Xiaoe96ff302015-06-15 18:21:48 -07001130} // namespace converter
1131} // namespace util
1132} // namespace protobuf
1133} // namespace google