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