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 return 0;
289 }
290 const FieldDescriptor* field = containing_type->FindFieldByName(descriptor->name());
291 if (field != NULL && field->type() == FieldDescriptor::Type::TYPE_GROUP) {
292 return internal::WireFormatLite::MakeTag(field->number(), internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
293 } else {
294 return 0;
295 }
296 }
297
ToCSharpName(const std::string & name,const FileDescriptor * file)298 std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
299 std::string result = GetFileNamespace(file);
300 if (result != "") {
301 result += '.';
302 }
303 string classname;
304 if (file->package().empty()) {
305 classname = name;
306 } else {
307 // Strip the proto package from full_name since we've replaced it with
308 // the C# namespace.
309 classname = name.substr(file->package().size() + 1);
310 }
311 result += StringReplace(classname, ".", ".Types.", true);
312 return "global::" + result;
313 }
314
GetReflectionClassName(const FileDescriptor * descriptor)315 std::string GetReflectionClassName(const FileDescriptor* descriptor) {
316 std::string result = GetFileNamespace(descriptor);
317 if (!result.empty()) {
318 result += '.';
319 }
320 result += GetReflectionClassUnqualifiedName(descriptor);
321 return "global::" + result;
322 }
323
GetFullExtensionName(const FieldDescriptor * descriptor)324 std::string GetFullExtensionName(const FieldDescriptor* descriptor) {
325 if (descriptor->extension_scope()) {
326 return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor);
327 }
328 else {
329 return GetExtensionClassUnqualifiedName(descriptor->file()) + "." + GetPropertyName(descriptor);
330 }
331 }
332
GetClassName(const Descriptor * descriptor)333 std::string GetClassName(const Descriptor* descriptor) {
334 return ToCSharpName(descriptor->full_name(), descriptor->file());
335 }
336
GetClassName(const EnumDescriptor * descriptor)337 std::string GetClassName(const EnumDescriptor* descriptor) {
338 return ToCSharpName(descriptor->full_name(), descriptor->file());
339 }
340
341 // Groups are hacky: The name of the field is just the lower-cased name
342 // of the group type. In C#, though, we would like to retain the original
343 // capitalization of the type name.
GetFieldName(const FieldDescriptor * descriptor)344 std::string GetFieldName(const FieldDescriptor* descriptor) {
345 if (descriptor->type() == FieldDescriptor::TYPE_GROUP) {
346 return descriptor->message_type()->name();
347 } else {
348 return descriptor->name();
349 }
350 }
351
GetFieldConstantName(const FieldDescriptor * field)352 std::string GetFieldConstantName(const FieldDescriptor* field) {
353 return GetPropertyName(field) + "FieldNumber";
354 }
355
GetPropertyName(const FieldDescriptor * descriptor)356 std::string GetPropertyName(const FieldDescriptor* descriptor) {
357 // TODO(jtattermusch): consider introducing csharp_property_name field option
358 std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
359 // Avoid either our own type name or reserved names. Note that not all names
360 // are reserved - a field called to_string, write_to etc would still cause a problem.
361 // There are various ways of ending up with naming collisions, but we try to avoid obvious
362 // ones.
363 if (property_name == descriptor->containing_type()->name()
364 || property_name == "Types"
365 || property_name == "Descriptor") {
366 property_name += "_";
367 }
368 return property_name;
369 }
370
GetOutputFile(const FileDescriptor * descriptor,const std::string file_extension,const bool generate_directories,const std::string base_namespace,string * error)371 std::string GetOutputFile(const FileDescriptor* descriptor,
372 const std::string file_extension,
373 const bool generate_directories,
374 const std::string base_namespace, string* error) {
375 string relative_filename = GetFileNameBase(descriptor) + file_extension;
376 if (!generate_directories) {
377 return relative_filename;
378 }
379 string ns = GetFileNamespace(descriptor);
380 string namespace_suffix = ns;
381 if (!base_namespace.empty()) {
382 // Check that the base_namespace is either equal to or a leading part of
383 // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't
384 // be regarded as a prefix of "Foo.Bar". The simplest option is to add "."
385 // to both.
386 string extended_ns = ns + ".";
387 if (extended_ns.find(base_namespace + ".") != 0) {
388 *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace;
389 return ""; // This will be ignored, because we've set an error.
390 }
391 namespace_suffix = ns.substr(base_namespace.length());
392 if (namespace_suffix.find(".") == 0) {
393 namespace_suffix = namespace_suffix.substr(1);
394 }
395 }
396
397 string namespace_dir = StringReplace(namespace_suffix, ".", "/", true);
398 if (!namespace_dir.empty()) {
399 namespace_dir += "/";
400 }
401 return namespace_dir + relative_filename;
402 }
403
404 // TODO: c&p from Java protoc plugin
405 // For encodings with fixed sizes, returns that size in bytes. Otherwise
406 // returns -1.
GetFixedSize(FieldDescriptor::Type type)407 int GetFixedSize(FieldDescriptor::Type type) {
408 switch (type) {
409 case FieldDescriptor::TYPE_INT32 : return -1;
410 case FieldDescriptor::TYPE_INT64 : return -1;
411 case FieldDescriptor::TYPE_UINT32 : return -1;
412 case FieldDescriptor::TYPE_UINT64 : return -1;
413 case FieldDescriptor::TYPE_SINT32 : return -1;
414 case FieldDescriptor::TYPE_SINT64 : return -1;
415 case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size;
416 case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size;
417 case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size;
418 case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size;
419 case FieldDescriptor::TYPE_FLOAT : return internal::WireFormatLite::kFloatSize;
420 case FieldDescriptor::TYPE_DOUBLE : return internal::WireFormatLite::kDoubleSize;
421
422 case FieldDescriptor::TYPE_BOOL : return internal::WireFormatLite::kBoolSize;
423 case FieldDescriptor::TYPE_ENUM : return -1;
424
425 case FieldDescriptor::TYPE_STRING : return -1;
426 case FieldDescriptor::TYPE_BYTES : return -1;
427 case FieldDescriptor::TYPE_GROUP : return -1;
428 case FieldDescriptor::TYPE_MESSAGE : return -1;
429
430 // No default because we want the compiler to complain if any new
431 // types are added.
432 }
433 GOOGLE_LOG(FATAL) << "Can't get here.";
434 return -1;
435 }
436
437 static const char base64_chars[] =
438 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
439
StringToBase64(const std::string & input)440 std::string StringToBase64(const std::string& input) {
441 std::string result;
442 size_t remaining = input.size();
443 const unsigned char *src = (const unsigned char*) input.c_str();
444 while (remaining > 2) {
445 result += base64_chars[src[0] >> 2];
446 result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
447 result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)];
448 result += base64_chars[src[2] & 0x3f];
449 remaining -= 3;
450 src += 3;
451 }
452 switch (remaining) {
453 case 2:
454 result += base64_chars[src[0] >> 2];
455 result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
456 result += base64_chars[(src[1] & 0xf) << 2];
457 result += '=';
458 src += 2;
459 break;
460 case 1:
461 result += base64_chars[src[0] >> 2];
462 result += base64_chars[((src[0] & 0x3) << 4)];
463 result += '=';
464 result += '=';
465 src += 1;
466 break;
467 }
468 return result;
469 }
470
FileDescriptorToBase64(const FileDescriptor * descriptor)471 std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
472 std::string fdp_bytes;
473 FileDescriptorProto fdp;
474 descriptor->CopyTo(&fdp);
475 fdp.SerializeToString(&fdp_bytes);
476 return StringToBase64(fdp_bytes);
477 }
478
CreateFieldGenerator(const FieldDescriptor * descriptor,int presenceIndex,const Options * options)479 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
480 int presenceIndex,
481 const Options* options) {
482 switch (descriptor->type()) {
483 case FieldDescriptor::TYPE_GROUP:
484 case FieldDescriptor::TYPE_MESSAGE:
485 if (descriptor->is_repeated()) {
486 if (descriptor->is_map()) {
487 return new MapFieldGenerator(descriptor, presenceIndex, options);
488 } else {
489 return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options);
490 }
491 } else {
492 if (IsWrapperType(descriptor)) {
493 if (descriptor->containing_oneof()) {
494 return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options);
495 } else {
496 return new WrapperFieldGenerator(descriptor, presenceIndex, options);
497 }
498 } else {
499 if (descriptor->containing_oneof()) {
500 return new MessageOneofFieldGenerator(descriptor, presenceIndex, options);
501 } else {
502 return new MessageFieldGenerator(descriptor, presenceIndex, options);
503 }
504 }
505 }
506 case FieldDescriptor::TYPE_ENUM:
507 if (descriptor->is_repeated()) {
508 return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options);
509 } else {
510 if (descriptor->containing_oneof()) {
511 return new EnumOneofFieldGenerator(descriptor, presenceIndex, options);
512 } else {
513 return new EnumFieldGenerator(descriptor, presenceIndex, options);
514 }
515 }
516 default:
517 if (descriptor->is_repeated()) {
518 return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options);
519 } else {
520 if (descriptor->containing_oneof()) {
521 return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options);
522 } else {
523 return new PrimitiveFieldGenerator(descriptor, presenceIndex, options);
524 }
525 }
526 }
527 }
528
IsNullable(const FieldDescriptor * descriptor)529 bool IsNullable(const FieldDescriptor* descriptor) {
530 if (descriptor->is_repeated()) {
531 return true;
532 }
533
534 switch (descriptor->type()) {
535 case FieldDescriptor::TYPE_ENUM:
536 case FieldDescriptor::TYPE_DOUBLE:
537 case FieldDescriptor::TYPE_FLOAT:
538 case FieldDescriptor::TYPE_INT64:
539 case FieldDescriptor::TYPE_UINT64:
540 case FieldDescriptor::TYPE_INT32:
541 case FieldDescriptor::TYPE_FIXED64:
542 case FieldDescriptor::TYPE_FIXED32:
543 case FieldDescriptor::TYPE_BOOL:
544 case FieldDescriptor::TYPE_UINT32:
545 case FieldDescriptor::TYPE_SFIXED32:
546 case FieldDescriptor::TYPE_SFIXED64:
547 case FieldDescriptor::TYPE_SINT32:
548 case FieldDescriptor::TYPE_SINT64:
549 return false;
550
551 case FieldDescriptor::TYPE_MESSAGE:
552 case FieldDescriptor::TYPE_GROUP:
553 case FieldDescriptor::TYPE_STRING:
554 case FieldDescriptor::TYPE_BYTES:
555 return true;
556
557 default:
558 GOOGLE_LOG(FATAL) << "Unknown field type.";
559 return true;
560 }
561 }
562
563 } // namespace csharp
564 } // namespace compiler
565 } // namespace protobuf
566 } // namespace google
567