• Home
  • 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 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 
35 #include <algorithm>
36 #include <limits>
37 #include <vector>
38 #include <sstream>
39 
40 #include <google/protobuf/compiler/csharp/csharp_helpers.h>
41 #include <google/protobuf/compiler/csharp/csharp_names.h>
42 #include <google/protobuf/descriptor.pb.h>
43 #include <google/protobuf/io/printer.h>
44 #include <google/protobuf/wire_format.h>
45 #include <google/protobuf/stubs/strutil.h>
46 
47 #include <google/protobuf/compiler/csharp/csharp_field_base.h>
48 #include <google/protobuf/compiler/csharp/csharp_enum_field.h>
49 #include <google/protobuf/compiler/csharp/csharp_map_field.h>
50 #include <google/protobuf/compiler/csharp/csharp_message_field.h>
51 #include <google/protobuf/compiler/csharp/csharp_options.h>
52 #include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
53 #include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
54 #include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h>
55 #include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h>
56 #include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
57 
58 namespace google {
59 namespace protobuf {
60 namespace compiler {
61 namespace csharp {
62 
GetCSharpType(FieldDescriptor::Type type)63 CSharpType GetCSharpType(FieldDescriptor::Type type) {
64   switch (type) {
65     case FieldDescriptor::TYPE_INT32:
66       return CSHARPTYPE_INT32;
67     case FieldDescriptor::TYPE_INT64:
68       return CSHARPTYPE_INT64;
69     case FieldDescriptor::TYPE_UINT32:
70       return CSHARPTYPE_UINT32;
71     case FieldDescriptor::TYPE_UINT64:
72       return CSHARPTYPE_UINT32;
73     case FieldDescriptor::TYPE_SINT32:
74       return CSHARPTYPE_INT32;
75     case FieldDescriptor::TYPE_SINT64:
76       return CSHARPTYPE_INT64;
77     case FieldDescriptor::TYPE_FIXED32:
78       return CSHARPTYPE_UINT32;
79     case FieldDescriptor::TYPE_FIXED64:
80       return CSHARPTYPE_UINT64;
81     case FieldDescriptor::TYPE_SFIXED32:
82       return CSHARPTYPE_INT32;
83     case FieldDescriptor::TYPE_SFIXED64:
84       return CSHARPTYPE_INT64;
85     case FieldDescriptor::TYPE_FLOAT:
86       return CSHARPTYPE_FLOAT;
87     case FieldDescriptor::TYPE_DOUBLE:
88       return CSHARPTYPE_DOUBLE;
89     case FieldDescriptor::TYPE_BOOL:
90       return CSHARPTYPE_BOOL;
91     case FieldDescriptor::TYPE_ENUM:
92       return CSHARPTYPE_ENUM;
93     case FieldDescriptor::TYPE_STRING:
94       return CSHARPTYPE_STRING;
95     case FieldDescriptor::TYPE_BYTES:
96       return CSHARPTYPE_BYTESTRING;
97     case FieldDescriptor::TYPE_GROUP:
98       return CSHARPTYPE_MESSAGE;
99     case FieldDescriptor::TYPE_MESSAGE:
100       return CSHARPTYPE_MESSAGE;
101 
102       // No default because we want the compiler to complain if any new
103       // types are added.
104   }
105   GOOGLE_LOG(FATAL)<< "Can't get here.";
106   return (CSharpType) -1;
107 }
108 
StripDotProto(const std::string & proto_file)109 std::string StripDotProto(const std::string& proto_file) {
110   int lastindex = proto_file.find_last_of(".");
111   return proto_file.substr(0, lastindex);
112 }
113 
GetFileNamespace(const FileDescriptor * descriptor)114 std::string GetFileNamespace(const FileDescriptor* descriptor) {
115   if (descriptor->options().has_csharp_namespace()) {
116     return descriptor->options().csharp_namespace();
117   }
118   return UnderscoresToCamelCase(descriptor->package(), true, true);
119 }
120 
121 // Returns the Pascal-cased last part of the proto file. For example,
122 // input of "google/protobuf/foo_bar.proto" would result in "FooBar".
GetFileNameBase(const FileDescriptor * descriptor)123 std::string GetFileNameBase(const FileDescriptor* descriptor) {
124     std::string proto_file = descriptor->name();
125     int lastslash = proto_file.find_last_of("/");
126     std::string base = proto_file.substr(lastslash + 1);
127     return UnderscoresToPascalCase(StripDotProto(base));
128 }
129 
GetReflectionClassUnqualifiedName(const FileDescriptor * descriptor)130 std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) {
131   // TODO: Detect collisions with existing messages,
132   // and append an underscore if necessary.
133   return GetFileNameBase(descriptor) + "Reflection";
134 }
135 
GetExtensionClassUnqualifiedName(const FileDescriptor * descriptor)136 std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) {
137   // TODO: Detect collisions with existing messages,
138   // and append an underscore if necessary.
139   return GetFileNameBase(descriptor) + "Extensions";
140 }
141 
142 // TODO(jtattermusch): can we reuse a utility function?
UnderscoresToCamelCase(const std::string & input,bool cap_next_letter,bool preserve_period)143 std::string UnderscoresToCamelCase(const std::string& input,
144                                    bool cap_next_letter,
145                                    bool preserve_period) {
146   string result;
147   // Note:  I distrust ctype.h due to locales.
148   for (int i = 0; i < input.size(); i++) {
149     if ('a' <= input[i] && input[i] <= 'z') {
150       if (cap_next_letter) {
151         result += input[i] + ('A' - 'a');
152       } else {
153         result += input[i];
154       }
155       cap_next_letter = false;
156     } else if ('A' <= input[i] && input[i] <= 'Z') {
157       if (i == 0 && !cap_next_letter) {
158         // Force first letter to lower-case unless explicitly told to
159         // capitalize it.
160         result += input[i] + ('a' - 'A');
161       } else {
162         // Capital letters after the first are left as-is.
163         result += input[i];
164       }
165       cap_next_letter = false;
166     } else if ('0' <= input[i] && input[i] <= '9') {
167       result += input[i];
168       cap_next_letter = true;
169     } else {
170       cap_next_letter = true;
171       if (input[i] == '.' && preserve_period) {
172         result += '.';
173       }
174     }
175   }
176   // Add a trailing "_" if the name should be altered.
177   if (input.size() > 0 && input[input.size() - 1] == '#') {
178     result += '_';
179   }
180   return result;
181 }
182 
UnderscoresToPascalCase(const std::string & input)183 std::string UnderscoresToPascalCase(const std::string& input) {
184   return UnderscoresToCamelCase(input, true);
185 }
186 
187 // Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty)
188 // into a PascalCase string. Precise rules implemented:
189 
190 // Previous input character      Current character         Case
191 // Any                           Non-alphanumeric          Skipped
192 // None - first char of input    Alphanumeric              Upper
193 // Non-letter (e.g. _ or 1)      Alphanumeric              Upper
194 // Numeric                       Alphanumeric              Upper
195 // Lower letter                  Alphanumeric              Same as current
196 // Upper letter                  Alphanumeric              Lower
ShoutyToPascalCase(const std::string & input)197 std::string ShoutyToPascalCase(const std::string& input) {
198   string result;
199   // Simple way of implementing "always start with upper"
200   char previous = '_';
201   for (int i = 0; i < input.size(); i++) {
202     char current = input[i];
203     if (!ascii_isalnum(current)) {
204       previous = current;
205       continue;
206     }
207     if (!ascii_isalnum(previous)) {
208       result += ascii_toupper(current);
209     } else if (ascii_isdigit(previous)) {
210       result += ascii_toupper(current);
211     } else if (ascii_islower(previous)) {
212       result += current;
213     } else {
214       result += ascii_tolower(current);
215     }
216     previous = current;
217   }
218   return result;
219 }
220 
221 // Attempt to remove a prefix from a value, ignoring casing and skipping underscores.
222 // (foo, foo_bar) => bar - underscore after prefix is skipped
223 // (FOO, foo_bar) => bar - casing is ignored
224 // (foo_bar, foobarbaz) => baz - underscore in prefix is ignored
225 // (foobar, foo_barbaz) => baz - underscore in value is ignored
226 // (foo, bar) => bar - prefix isn't matched; return original value
TryRemovePrefix(const std::string & prefix,const std::string & value)227 std::string TryRemovePrefix(const std::string& prefix, const std::string& value) {
228   // First normalize to a lower-case no-underscores prefix to match against
229   std::string prefix_to_match = "";
230   for (size_t i = 0; i < prefix.size(); i++) {
231     if (prefix[i] != '_') {
232       prefix_to_match += ascii_tolower(prefix[i]);
233     }
234   }
235 
236   // This keeps track of how much of value we've consumed
237   size_t prefix_index, value_index;
238   for (prefix_index = 0, value_index = 0;
239       prefix_index < prefix_to_match.size() && value_index < value.size();
240       value_index++) {
241     // Skip over underscores in the value
242     if (value[value_index] == '_') {
243       continue;
244     }
245     if (ascii_tolower(value[value_index]) != prefix_to_match[prefix_index++]) {
246       // Failed to match the prefix - bail out early.
247       return value;
248     }
249   }
250 
251   // If we didn't finish looking through the prefix, we can't strip it.
252   if (prefix_index < prefix_to_match.size()) {
253     return value;
254   }
255 
256   // Step over any underscores after the prefix
257   while (value_index < value.size() && value[value_index] == '_') {
258     value_index++;
259   }
260 
261   // If there's nothing left (e.g. it was a prefix with only underscores afterwards), don't strip.
262   if (value_index == value.size()) {
263     return value;
264   }
265 
266   return value.substr(value_index);
267 }
268 
269 // Format the enum value name in a pleasant way for C#:
270 // - Strip the enum name as a prefix if possible
271 // - Convert to PascalCase.
272 // For example, an enum called Color with a value of COLOR_BLUE should
273 // result in an enum value in C# called just Blue
GetEnumValueName(const std::string & enum_name,const std::string & enum_value_name)274 std::string GetEnumValueName(const std::string& enum_name, const std::string& enum_value_name) {
275   std::string stripped = TryRemovePrefix(enum_name, enum_value_name);
276   std::string result = ShoutyToPascalCase(stripped);
277   // Just in case we have an enum name of FOO and a value of FOO_2... make sure the returned
278   // string is a valid identifier.
279   if (ascii_isdigit(result[0])) {
280     result = "_" + result;
281   }
282   return result;
283 }
284 
GetGroupEndTag(const Descriptor * descriptor)285 uint GetGroupEndTag(const Descriptor* descriptor) {
286   const Descriptor* containing_type = descriptor->containing_type();
287   if (containing_type != NULL) {
288     const FieldDescriptor* field;
289     for (int i = 0; i < containing_type->field_count(); i++) {
290       field = containing_type->field(i);
291       if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
292           field->message_type() == descriptor) {
293         return internal::WireFormatLite::MakeTag(
294             field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
295       }
296     }
297     for (int i = 0; i < containing_type->extension_count(); i++) {
298       field = containing_type->extension(i);
299       if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
300           field->message_type() == descriptor) {
301         return internal::WireFormatLite::MakeTag(
302             field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
303       }
304     }
305   } else {
306     const FileDescriptor* containing_file = descriptor->file();
307     if (containing_file != NULL) {
308       const FieldDescriptor* field;
309       for (int i = 0; i < containing_file->extension_count(); i++) {
310         field = containing_file->extension(i);
311         if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
312             field->message_type() == descriptor) {
313           return internal::WireFormatLite::MakeTag(
314               field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
315         }
316       }
317     }
318   }
319 
320   return 0;
321 }
322 
ToCSharpName(const std::string & name,const FileDescriptor * file)323 std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
324   std::string result = GetFileNamespace(file);
325   if (!result.empty()) {
326     result += '.';
327   }
328   string classname;
329   if (file->package().empty()) {
330     classname = name;
331   } else {
332     // Strip the proto package from full_name since we've replaced it with
333     // the C# namespace.
334     classname = name.substr(file->package().size() + 1);
335   }
336   result += StringReplace(classname, ".", ".Types.", true);
337   return "global::" + result;
338 }
339 
GetReflectionClassName(const FileDescriptor * descriptor)340 std::string GetReflectionClassName(const FileDescriptor* descriptor) {
341   std::string result = GetFileNamespace(descriptor);
342   if (!result.empty()) {
343     result += '.';
344   }
345   result += GetReflectionClassUnqualifiedName(descriptor);
346   return "global::" + result;
347 }
348 
GetFullExtensionName(const FieldDescriptor * descriptor)349 std::string GetFullExtensionName(const FieldDescriptor* descriptor) {
350   if (descriptor->extension_scope()) {
351     return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor);
352   }
353   else {
354     return GetExtensionClassUnqualifiedName(descriptor->file())  + "." + GetPropertyName(descriptor);
355   }
356 }
357 
GetClassName(const Descriptor * descriptor)358 std::string GetClassName(const Descriptor* descriptor) {
359   return ToCSharpName(descriptor->full_name(), descriptor->file());
360 }
361 
GetClassName(const EnumDescriptor * descriptor)362 std::string GetClassName(const EnumDescriptor* descriptor) {
363   return ToCSharpName(descriptor->full_name(), descriptor->file());
364 }
365 
366 // Groups are hacky:  The name of the field is just the lower-cased name
367 // of the group type.  In C#, though, we would like to retain the original
368 // capitalization of the type name.
GetFieldName(const FieldDescriptor * descriptor)369 std::string GetFieldName(const FieldDescriptor* descriptor) {
370   if (descriptor->type() == FieldDescriptor::TYPE_GROUP) {
371     return descriptor->message_type()->name();
372   } else {
373     return descriptor->name();
374   }
375 }
376 
GetFieldConstantName(const FieldDescriptor * field)377 std::string GetFieldConstantName(const FieldDescriptor* field) {
378   return GetPropertyName(field) + "FieldNumber";
379 }
380 
GetPropertyName(const FieldDescriptor * descriptor)381 std::string GetPropertyName(const FieldDescriptor* descriptor) {
382   // TODO(jtattermusch): consider introducing csharp_property_name field option
383   std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
384   // Avoid either our own type name or reserved names. Note that not all names
385   // are reserved - a field called to_string, write_to etc would still cause a problem.
386   // There are various ways of ending up with naming collisions, but we try to avoid obvious
387   // ones.
388   if (property_name == descriptor->containing_type()->name()
389       || property_name == "Types"
390       || property_name == "Descriptor") {
391     property_name += "_";
392   }
393   return property_name;
394 }
395 
GetOutputFile(const FileDescriptor * descriptor,const std::string file_extension,const bool generate_directories,const std::string base_namespace,string * error)396 std::string GetOutputFile(const FileDescriptor* descriptor,
397                           const std::string file_extension,
398                           const bool generate_directories,
399                           const std::string base_namespace, string* error) {
400   string relative_filename = GetFileNameBase(descriptor) + file_extension;
401   if (!generate_directories) {
402     return relative_filename;
403   }
404   string ns = GetFileNamespace(descriptor);
405   string namespace_suffix = ns;
406   if (!base_namespace.empty()) {
407     // Check that the base_namespace is either equal to or a leading part of
408     // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't
409     // be regarded as a prefix of "Foo.Bar". The simplest option is to add "."
410     // to both.
411     string extended_ns = ns + ".";
412     if (extended_ns.find(base_namespace + ".") != 0) {
413       *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace;
414       return ""; // This will be ignored, because we've set an error.
415     }
416     namespace_suffix = ns.substr(base_namespace.length());
417     if (namespace_suffix.find(".") == 0) {
418       namespace_suffix = namespace_suffix.substr(1);
419     }
420   }
421 
422   string namespace_dir = StringReplace(namespace_suffix, ".", "/", true);
423   if (!namespace_dir.empty()) {
424     namespace_dir += "/";
425   }
426   return namespace_dir + relative_filename;
427 }
428 
429 // TODO: c&p from Java protoc plugin
430 // For encodings with fixed sizes, returns that size in bytes.  Otherwise
431 // returns -1.
GetFixedSize(FieldDescriptor::Type type)432 int GetFixedSize(FieldDescriptor::Type type) {
433   switch (type) {
434     case FieldDescriptor::TYPE_INT32   : return -1;
435     case FieldDescriptor::TYPE_INT64   : return -1;
436     case FieldDescriptor::TYPE_UINT32  : return -1;
437     case FieldDescriptor::TYPE_UINT64  : return -1;
438     case FieldDescriptor::TYPE_SINT32  : return -1;
439     case FieldDescriptor::TYPE_SINT64  : return -1;
440     case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size;
441     case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size;
442     case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size;
443     case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size;
444     case FieldDescriptor::TYPE_FLOAT   : return internal::WireFormatLite::kFloatSize;
445     case FieldDescriptor::TYPE_DOUBLE  : return internal::WireFormatLite::kDoubleSize;
446 
447     case FieldDescriptor::TYPE_BOOL    : return internal::WireFormatLite::kBoolSize;
448     case FieldDescriptor::TYPE_ENUM    : return -1;
449 
450     case FieldDescriptor::TYPE_STRING  : return -1;
451     case FieldDescriptor::TYPE_BYTES   : return -1;
452     case FieldDescriptor::TYPE_GROUP   : return -1;
453     case FieldDescriptor::TYPE_MESSAGE : return -1;
454 
455     // No default because we want the compiler to complain if any new
456     // types are added.
457   }
458   GOOGLE_LOG(FATAL) << "Can't get here.";
459   return -1;
460 }
461 
462 static const char base64_chars[] =
463     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
464 
StringToBase64(const std::string & input)465 std::string StringToBase64(const std::string& input) {
466   std::string result;
467   size_t remaining = input.size();
468   const unsigned char *src = (const unsigned char*) input.c_str();
469   while (remaining > 2) {
470     result += base64_chars[src[0] >> 2];
471     result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
472     result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)];
473     result += base64_chars[src[2] & 0x3f];
474     remaining -= 3;
475     src += 3;
476   }
477   switch (remaining) {
478     case 2:
479       result += base64_chars[src[0] >> 2];
480       result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
481       result += base64_chars[(src[1] & 0xf) << 2];
482       result += '=';
483       src += 2;
484       break;
485     case 1:
486       result += base64_chars[src[0] >> 2];
487       result += base64_chars[((src[0] & 0x3) << 4)];
488       result += '=';
489       result += '=';
490       src += 1;
491       break;
492   }
493   return result;
494 }
495 
FileDescriptorToBase64(const FileDescriptor * descriptor)496 std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
497   std::string fdp_bytes;
498   FileDescriptorProto fdp;
499   descriptor->CopyTo(&fdp);
500   fdp.SerializeToString(&fdp_bytes);
501   return StringToBase64(fdp_bytes);
502 }
503 
CreateFieldGenerator(const FieldDescriptor * descriptor,int presenceIndex,const Options * options)504 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
505                                          int presenceIndex,
506                                          const Options* options) {
507   switch (descriptor->type()) {
508     case FieldDescriptor::TYPE_GROUP:
509     case FieldDescriptor::TYPE_MESSAGE:
510       if (descriptor->is_repeated()) {
511         if (descriptor->is_map()) {
512           return new MapFieldGenerator(descriptor, presenceIndex, options);
513         } else {
514           return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options);
515         }
516       } else {
517         if (IsWrapperType(descriptor)) {
518           if (descriptor->real_containing_oneof()) {
519             return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options);
520           } else {
521             return new WrapperFieldGenerator(descriptor, presenceIndex, options);
522           }
523         } else {
524           if (descriptor->real_containing_oneof()) {
525             return new MessageOneofFieldGenerator(descriptor, presenceIndex, options);
526           } else {
527             return new MessageFieldGenerator(descriptor, presenceIndex, options);
528           }
529         }
530       }
531     case FieldDescriptor::TYPE_ENUM:
532       if (descriptor->is_repeated()) {
533         return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options);
534       } else {
535         if (descriptor->real_containing_oneof()) {
536           return new EnumOneofFieldGenerator(descriptor, presenceIndex, options);
537         } else {
538           return new EnumFieldGenerator(descriptor, presenceIndex, options);
539         }
540       }
541     default:
542       if (descriptor->is_repeated()) {
543         return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options);
544       } else {
545         if (descriptor->real_containing_oneof()) {
546           return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options);
547         } else {
548           return new PrimitiveFieldGenerator(descriptor, presenceIndex, options);
549         }
550       }
551   }
552 }
553 
IsNullable(const FieldDescriptor * descriptor)554 bool IsNullable(const FieldDescriptor* descriptor) {
555   if (descriptor->is_repeated()) {
556     return true;
557   }
558 
559   switch (descriptor->type()) {
560     case FieldDescriptor::TYPE_ENUM:
561     case FieldDescriptor::TYPE_DOUBLE:
562     case FieldDescriptor::TYPE_FLOAT:
563     case FieldDescriptor::TYPE_INT64:
564     case FieldDescriptor::TYPE_UINT64:
565     case FieldDescriptor::TYPE_INT32:
566     case FieldDescriptor::TYPE_FIXED64:
567     case FieldDescriptor::TYPE_FIXED32:
568     case FieldDescriptor::TYPE_BOOL:
569     case FieldDescriptor::TYPE_UINT32:
570     case FieldDescriptor::TYPE_SFIXED32:
571     case FieldDescriptor::TYPE_SFIXED64:
572     case FieldDescriptor::TYPE_SINT32:
573     case FieldDescriptor::TYPE_SINT64:
574       return false;
575 
576     case FieldDescriptor::TYPE_MESSAGE:
577     case FieldDescriptor::TYPE_GROUP:
578     case FieldDescriptor::TYPE_STRING:
579     case FieldDescriptor::TYPE_BYTES:
580       return true;
581 
582     default:
583       GOOGLE_LOG(FATAL) << "Unknown field type.";
584       return true;
585   }
586 }
587 
588 }  // namespace csharp
589 }  // namespace compiler
590 }  // namespace protobuf
591 }  // namespace google
592