• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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