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