Line data Source code
1 : // Protocol Buffers - Google's data interchange format
2 : // Copyright 2008 Google Inc. All rights reserved.
3 : // http://code.google.com/p/protobuf/
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 <limits>
36 : #include <vector>
37 :
38 : #include <google/protobuf/compiler/javanano/javanano_helpers.h>
39 : #include <google/protobuf/compiler/javanano/javanano_params.h>
40 : #include <google/protobuf/descriptor.pb.h>
41 : #include <google/protobuf/stubs/hash.h>
42 : #include <google/protobuf/stubs/strutil.h>
43 : #include <google/protobuf/stubs/substitute.h>
44 :
45 : namespace google {
46 : namespace protobuf {
47 : namespace compiler {
48 : namespace javanano {
49 :
50 : const char kThickSeparator[] =
51 : "// ===================================================================\n";
52 : const char kThinSeparator[] =
53 : "// -------------------------------------------------------------------\n";
54 :
55 34 : class RenameKeywords {
56 : private:
57 : hash_set<string> java_keywords_set_;
58 :
59 : public:
60 17 : RenameKeywords() {
61 : static const char* kJavaKeywordsList[] = {
62 : // Reserved Java Keywords
63 : "abstract", "assert", "boolean", "break", "byte", "case", "catch",
64 : "char", "class", "const", "continue", "default", "do", "double", "else",
65 : "enum", "extends", "final", "finally", "float", "for", "goto", "if",
66 : "implements", "import", "instanceof", "int", "interface", "long",
67 : "native", "new", "package", "private", "protected", "public", "return",
68 : "short", "static", "strictfp", "super", "switch", "synchronized",
69 : "this", "throw", "throws", "transient", "try", "void", "volatile", "while",
70 :
71 : // Reserved Keywords for Literals
72 : "false", "null", "true"
73 : };
74 :
75 918 : for (int i = 0; i < GOOGLE_ARRAYSIZE(kJavaKeywordsList); i++) {
76 2703 : java_keywords_set_.insert(kJavaKeywordsList[i]);
77 : }
78 17 : }
79 :
80 : // Used to rename the a field name if it's a java keyword. Specifically
81 : // this is used to rename the ["name"] or ["capitalized_name"] field params.
82 : // (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html)
83 0 : string RenameJavaKeywordsImpl(const string& input) {
84 0 : string result = input;
85 :
86 0 : if (java_keywords_set_.find(result) != java_keywords_set_.end()) {
87 : result += "_";
88 : }
89 :
90 0 : return result;
91 : }
92 :
93 : };
94 :
95 17 : static RenameKeywords sRenameKeywords;
96 :
97 : namespace {
98 :
99 : const char* kDefaultPackage = "";
100 :
101 0 : const string& FieldName(const FieldDescriptor* field) {
102 : // Groups are hacky: The name of the field is just the lower-cased name
103 : // of the group type. In Java, though, we would like to retain the original
104 : // capitalization of the type name.
105 0 : if (field->type() == FieldDescriptor::TYPE_GROUP) {
106 0 : return field->message_type()->name();
107 : } else {
108 0 : return field->name();
109 : }
110 : }
111 :
112 0 : string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
113 : string result;
114 : // Note: I distrust ctype.h due to locales.
115 0 : for (int i = 0; i < input.size(); i++) {
116 0 : if ('a' <= input[i] && input[i] <= 'z') {
117 0 : if (cap_next_letter) {
118 0 : result += input[i] + ('A' - 'a');
119 : } else {
120 0 : result += input[i];
121 : }
122 : cap_next_letter = false;
123 0 : } else if ('A' <= input[i] && input[i] <= 'Z') {
124 0 : if (i == 0 && !cap_next_letter) {
125 : // Force first letter to lower-case unless explicitly told to
126 : // capitalize it.
127 0 : result += input[i] + ('a' - 'A');
128 : } else {
129 : // Capital letters after the first are left as-is.
130 0 : result += input[i];
131 : }
132 : cap_next_letter = false;
133 0 : } else if ('0' <= input[i] && input[i] <= '9') {
134 0 : result += input[i];
135 : cap_next_letter = true;
136 : } else {
137 : cap_next_letter = true;
138 : }
139 : }
140 0 : return result;
141 : }
142 :
143 : } // namespace
144 :
145 0 : string UnderscoresToCamelCase(const FieldDescriptor* field) {
146 0 : return UnderscoresToCamelCaseImpl(FieldName(field), false);
147 : }
148 :
149 0 : string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
150 0 : return UnderscoresToCamelCaseImpl(FieldName(field), true);
151 : }
152 :
153 0 : string UnderscoresToCamelCase(const MethodDescriptor* method) {
154 0 : return UnderscoresToCamelCaseImpl(method->name(), false);
155 : }
156 :
157 0 : string UnderscoresToCamelCase(const OneofDescriptor* oneof) {
158 0 : return UnderscoresToCamelCaseImpl(oneof->name(), false);
159 : }
160 :
161 0 : string UnderscoresToCapitalizedCamelCase(const OneofDescriptor* oneof) {
162 0 : return UnderscoresToCamelCaseImpl(oneof->name(), true);
163 : }
164 :
165 0 : string RenameJavaKeywords(const string& input) {
166 0 : return sRenameKeywords.RenameJavaKeywordsImpl(input);
167 : }
168 :
169 0 : string StripProto(const string& filename) {
170 0 : if (HasSuffixString(filename, ".protodevel")) {
171 0 : return StripSuffixString(filename, ".protodevel");
172 : } else {
173 0 : return StripSuffixString(filename, ".proto");
174 : }
175 : }
176 :
177 0 : string FileClassName(const Params& params, const FileDescriptor* file) {
178 0 : if (params.has_java_outer_classname(file->name())) {
179 0 : return params.java_outer_classname(file->name());
180 : } else {
181 : // Use the filename itself with underscores removed
182 : // and a CamelCase style name.
183 : string basename;
184 0 : string::size_type last_slash = file->name().find_last_of('/');
185 0 : if (last_slash == string::npos) {
186 0 : basename = file->name();
187 : } else {
188 0 : basename = file->name().substr(last_slash + 1);
189 : }
190 0 : return UnderscoresToCamelCaseImpl(StripProto(basename), true);
191 : }
192 : }
193 :
194 0 : string FileJavaPackage(const Params& params, const FileDescriptor* file) {
195 0 : if (params.has_java_package(file->name())) {
196 0 : return params.java_package(file->name());
197 : } else {
198 0 : string result = kDefaultPackage;
199 0 : if (!file->package().empty()) {
200 0 : if (!result.empty()) result += '.';
201 0 : result += file->package();
202 : }
203 :
204 0 : if (!file->options().javanano_use_deprecated_package()) {
205 0 : if (!result.empty()) {
206 : result += ".";
207 : }
208 : result += "nano";
209 : }
210 :
211 0 : return result;
212 : }
213 : }
214 :
215 0 : bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) {
216 : // If java_multiple_files is false, the outer class is always needed.
217 0 : if (!params.java_multiple_files(file->name())) {
218 : return true;
219 : }
220 :
221 : // File-scope extensions need the outer class as the scope.
222 0 : if (file->extension_count() != 0) {
223 : return true;
224 : }
225 :
226 : // If container interfaces are not generated, file-scope enums need the
227 : // outer class as the scope.
228 0 : if (file->enum_type_count() != 0 && !params.java_enum_style()) {
229 : return true;
230 : }
231 :
232 0 : return false;
233 : }
234 :
235 0 : string ToJavaName(const Params& params, const string& name, bool is_class,
236 0 : const Descriptor* parent, const FileDescriptor* file) {
237 : string result;
238 0 : if (parent != NULL) {
239 0 : result.append(ClassName(params, parent));
240 0 : } else if (is_class && params.java_multiple_files(file->name())) {
241 0 : result.append(FileJavaPackage(params, file));
242 : } else {
243 0 : result.append(ClassName(params, file));
244 : }
245 0 : if (!result.empty()) result.append(1, '.');
246 0 : result.append(RenameJavaKeywords(name));
247 0 : return result;
248 : }
249 :
250 0 : string ClassName(const Params& params, const FileDescriptor* descriptor) {
251 0 : string result = FileJavaPackage(params, descriptor);
252 0 : if (!result.empty()) result += '.';
253 0 : result += FileClassName(params, descriptor);
254 0 : return result;
255 : }
256 :
257 0 : string ClassName(const Params& params, const EnumDescriptor* descriptor) {
258 0 : const Descriptor* parent = descriptor->containing_type();
259 : // When using Java enum style, an enum's class name contains the enum name.
260 : // Use the standard ToJavaName translation.
261 0 : if (params.java_enum_style()) {
262 0 : return ToJavaName(params, descriptor->name(), true, parent,
263 0 : descriptor->file());
264 : }
265 : // Otherwise the enum members are accessed from the enclosing class.
266 0 : if (parent != NULL) {
267 : return ClassName(params, parent);
268 : } else {
269 0 : return ClassName(params, descriptor->file());
270 : }
271 : }
272 :
273 0 : string FieldConstantName(const FieldDescriptor *field) {
274 0 : string name = field->name() + "_FIELD_NUMBER";
275 0 : UpperString(&name);
276 0 : return name;
277 : }
278 :
279 0 : string FieldDefaultConstantName(const FieldDescriptor *field) {
280 0 : return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default";
281 : }
282 :
283 0 : void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
284 : // We don't want to print group bodies so we cut off after the first line
285 : // (the second line for extensions).
286 0 : string def = field->DebugString();
287 0 : string::size_type first_line_end = def.find_first_of('\n');
288 : printer->Print("// $def$\n",
289 0 : "def", def.substr(0, first_line_end));
290 0 : if (field->is_extension()) {
291 0 : string::size_type second_line_start = first_line_end + 1;
292 : string::size_type second_line_length =
293 0 : def.find('\n', second_line_start) - second_line_start;
294 : printer->Print("// $def$\n",
295 0 : "def", def.substr(second_line_start, second_line_length));
296 : }
297 0 : }
298 :
299 0 : JavaType GetJavaType(FieldDescriptor::Type field_type) {
300 0 : switch (field_type) {
301 : case FieldDescriptor::TYPE_INT32:
302 : case FieldDescriptor::TYPE_UINT32:
303 : case FieldDescriptor::TYPE_SINT32:
304 : case FieldDescriptor::TYPE_FIXED32:
305 : case FieldDescriptor::TYPE_SFIXED32:
306 : return JAVATYPE_INT;
307 :
308 : case FieldDescriptor::TYPE_INT64:
309 : case FieldDescriptor::TYPE_UINT64:
310 : case FieldDescriptor::TYPE_SINT64:
311 : case FieldDescriptor::TYPE_FIXED64:
312 : case FieldDescriptor::TYPE_SFIXED64:
313 0 : return JAVATYPE_LONG;
314 :
315 : case FieldDescriptor::TYPE_FLOAT:
316 0 : return JAVATYPE_FLOAT;
317 :
318 : case FieldDescriptor::TYPE_DOUBLE:
319 0 : return JAVATYPE_DOUBLE;
320 :
321 : case FieldDescriptor::TYPE_BOOL:
322 0 : return JAVATYPE_BOOLEAN;
323 :
324 : case FieldDescriptor::TYPE_STRING:
325 0 : return JAVATYPE_STRING;
326 :
327 : case FieldDescriptor::TYPE_BYTES:
328 0 : return JAVATYPE_BYTES;
329 :
330 : case FieldDescriptor::TYPE_ENUM:
331 0 : return JAVATYPE_ENUM;
332 :
333 : case FieldDescriptor::TYPE_GROUP:
334 : case FieldDescriptor::TYPE_MESSAGE:
335 0 : return JAVATYPE_MESSAGE;
336 :
337 : // No default because we want the compiler to complain if any new
338 : // types are added.
339 : }
340 :
341 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
342 0 : return JAVATYPE_INT;
343 : }
344 :
345 0 : string PrimitiveTypeName(JavaType type) {
346 0 : switch (type) {
347 0 : case JAVATYPE_INT : return "int";
348 0 : case JAVATYPE_LONG : return "long";
349 0 : case JAVATYPE_FLOAT : return "float";
350 0 : case JAVATYPE_DOUBLE : return "double";
351 0 : case JAVATYPE_BOOLEAN: return "boolean";
352 0 : case JAVATYPE_STRING : return "java.lang.String";
353 0 : case JAVATYPE_BYTES : return "byte[]";
354 0 : case JAVATYPE_ENUM : return "int";
355 0 : case JAVATYPE_MESSAGE: return "";
356 :
357 : // No default because we want the compiler to complain if any new
358 : // JavaTypes are added.
359 : }
360 :
361 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
362 0 : return "";
363 : }
364 :
365 0 : string BoxedPrimitiveTypeName(JavaType type) {
366 0 : switch (type) {
367 0 : case JAVATYPE_INT : return "java.lang.Integer";
368 0 : case JAVATYPE_LONG : return "java.lang.Long";
369 0 : case JAVATYPE_FLOAT : return "java.lang.Float";
370 0 : case JAVATYPE_DOUBLE : return "java.lang.Double";
371 0 : case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
372 0 : case JAVATYPE_STRING : return "java.lang.String";
373 0 : case JAVATYPE_BYTES : return "byte[]";
374 0 : case JAVATYPE_ENUM : return "java.lang.Integer";
375 0 : case JAVATYPE_MESSAGE: return "";
376 :
377 : // No default because we want the compiler to complain if any new
378 : // JavaTypes are added.
379 : }
380 :
381 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
382 0 : return "";
383 : }
384 :
385 0 : string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
386 0 : switch (GetJavaType(field)) {
387 0 : case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
388 0 : case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY";
389 0 : case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY";
390 0 : case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY";
391 0 : case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY";
392 0 : case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY";
393 0 : case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY";
394 0 : case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
395 0 : case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY";
396 :
397 : // No default because we want the compiler to complain if any new
398 : // JavaTypes are added.
399 : }
400 :
401 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
402 0 : return "";
403 : }
404 :
405 0 : string DefaultValue(const Params& params, const FieldDescriptor* field) {
406 0 : if (field->label() == FieldDescriptor::LABEL_REPEATED) {
407 0 : return EmptyArrayName(params, field);
408 : }
409 :
410 0 : if (params.use_reference_types_for_primitives()) {
411 0 : if (params.reftypes_primitive_enums()
412 0 : && field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
413 0 : return "Integer.MIN_VALUE";
414 : }
415 0 : return "null";
416 : }
417 :
418 : // Switch on cpp_type since we need to know which default_value_* method
419 : // of FieldDescriptor to call.
420 0 : switch (field->cpp_type()) {
421 : case FieldDescriptor::CPPTYPE_INT32:
422 0 : return SimpleItoa(field->default_value_int32());
423 : case FieldDescriptor::CPPTYPE_UINT32:
424 : // Need to print as a signed int since Java has no unsigned.
425 0 : return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
426 : case FieldDescriptor::CPPTYPE_INT64:
427 0 : return SimpleItoa(field->default_value_int64()) + "L";
428 : case FieldDescriptor::CPPTYPE_UINT64:
429 0 : return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
430 0 : "L";
431 : case FieldDescriptor::CPPTYPE_DOUBLE: {
432 0 : double value = field->default_value_double();
433 0 : if (value == numeric_limits<double>::infinity()) {
434 0 : return "Double.POSITIVE_INFINITY";
435 0 : } else if (value == -numeric_limits<double>::infinity()) {
436 0 : return "Double.NEGATIVE_INFINITY";
437 0 : } else if (value != value) {
438 0 : return "Double.NaN";
439 : } else {
440 0 : return SimpleDtoa(value) + "D";
441 : }
442 : }
443 : case FieldDescriptor::CPPTYPE_FLOAT: {
444 0 : float value = field->default_value_float();
445 0 : if (value == numeric_limits<float>::infinity()) {
446 0 : return "Float.POSITIVE_INFINITY";
447 0 : } else if (value == -numeric_limits<float>::infinity()) {
448 0 : return "Float.NEGATIVE_INFINITY";
449 0 : } else if (value != value) {
450 0 : return "Float.NaN";
451 : } else {
452 0 : return SimpleFtoa(value) + "F";
453 : }
454 : }
455 : case FieldDescriptor::CPPTYPE_BOOL:
456 0 : return field->default_value_bool() ? "true" : "false";
457 : case FieldDescriptor::CPPTYPE_STRING:
458 0 : if (!field->default_value_string().empty()) {
459 : // Point it to the static final in the generated code.
460 0 : return FieldDefaultConstantName(field);
461 : } else {
462 0 : if (field->type() == FieldDescriptor::TYPE_BYTES) {
463 0 : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
464 : } else {
465 0 : return "\"\"";
466 : }
467 : }
468 :
469 : case FieldDescriptor::CPPTYPE_ENUM:
470 0 : return ClassName(params, field->enum_type()) + "." +
471 0 : RenameJavaKeywords(field->default_value_enum()->name());
472 :
473 : case FieldDescriptor::CPPTYPE_MESSAGE:
474 0 : return "null";
475 :
476 : // No default because we want the compiler to complain if any new
477 : // types are added.
478 : }
479 :
480 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
481 0 : return "";
482 : }
483 :
484 :
485 : static const char* kBitMasks[] = {
486 : "0x00000001",
487 : "0x00000002",
488 : "0x00000004",
489 : "0x00000008",
490 : "0x00000010",
491 : "0x00000020",
492 : "0x00000040",
493 : "0x00000080",
494 :
495 : "0x00000100",
496 : "0x00000200",
497 : "0x00000400",
498 : "0x00000800",
499 : "0x00001000",
500 : "0x00002000",
501 : "0x00004000",
502 : "0x00008000",
503 :
504 : "0x00010000",
505 : "0x00020000",
506 : "0x00040000",
507 : "0x00080000",
508 : "0x00100000",
509 : "0x00200000",
510 : "0x00400000",
511 : "0x00800000",
512 :
513 : "0x01000000",
514 : "0x02000000",
515 : "0x04000000",
516 : "0x08000000",
517 : "0x10000000",
518 : "0x20000000",
519 : "0x40000000",
520 : "0x80000000",
521 : };
522 :
523 0 : string GetBitFieldName(int index) {
524 0 : string var_name = "bitField";
525 0 : var_name += SimpleItoa(index);
526 : var_name += "_";
527 0 : return var_name;
528 : }
529 :
530 0 : string GetBitFieldNameForBit(int bit_index) {
531 0 : return GetBitFieldName(bit_index / 32);
532 : }
533 :
534 0 : string GenerateGetBit(int bit_index) {
535 0 : string var_name = GetBitFieldNameForBit(bit_index);
536 0 : int bit_in_var_index = bit_index % 32;
537 :
538 0 : string mask = kBitMasks[bit_in_var_index];
539 0 : string result = "((" + var_name + " & " + mask + ") != 0)";
540 0 : return result;
541 : }
542 :
543 0 : string GenerateSetBit(int bit_index) {
544 0 : string var_name = GetBitFieldNameForBit(bit_index);
545 0 : int bit_in_var_index = bit_index % 32;
546 :
547 0 : string mask = kBitMasks[bit_in_var_index];
548 0 : string result = var_name + " |= " + mask;
549 0 : return result;
550 : }
551 :
552 0 : string GenerateClearBit(int bit_index) {
553 0 : string var_name = GetBitFieldNameForBit(bit_index);
554 0 : int bit_in_var_index = bit_index % 32;
555 :
556 0 : string mask = kBitMasks[bit_in_var_index];
557 0 : string result = var_name + " = (" + var_name + " & ~" + mask + ")";
558 0 : return result;
559 : }
560 :
561 0 : string GenerateDifferentBit(int bit_index) {
562 0 : string var_name = GetBitFieldNameForBit(bit_index);
563 0 : int bit_in_var_index = bit_index % 32;
564 :
565 0 : string mask = kBitMasks[bit_in_var_index];
566 0 : string result = "((" + var_name + " & " + mask
567 0 : + ") != (other." + var_name + " & " + mask + "))";
568 0 : return result;
569 : }
570 :
571 0 : void SetBitOperationVariables(const string name,
572 : int bitIndex, map<string, string>* variables) {
573 0 : (*variables)["get_" + name] = GenerateGetBit(bitIndex);
574 0 : (*variables)["set_" + name] = GenerateSetBit(bitIndex);
575 0 : (*variables)["clear_" + name] = GenerateClearBit(bitIndex);
576 0 : (*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
577 0 : }
578 :
579 0 : bool HasMapField(const Descriptor* descriptor) {
580 0 : for (int i = 0; i < descriptor->field_count(); ++i) {
581 0 : const FieldDescriptor* field = descriptor->field(i);
582 0 : if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
583 0 : IsMapEntry(field->message_type())) {
584 : return true;
585 : }
586 : }
587 : return false;
588 : }
589 :
590 : } // namespace javanano
591 : } // namespace compiler
592 : } // namespace protobuf
593 51 : } // namespace google
|