1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include <google/protobuf/util/internal/protostream_objectwriter.h>
32
33 #include <functional>
34 #include <stack>
35
36 #include <google/protobuf/stubs/once.h>
37 #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
48 namespace google {
49 namespace protobuf {
50 namespace util {
51 namespace converter {
52
53 using google::protobuf::internal::WireFormatLite;
54 using util::error::INVALID_ARGUMENT;
55 using util::Status;
56 using util::StatusOr;
57
58
ProtoStreamObjectWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)59 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
60 TypeResolver* type_resolver, const google::protobuf::Type& type,
61 strings::ByteSink* output, ErrorListener* listener,
62 const ProtoStreamObjectWriter::Options& options)
63 : ProtoWriter(type_resolver, type, output, listener),
64 master_type_(type),
65 current_(NULL),
66 options_(options) {
67 set_ignore_unknown_fields(options_.ignore_unknown_fields);
68 }
69
ProtoStreamObjectWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)70 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
71 const TypeInfo* typeinfo, const google::protobuf::Type& type,
72 strings::ByteSink* output, ErrorListener* listener)
73 : ProtoWriter(typeinfo, type, output, listener),
74 master_type_(type),
75 current_(NULL),
76 options_(ProtoStreamObjectWriter::Options::Defaults()) {}
77
~ProtoStreamObjectWriter()78 ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
79 if (current_ == NULL) return;
80 // Cleanup explicitly in order to avoid destructor stack overflow when input
81 // is deeply nested.
82 // Cast to BaseElement to avoid doing additional checks (like missing fields)
83 // during pop().
84 google::protobuf::scoped_ptr<BaseElement> element(
85 static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
86 while (element != NULL) {
87 element.reset(element->pop<BaseElement>());
88 }
89 }
90
91 namespace {
92 // Utility method to split a string representation of Timestamp or Duration and
93 // return the parts.
SplitSecondsAndNanos(StringPiece input,StringPiece * seconds,StringPiece * nanos)94 void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
95 StringPiece* nanos) {
96 size_t idx = input.rfind('.');
97 if (idx != string::npos) {
98 *seconds = input.substr(0, idx);
99 *nanos = input.substr(idx + 1);
100 } else {
101 *seconds = input;
102 *nanos = StringPiece();
103 }
104 }
105
GetNanosFromStringPiece(StringPiece s_nanos,const char * parse_failure_message,const char * exceeded_limit_message,int32 * nanos)106 Status GetNanosFromStringPiece(StringPiece s_nanos,
107 const char* parse_failure_message,
108 const char* exceeded_limit_message,
109 int32* nanos) {
110 *nanos = 0;
111
112 // Count the number of leading 0s and consume them.
113 int num_leading_zeros = 0;
114 while (s_nanos.Consume("0")) {
115 num_leading_zeros++;
116 }
117 int32 i_nanos = 0;
118 // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
119 // "0." + s_nanos.ToString() seconds. An int32 is used for the
120 // conversion to 'nanos', rather than a double, so that there is no
121 // loss of precision.
122 if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) {
123 return Status(INVALID_ARGUMENT, parse_failure_message);
124 }
125 if (i_nanos > kNanosPerSecond || i_nanos < 0) {
126 return Status(INVALID_ARGUMENT, exceeded_limit_message);
127 }
128 // s_nanos should only have digits. No whitespace.
129 if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
130 return Status(INVALID_ARGUMENT, parse_failure_message);
131 }
132
133 if (i_nanos > 0) {
134 // 'scale' is the number of digits to the right of the decimal
135 // point in "0." + s_nanos.ToString()
136 int32 scale = num_leading_zeros + s_nanos.size();
137 // 'conversion' converts i_nanos into nanoseconds.
138 // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
139 // For efficiency, we precompute the conversion factor.
140 int32 conversion = 0;
141 switch (scale) {
142 case 1:
143 conversion = 100000000;
144 break;
145 case 2:
146 conversion = 10000000;
147 break;
148 case 3:
149 conversion = 1000000;
150 break;
151 case 4:
152 conversion = 100000;
153 break;
154 case 5:
155 conversion = 10000;
156 break;
157 case 6:
158 conversion = 1000;
159 break;
160 case 7:
161 conversion = 100;
162 break;
163 case 8:
164 conversion = 10;
165 break;
166 case 9:
167 conversion = 1;
168 break;
169 default:
170 return Status(INVALID_ARGUMENT, exceeded_limit_message);
171 }
172 *nanos = i_nanos * conversion;
173 }
174
175 return Status::OK;
176 }
177
178 } // namespace
179
AnyWriter(ProtoStreamObjectWriter * parent)180 ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
181 : parent_(parent),
182 ow_(),
183 invalid_(false),
184 data_(),
185 output_(&data_),
186 depth_(0),
187 is_well_known_type_(false),
188 well_known_type_render_(NULL) {}
189
~AnyWriter()190 ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
191
StartObject(StringPiece name)192 void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
193 ++depth_;
194 // If an object writer is absent, that means we have not called StartAny()
195 // before reaching here. This is an invalid state. StartAny() gets called
196 // whenever we see an "@type" being rendered (see AnyWriter::RenderDataPiece).
197 if (ow_ == NULL) {
198 // Make sure we are not already in an invalid state. This avoids making
199 // multiple unnecessary InvalidValue calls.
200 if (!invalid_) {
201 parent_->InvalidValue("Any",
202 StrCat("Missing or invalid @type for any field in ",
203 parent_->master_type_.name()));
204 invalid_ = true;
205 }
206 } else if (is_well_known_type_ && depth_ == 1) {
207 // For well-known types, the only other field besides "@type" should be a
208 // "value" field.
209 if (name != "value" && !invalid_) {
210 parent_->InvalidValue("Any",
211 "Expect a \"value\" field for well-known types.");
212 invalid_ = true;
213 }
214 ow_->StartObject("");
215 } else {
216 // Forward the call to the child writer if:
217 // 1. the type is not a well-known type.
218 // 2. or, we are in a nested Any, Struct, or Value object.
219 ow_->StartObject(name);
220 }
221 }
222
EndObject()223 bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
224 --depth_;
225 // As long as depth_ >= 0, we know we haven't reached the end of Any.
226 // Propagate these EndObject() calls to the contained ow_. For regular
227 // message types, we propagate the end of Any as well.
228 if (ow_ != NULL && (depth_ >= 0 || !is_well_known_type_)) {
229 ow_->EndObject();
230 }
231 // A negative depth_ implies that we have reached the end of Any
232 // object. Now we write out its contents.
233 if (depth_ < 0) {
234 WriteAny();
235 return false;
236 }
237 return true;
238 }
239
StartList(StringPiece name)240 void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
241 ++depth_;
242 // We expect ow_ to be present as this call only makes sense inside an Any.
243 if (ow_ == NULL) {
244 if (!invalid_) {
245 parent_->InvalidValue("Any",
246 StrCat("Missing or invalid @type for any field in ",
247 parent_->master_type_.name()));
248 invalid_ = true;
249 }
250 } else if (is_well_known_type_ && depth_ == 1) {
251 if (name != "value" && !invalid_) {
252 parent_->InvalidValue("Any",
253 "Expect a \"value\" field for well-known types.");
254 invalid_ = true;
255 }
256 ow_->StartList("");
257 } else {
258 ow_->StartList(name);
259 }
260 }
261
EndList()262 void ProtoStreamObjectWriter::AnyWriter::EndList() {
263 --depth_;
264 if (depth_ < 0) {
265 GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
266 depth_ = 0;
267 }
268 // We don't write an error on the close, only on the open
269 if (ow_ != NULL) {
270 ow_->EndList();
271 }
272 }
273
RenderDataPiece(StringPiece name,const DataPiece & value)274 void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
275 StringPiece name, const DataPiece& value) {
276 // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
277 // should go to the contained ow_ as they indicate nested Anys.
278 if (depth_ == 0 && ow_ == NULL && name == "@type") {
279 StartAny(value);
280 } else if (ow_ == NULL) {
281 if (!invalid_) {
282 parent_->InvalidValue("Any",
283 StrCat("Missing or invalid @type for any field in ",
284 parent_->master_type_.name()));
285 invalid_ = true;
286 }
287 } else if (depth_ == 0 && is_well_known_type_) {
288 if (name != "value" && !invalid_) {
289 parent_->InvalidValue("Any",
290 "Expect a \"value\" field for well-known types.");
291 invalid_ = true;
292 }
293 if (well_known_type_render_ == NULL) {
294 // Only Any and Struct don't have a special type render but both of
295 // them expect a JSON object (i.e., a StartObject() call).
296 if (!invalid_) {
297 parent_->InvalidValue("Any", "Expect a JSON object.");
298 invalid_ = true;
299 }
300 } else {
301 ow_->ProtoWriter::StartObject("");
302 Status status = (*well_known_type_render_)(ow_.get(), value);
303 if (!status.ok()) ow_->InvalidValue("Any", status.error_message());
304 ow_->ProtoWriter::EndObject();
305 }
306 } else {
307 ow_->RenderDataPiece(name, value);
308 }
309 }
310
StartAny(const DataPiece & value)311 void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
312 // Figure out the type url. This is a copy-paste from WriteString but we also
313 // need the value, so we can't just call through to that.
314 if (value.type() == DataPiece::TYPE_STRING) {
315 type_url_ = value.str().ToString();
316 } else {
317 StatusOr<string> s = value.ToString();
318 if (!s.ok()) {
319 parent_->InvalidValue("String", s.status().error_message());
320 invalid_ = true;
321 return;
322 }
323 type_url_ = s.ValueOrDie();
324 }
325 // Resolve the type url, and report an error if we failed to resolve it.
326 StatusOr<const google::protobuf::Type*> resolved_type =
327 parent_->typeinfo()->ResolveTypeUrl(type_url_);
328 if (!resolved_type.ok()) {
329 parent_->InvalidValue("Any", resolved_type.status().error_message());
330 invalid_ = true;
331 return;
332 }
333 // At this point, type is never null.
334 const google::protobuf::Type* type = resolved_type.ValueOrDie();
335
336 well_known_type_render_ = FindTypeRenderer(type_url_);
337 if (well_known_type_render_ != NULL ||
338 // Explicitly list Any and Struct here because they don't have a
339 // custom renderer.
340 type->name() == kAnyType || type->name() == kStructType) {
341 is_well_known_type_ = true;
342 }
343
344 // Create our object writer and initialize it with the first StartObject
345 // call.
346 ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
347 parent_->listener()));
348
349 // Don't call StartObject() for well-known types yet. Depending on the
350 // type of actual data, we may not need to call StartObject(). For
351 // example:
352 // {
353 // "@type": "type.googleapis.com/google.protobuf.Value",
354 // "value": [1, 2, 3],
355 // }
356 // With the above JSON representation, we will only call StartList() on the
357 // contained ow_.
358 if (!is_well_known_type_) {
359 ow_->StartObject("");
360 }
361 }
362
WriteAny()363 void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
364 if (ow_ == NULL) {
365 // If we had no object writer, we never got any content, so just return
366 // immediately, which is equivalent to writing an empty Any.
367 return;
368 }
369 // Render the type_url and value fields directly to the stream.
370 // type_url has tag 1 and value has tag 2.
371 WireFormatLite::WriteString(1, type_url_, parent_->stream());
372 if (!data_.empty()) {
373 WireFormatLite::WriteBytes(2, data_, parent_->stream());
374 }
375 }
376
Item(ProtoStreamObjectWriter * enclosing,ItemType item_type,bool is_placeholder,bool is_list)377 ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
378 ItemType item_type, bool is_placeholder,
379 bool is_list)
380 : BaseElement(NULL),
381 ow_(enclosing),
382 any_(),
383 item_type_(item_type),
384 is_placeholder_(is_placeholder),
385 is_list_(is_list) {
386 if (item_type_ == ANY) {
387 any_.reset(new AnyWriter(ow_));
388 }
389 if (item_type == MAP) {
390 map_keys_.reset(new hash_set<string>);
391 }
392 }
393
Item(ProtoStreamObjectWriter::Item * parent,ItemType item_type,bool is_placeholder,bool is_list)394 ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
395 ItemType item_type, bool is_placeholder,
396 bool is_list)
397 : BaseElement(parent),
398 ow_(this->parent()->ow_),
399 any_(),
400 item_type_(item_type),
401 is_placeholder_(is_placeholder),
402 is_list_(is_list) {
403 if (item_type == ANY) {
404 any_.reset(new AnyWriter(ow_));
405 }
406 if (item_type == MAP) {
407 map_keys_.reset(new hash_set<string>);
408 }
409 }
410
InsertMapKeyIfNotPresent(StringPiece map_key)411 bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
412 StringPiece map_key) {
413 return InsertIfNotPresent(map_keys_.get(), map_key.ToString());
414 }
415
StartObject(StringPiece name)416 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
417 StringPiece name) {
418 if (invalid_depth() > 0) {
419 IncrementInvalidDepth();
420 return this;
421 }
422
423 // Starting the root message. Create the root Item and return.
424 // ANY message type does not need special handling, just set the ItemType
425 // to ANY.
426 if (current_ == NULL) {
427 ProtoWriter::StartObject(name);
428 current_.reset(new Item(
429 this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
430 false, false));
431
432 // If master type is a special type that needs extra values to be written to
433 // stream, we write those values.
434 if (master_type_.name() == kStructType) {
435 // Struct has a map<string, Value> field called "fields".
436 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
437 // "fields": [
438 Push("fields", Item::MAP, true, true);
439 return this;
440 }
441
442 if (master_type_.name() == kStructValueType) {
443 // We got a StartObject call with google.protobuf.Value field. The only
444 // object within that type is a struct type. So start a struct.
445 //
446 // The struct field in Value type is named "struct_value"
447 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
448 // Also start the map field "fields" within the struct.
449 // "struct_value": {
450 // "fields": [
451 Push("struct_value", Item::MESSAGE, true, false);
452 Push("fields", Item::MAP, true, true);
453 return this;
454 }
455
456 if (master_type_.name() == kStructListValueType) {
457 InvalidValue(kStructListValueType,
458 "Cannot start root message with ListValue.");
459 }
460
461 return this;
462 }
463
464 // Send all ANY events to AnyWriter.
465 if (current_->IsAny()) {
466 current_->any()->StartObject(name);
467 return this;
468 }
469
470 // If we are within a map, we render name as keys and send StartObject to the
471 // value field.
472 if (current_->IsMap()) {
473 if (!ValidMapKey(name)) {
474 IncrementInvalidDepth();
475 return this;
476 }
477
478 // Map is a repeated field of message type with a "key" and a "value" field.
479 // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
480 // message MapFieldEntry {
481 // key_type key = 1;
482 // value_type value = 2;
483 // }
484 //
485 // repeated MapFieldEntry map_field = N;
486 //
487 // That means, we render the following element within a list (hence no
488 // name):
489 // { "key": "<name>", "value": {
490 Push("", Item::MESSAGE, false, false);
491 ProtoWriter::RenderDataPiece("key",
492 DataPiece(name, use_strict_base64_decoding()));
493 Push("value", Item::MESSAGE, true, false);
494
495 // Make sure we are valid so far after starting map fields.
496 if (invalid_depth() > 0) return this;
497
498 // If top of stack is g.p.Struct type, start the struct the map field within
499 // it.
500 if (element() != NULL && IsStruct(*element()->parent_field())) {
501 // Render "fields": [
502 Push("fields", Item::MAP, true, true);
503 return this;
504 }
505
506 // If top of stack is g.p.Value type, start the Struct within it.
507 if (element() != NULL && IsStructValue(*element()->parent_field())) {
508 // Render
509 // "struct_value": {
510 // "fields": [
511 Push("struct_value", Item::MESSAGE, true, false);
512 Push("fields", Item::MAP, true, true);
513 }
514 return this;
515 }
516
517 const google::protobuf::Field* field = BeginNamed(name, false);
518 if (field == NULL) return this;
519
520 if (IsStruct(*field)) {
521 // Start a struct object.
522 // Render
523 // "<name>": {
524 // "fields": {
525 Push(name, Item::MESSAGE, false, false);
526 Push("fields", Item::MAP, true, true);
527 return this;
528 }
529
530 if (IsStructValue(*field)) {
531 // We got a StartObject call with google.protobuf.Value field. The only
532 // object within that type is a struct type. So start a struct.
533 // Render
534 // "<name>": {
535 // "struct_value": {
536 // "fields": {
537 Push(name, Item::MESSAGE, false, false);
538 Push("struct_value", Item::MESSAGE, true, false);
539 Push("fields", Item::MAP, true, true);
540 return this;
541 }
542
543 if (IsMap(*field)) {
544 // Begin a map. A map is triggered by a StartObject() call if the current
545 // field has a map type.
546 // A map type is always repeated, hence set is_list to true.
547 // Render
548 // "<name>": [
549 Push(name, Item::MAP, false, true);
550 return this;
551 }
552
553 // A regular message type. Pass it directly to ProtoWriter.
554 // Render
555 // "<name>": {
556 Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
557 return this;
558 }
559
EndObject()560 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
561 if (invalid_depth() > 0) {
562 DecrementInvalidDepth();
563 return this;
564 }
565
566 if (current_ == NULL) return this;
567
568 if (current_->IsAny()) {
569 if (current_->any()->EndObject()) return this;
570 }
571
572 Pop();
573
574 return this;
575 }
576
StartList(StringPiece name)577 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
578 if (invalid_depth() > 0) {
579 IncrementInvalidDepth();
580 return this;
581 }
582
583 // Since we cannot have a top-level repeated item in protobuf, the only way
584 // this is valid is if we start a special type google.protobuf.ListValue or
585 // google.protobuf.Value.
586 if (current_ == NULL) {
587 if (!name.empty()) {
588 InvalidName(name, "Root element should not be named.");
589 IncrementInvalidDepth();
590 return this;
591 }
592
593 // If master type is a special type that needs extra values to be written to
594 // stream, we write those values.
595 if (master_type_.name() == kStructValueType) {
596 // We got a StartList with google.protobuf.Value master type. This means
597 // we have to start the "list_value" within google.protobuf.Value.
598 //
599 // See
600 // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
601 //
602 // Render
603 // "<name>": {
604 // "list_value": {
605 // "values": [ // Start this list.
606 ProtoWriter::StartObject(name);
607 current_.reset(new Item(this, Item::MESSAGE, false, false));
608 Push("list_value", Item::MESSAGE, true, false);
609 Push("values", Item::MESSAGE, true, true);
610 return this;
611 }
612
613 if (master_type_.name() == kStructListValueType) {
614 // We got a StartList with google.protobuf.ListValue master type. This
615 // means we have to start the "values" within google.protobuf.ListValue.
616 //
617 // Render
618 // "<name>": {
619 // "values": [ // Start this list.
620 ProtoWriter::StartObject(name);
621 current_.reset(new Item(this, Item::MESSAGE, false, false));
622 Push("values", Item::MESSAGE, true, true);
623 return this;
624 }
625
626 // Send the event to ProtoWriter so proper errors can be reported.
627 //
628 // Render a regular list:
629 // "<name>": [
630 ProtoWriter::StartList(name);
631 current_.reset(new Item(this, Item::MESSAGE, false, true));
632 return this;
633 }
634
635 if (current_->IsAny()) {
636 current_->any()->StartList(name);
637 return this;
638 }
639
640 // If the top of stack is a map, we are starting a list value within a map.
641 // Since map does not allow repeated values, this can only happen when the map
642 // value is of a special type that renders a list in JSON. These can be one
643 // of 3 cases:
644 // i. We are rendering a list value within google.protobuf.Struct
645 // ii. We are rendering a list value within google.protobuf.Value
646 // iii. We are rendering a list value with type google.protobuf.ListValue.
647 if (current_->IsMap()) {
648 if (!ValidMapKey(name)) {
649 IncrementInvalidDepth();
650 return this;
651 }
652
653 // Start the repeated map entry object.
654 // Render
655 // { "key": "<name>", "value": {
656 Push("", Item::MESSAGE, false, false);
657 ProtoWriter::RenderDataPiece("key",
658 DataPiece(name, use_strict_base64_decoding()));
659 Push("value", Item::MESSAGE, true, false);
660
661 // Make sure we are valid after pushing all above items.
662 if (invalid_depth() > 0) return this;
663
664 // case i and ii above. Start "list_value" field within g.p.Value
665 if (element() != NULL && element()->parent_field() != NULL) {
666 // Render
667 // "list_value": {
668 // "values": [ // Start this list
669 if (IsStructValue(*element()->parent_field())) {
670 Push("list_value", Item::MESSAGE, true, false);
671 Push("values", Item::MESSAGE, true, true);
672 return this;
673 }
674
675 // Render
676 // "values": [
677 if (IsStructListValue(*element()->parent_field())) {
678 // case iii above. Bind directly to g.p.ListValue
679 Push("values", Item::MESSAGE, true, true);
680 return this;
681 }
682 }
683
684 // Report an error.
685 InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
686 "') within a map."));
687 return this;
688 }
689
690 // When name is empty and stack is not empty, we are rendering an item within
691 // a list.
692 if (name.empty()) {
693 if (element() != NULL && element()->parent_field() != NULL) {
694 if (IsStructValue(*element()->parent_field())) {
695 // Since it is g.p.Value, we bind directly to the list_value.
696 // Render
697 // { // g.p.Value item within the list
698 // "list_value": {
699 // "values": [
700 Push("", Item::MESSAGE, false, false);
701 Push("list_value", Item::MESSAGE, true, false);
702 Push("values", Item::MESSAGE, true, true);
703 return this;
704 }
705
706 if (IsStructListValue(*element()->parent_field())) {
707 // Since it is g.p.ListValue, we bind to it directly.
708 // Render
709 // { // g.p.ListValue item within the list
710 // "values": [
711 Push("", Item::MESSAGE, false, false);
712 Push("values", Item::MESSAGE, true, true);
713 return this;
714 }
715 }
716
717 // Pass the event to underlying ProtoWriter.
718 Push(name, Item::MESSAGE, false, true);
719 return this;
720 }
721
722 // name is not empty
723 const google::protobuf::Field* field = Lookup(name);
724 if (field == NULL) {
725 IncrementInvalidDepth();
726 return this;
727 }
728
729 if (IsStructValue(*field)) {
730 // If g.p.Value is repeated, start that list. Otherwise, start the
731 // "list_value" within it.
732 if (IsRepeated(*field)) {
733 // Render it just like a regular repeated field.
734 // "<name>": [
735 Push(name, Item::MESSAGE, false, true);
736 return this;
737 }
738
739 // Start the "list_value" field.
740 // Render
741 // "<name>": {
742 // "list_value": {
743 // "values": [
744 Push(name, Item::MESSAGE, false, false);
745 Push("list_value", Item::MESSAGE, true, false);
746 Push("values", Item::MESSAGE, true, true);
747 return this;
748 }
749
750 if (IsStructListValue(*field)) {
751 // If g.p.ListValue is repeated, start that list. Otherwise, start the
752 // "values" within it.
753 if (IsRepeated(*field)) {
754 // Render it just like a regular repeated field.
755 // "<name>": [
756 Push(name, Item::MESSAGE, false, true);
757 return this;
758 }
759
760 // Start the "values" field within g.p.ListValue.
761 // Render
762 // "<name>": {
763 // "values": [
764 Push(name, Item::MESSAGE, false, false);
765 Push("values", Item::MESSAGE, true, true);
766 return this;
767 }
768
769 // If we are here, the field should be repeated. Report an error otherwise.
770 if (!IsRepeated(*field)) {
771 IncrementInvalidDepth();
772 InvalidName(name, "Proto field is not repeating, cannot start list.");
773 return this;
774 }
775
776 if (IsMap(*field)) {
777 InvalidValue("Map",
778 StrCat("Cannot bind a list to map for field '", name, "'."));
779 IncrementInvalidDepth();
780 return this;
781 }
782
783 // Pass the event to ProtoWriter.
784 // Render
785 // "<name>": [
786 Push(name, Item::MESSAGE, false, true);
787 return this;
788 }
789
EndList()790 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
791 if (invalid_depth() > 0) {
792 DecrementInvalidDepth();
793 return this;
794 }
795
796 if (current_ == NULL) return this;
797
798 if (current_->IsAny()) {
799 current_->any()->EndList();
800 return this;
801 }
802
803 Pop();
804 return this;
805 }
806
RenderStructValue(ProtoStreamObjectWriter * ow,const DataPiece & data)807 Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
808 const DataPiece& data) {
809 string struct_field_name;
810 switch (data.type()) {
811 // Our JSON parser parses numbers as either int64, uint64, or double.
812 case DataPiece::TYPE_INT64: {
813 // If the option to treat integers as strings is set, then render them as
814 // strings. Otherwise, fallback to rendering them as double.
815 if (ow->options_.struct_integers_as_strings) {
816 StatusOr<int64> int_value = data.ToInt64();
817 if (int_value.ok()) {
818 ow->ProtoWriter::RenderDataPiece(
819 "string_value",
820 DataPiece(SimpleItoa(int_value.ValueOrDie()), true));
821 return Status::OK;
822 }
823 }
824 struct_field_name = "number_value";
825 break;
826 }
827 case DataPiece::TYPE_UINT64: {
828 // If the option to treat integers as strings is set, then render them as
829 // strings. Otherwise, fallback to rendering them as double.
830 if (ow->options_.struct_integers_as_strings) {
831 StatusOr<uint64> int_value = data.ToUint64();
832 if (int_value.ok()) {
833 ow->ProtoWriter::RenderDataPiece(
834 "string_value",
835 DataPiece(SimpleItoa(int_value.ValueOrDie()), true));
836 return Status::OK;
837 }
838 }
839 struct_field_name = "number_value";
840 break;
841 }
842 case DataPiece::TYPE_DOUBLE: {
843 struct_field_name = "number_value";
844 break;
845 }
846 case DataPiece::TYPE_STRING: {
847 struct_field_name = "string_value";
848 break;
849 }
850 case DataPiece::TYPE_BOOL: {
851 struct_field_name = "bool_value";
852 break;
853 }
854 case DataPiece::TYPE_NULL: {
855 struct_field_name = "null_value";
856 break;
857 }
858 default: {
859 return Status(INVALID_ARGUMENT,
860 "Invalid struct data type. Only number, string, boolean or "
861 "null values are supported.");
862 }
863 }
864 ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
865 return Status::OK;
866 }
867
RenderTimestamp(ProtoStreamObjectWriter * ow,const DataPiece & data)868 Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
869 const DataPiece& data) {
870 if (data.type() != DataPiece::TYPE_STRING) {
871 return Status(INVALID_ARGUMENT,
872 StrCat("Invalid data type for timestamp, value is ",
873 data.ValueAsStringOrDefault("")));
874 }
875
876 StringPiece value(data.str());
877
878 int64 seconds;
879 int32 nanos;
880 if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
881 &nanos)) {
882 return Status(INVALID_ARGUMENT, StrCat("Invalid time format: ", value));
883 }
884
885
886 ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
887 ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
888 return Status::OK;
889 }
890
RenderOneFieldPath(ProtoStreamObjectWriter * ow,StringPiece path)891 static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
892 StringPiece path) {
893 ow->ProtoWriter::RenderDataPiece(
894 "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true));
895 return Status::OK;
896 }
897
RenderFieldMask(ProtoStreamObjectWriter * ow,const DataPiece & data)898 Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
899 const DataPiece& data) {
900 if (data.type() != DataPiece::TYPE_STRING) {
901 return Status(INVALID_ARGUMENT,
902 StrCat("Invalid data type for field mask, value is ",
903 data.ValueAsStringOrDefault("")));
904 }
905
906 // TODO(tsun): figure out how to do proto descriptor based snake case
907 // conversions as much as possible. Because ToSnakeCase sometimes returns the
908 // wrong value.
909 google::protobuf::scoped_ptr<ResultCallback1<util::Status, StringPiece> > callback(
910 ::google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow));
911 return DecodeCompactFieldMaskPaths(data.str(), callback.get());
912 }
913
RenderDuration(ProtoStreamObjectWriter * ow,const DataPiece & data)914 Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
915 const DataPiece& data) {
916 if (data.type() != DataPiece::TYPE_STRING) {
917 return Status(INVALID_ARGUMENT,
918 StrCat("Invalid data type for duration, value is ",
919 data.ValueAsStringOrDefault("")));
920 }
921
922 StringPiece value(data.str());
923
924 if (!value.ends_with("s")) {
925 return Status(INVALID_ARGUMENT,
926 "Illegal duration format; duration must end with 's'");
927 }
928 value = value.substr(0, value.size() - 1);
929 int sign = 1;
930 if (value.starts_with("-")) {
931 sign = -1;
932 value = value.substr(1);
933 }
934
935 StringPiece s_secs, s_nanos;
936 SplitSecondsAndNanos(value, &s_secs, &s_nanos);
937 uint64 unsigned_seconds;
938 if (!safe_strtou64(s_secs, &unsigned_seconds)) {
939 return Status(INVALID_ARGUMENT,
940 "Invalid duration format, failed to parse seconds");
941 }
942
943 int32 nanos = 0;
944 Status nanos_status = GetNanosFromStringPiece(
945 s_nanos, "Invalid duration format, failed to parse nano seconds",
946 "Duration value exceeds limits", &nanos);
947 if (!nanos_status.ok()) {
948 return nanos_status;
949 }
950 nanos = sign * nanos;
951
952 int64 seconds = sign * unsigned_seconds;
953 if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
954 nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
955 return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
956 }
957
958 ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
959 ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
960 return Status::OK;
961 }
962
RenderWrapperType(ProtoStreamObjectWriter * ow,const DataPiece & data)963 Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
964 const DataPiece& data) {
965 ow->ProtoWriter::RenderDataPiece("value", data);
966 return Status::OK;
967 }
968
RenderDataPiece(StringPiece name,const DataPiece & data)969 ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
970 StringPiece name, const DataPiece& data) {
971 Status status;
972 if (invalid_depth() > 0) return this;
973
974 if (current_ == NULL) {
975 const TypeRenderer* type_renderer =
976 FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
977 if (type_renderer == NULL) {
978 InvalidName(name, "Root element must be a message.");
979 return this;
980 }
981 // Render the special type.
982 // "<name>": {
983 // ... Render special type ...
984 // }
985 ProtoWriter::StartObject(name);
986 status = (*type_renderer)(this, data);
987 if (!status.ok()) {
988 InvalidValue(master_type_.name(),
989 StrCat("Field '", name, "', ", status.error_message()));
990 }
991 ProtoWriter::EndObject();
992 return this;
993 }
994
995 if (current_->IsAny()) {
996 current_->any()->RenderDataPiece(name, data);
997 return this;
998 }
999
1000 const google::protobuf::Field* field = NULL;
1001 if (current_->IsMap()) {
1002 if (!ValidMapKey(name)) return this;
1003
1004 // Render an item in repeated map list.
1005 // { "key": "<name>", "value":
1006 Push("", Item::MESSAGE, false, false);
1007 ProtoWriter::RenderDataPiece("key",
1008 DataPiece(name, use_strict_base64_decoding()));
1009 field = Lookup("value");
1010 if (field == NULL) {
1011 Pop();
1012 GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
1013 return this;
1014 }
1015
1016 const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1017 if (type_renderer != NULL) {
1018 // Map's value type is a special type. Render it like a message:
1019 // "value": {
1020 // ... Render special type ...
1021 // }
1022 Push("value", Item::MESSAGE, true, false);
1023 status = (*type_renderer)(this, data);
1024 if (!status.ok()) {
1025 InvalidValue(field->type_url(),
1026 StrCat("Field '", name, "', ", status.error_message()));
1027 }
1028 Pop();
1029 return this;
1030 }
1031
1032 // If we are rendering explicit null values and the backend proto field is
1033 // not of the google.protobuf.NullType type, we do nothing.
1034 if (data.type() == DataPiece::TYPE_NULL &&
1035 field->type_url() != kStructNullValueTypeUrl) {
1036 Pop();
1037 return this;
1038 }
1039
1040 // Render the map value as a primitive type.
1041 ProtoWriter::RenderDataPiece("value", data);
1042 Pop();
1043 return this;
1044 }
1045
1046 field = Lookup(name);
1047 if (field == NULL) return this;
1048
1049 // Check if the field is of special type. Render it accordingly if so.
1050 const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1051 if (type_renderer != NULL) {
1052 Push(name, Item::MESSAGE, false, false);
1053 status = (*type_renderer)(this, data);
1054 if (!status.ok()) {
1055 InvalidValue(field->type_url(),
1056 StrCat("Field '", name, "', ", status.error_message()));
1057 }
1058 Pop();
1059 return this;
1060 }
1061
1062 // If we are rendering explicit null values and the backend proto field is
1063 // not of the google.protobuf.NullType type, we do nothing.
1064 if (data.type() == DataPiece::TYPE_NULL &&
1065 field->type_url() != kStructNullValueTypeUrl) {
1066 return this;
1067 }
1068
1069 ProtoWriter::RenderDataPiece(name, data);
1070 return this;
1071 }
1072
1073 // Map of functions that are responsible for rendering well known type
1074 // represented by the key.
1075 hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
1076 ProtoStreamObjectWriter::renderers_ = NULL;
1077 GOOGLE_PROTOBUF_DECLARE_ONCE(writer_renderers_init_);
1078
InitRendererMap()1079 void ProtoStreamObjectWriter::InitRendererMap() {
1080 renderers_ = new hash_map<string, ProtoStreamObjectWriter::TypeRenderer>();
1081 (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
1082 &ProtoStreamObjectWriter::RenderTimestamp;
1083 (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
1084 &ProtoStreamObjectWriter::RenderDuration;
1085 (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
1086 &ProtoStreamObjectWriter::RenderFieldMask;
1087 (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
1088 &ProtoStreamObjectWriter::RenderWrapperType;
1089 (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
1090 &ProtoStreamObjectWriter::RenderWrapperType;
1091 (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
1092 &ProtoStreamObjectWriter::RenderWrapperType;
1093 (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
1094 &ProtoStreamObjectWriter::RenderWrapperType;
1095 (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
1096 &ProtoStreamObjectWriter::RenderWrapperType;
1097 (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
1098 &ProtoStreamObjectWriter::RenderWrapperType;
1099 (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
1100 &ProtoStreamObjectWriter::RenderWrapperType;
1101 (*renderers_)["type.googleapis.com/google.protobuf.String"] =
1102 &ProtoStreamObjectWriter::RenderWrapperType;
1103 (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
1104 &ProtoStreamObjectWriter::RenderWrapperType;
1105 (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
1106 &ProtoStreamObjectWriter::RenderWrapperType;
1107 (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
1108 &ProtoStreamObjectWriter::RenderWrapperType;
1109 (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
1110 &ProtoStreamObjectWriter::RenderWrapperType;
1111 (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
1112 &ProtoStreamObjectWriter::RenderWrapperType;
1113 (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
1114 &ProtoStreamObjectWriter::RenderWrapperType;
1115 (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
1116 &ProtoStreamObjectWriter::RenderWrapperType;
1117 (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
1118 &ProtoStreamObjectWriter::RenderWrapperType;
1119 (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
1120 &ProtoStreamObjectWriter::RenderWrapperType;
1121 (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
1122 &ProtoStreamObjectWriter::RenderWrapperType;
1123 (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
1124 &ProtoStreamObjectWriter::RenderStructValue;
1125 ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
1126 }
1127
DeleteRendererMap()1128 void ProtoStreamObjectWriter::DeleteRendererMap() {
1129 delete ProtoStreamObjectWriter::renderers_;
1130 renderers_ = NULL;
1131 }
1132
1133 ProtoStreamObjectWriter::TypeRenderer*
FindTypeRenderer(const string & type_url)1134 ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
1135 ::google::protobuf::GoogleOnceInit(&writer_renderers_init_, &InitRendererMap);
1136 return FindOrNull(*renderers_, type_url);
1137 }
1138
ValidMapKey(StringPiece unnormalized_name)1139 bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
1140 if (current_ == NULL) return true;
1141
1142 if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
1143 listener()->InvalidName(
1144 location(), unnormalized_name,
1145 StrCat("Repeated map key: '", unnormalized_name, "' is already set."));
1146 return false;
1147 }
1148
1149 return true;
1150 }
1151
Push(StringPiece name,Item::ItemType item_type,bool is_placeholder,bool is_list)1152 void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type,
1153 bool is_placeholder, bool is_list) {
1154 is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
1155
1156 // invalid_depth == 0 means it is a successful StartObject or StartList.
1157 if (invalid_depth() == 0)
1158 current_.reset(
1159 new Item(current_.release(), item_type, is_placeholder, is_list));
1160 }
1161
Pop()1162 void ProtoStreamObjectWriter::Pop() {
1163 // Pop all placeholder items sending StartObject or StartList events to
1164 // ProtoWriter according to is_list value.
1165 while (current_ != NULL && current_->is_placeholder()) {
1166 PopOneElement();
1167 }
1168 if (current_ != NULL) {
1169 PopOneElement();
1170 }
1171 }
1172
PopOneElement()1173 void ProtoStreamObjectWriter::PopOneElement() {
1174 current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
1175 current_.reset(current_->pop<Item>());
1176 }
1177
IsMap(const google::protobuf::Field & field)1178 bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
1179 if (field.type_url().empty() ||
1180 field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE ||
1181 field.cardinality() !=
1182 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
1183 return false;
1184 }
1185 const google::protobuf::Type* field_type =
1186 typeinfo()->GetTypeByTypeUrl(field.type_url());
1187
1188 // TODO(xiaofeng): Unify option names.
1189 return GetBoolOptionOrDefault(field_type->options(),
1190 "google.protobuf.MessageOptions.map_entry", false) ||
1191 GetBoolOptionOrDefault(field_type->options(), "map_entry", false);
1192 }
1193
IsAny(const google::protobuf::Field & field)1194 bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
1195 return GetTypeWithoutUrl(field.type_url()) == kAnyType;
1196 }
1197
IsStruct(const google::protobuf::Field & field)1198 bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
1199 return GetTypeWithoutUrl(field.type_url()) == kStructType;
1200 }
1201
IsStructValue(const google::protobuf::Field & field)1202 bool ProtoStreamObjectWriter::IsStructValue(
1203 const google::protobuf::Field& field) {
1204 return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
1205 }
1206
IsStructListValue(const google::protobuf::Field & field)1207 bool ProtoStreamObjectWriter::IsStructListValue(
1208 const google::protobuf::Field& field) {
1209 return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
1210 }
1211
1212 } // namespace converter
1213 } // namespace util
1214 } // namespace protobuf
1215 } // namespace google
1216