javanano_helpers.cc revision 382ddccb550e1c822ef26a0e65988998f7446624
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
45namespace google {
46namespace protobuf {
47namespace compiler {
48namespace javanano {
49
50const char kThickSeparator[] =
51  "// ===================================================================\n";
52const char kThinSeparator[] =
53  "// -------------------------------------------------------------------\n";
54
55class RenameKeywords {
56 private:
57  hash_set<string> java_keywords_set_;
58
59 public:
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)
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
95static RenameKeywords sRenameKeywords;
96
97namespace {
98
99const char* kDefaultPackage = "";
100
101const 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
112string 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
145string UnderscoresToCamelCase(const FieldDescriptor* field) {
146  return UnderscoresToCamelCaseImpl(FieldName(field), false);
147}
148
149string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
150  return UnderscoresToCamelCaseImpl(FieldName(field), true);
151}
152
153string UnderscoresToCamelCase(const MethodDescriptor* method) {
154  return UnderscoresToCamelCaseImpl(method->name(), false);
155}
156
157string RenameJavaKeywords(const string& input) {
158  return sRenameKeywords.RenameJavaKeywordsImpl(input);
159}
160
161string StripProto(const string& filename) {
162  if (HasSuffixString(filename, ".protodevel")) {
163    return StripSuffixString(filename, ".protodevel");
164  } else {
165    return StripSuffixString(filename, ".proto");
166  }
167}
168
169string 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
186string 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
199bool 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
219string 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
234string 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
241string 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
257string FieldConstantName(const FieldDescriptor *field) {
258  string name = field->name() + "_FIELD_NUMBER";
259  UpperString(&name);
260  return name;
261}
262
263string FieldDefaultConstantName(const FieldDescriptor *field) {
264  return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default";
265}
266
267void 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
283JavaType 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
329string 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 NULL;
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 NULL;
347}
348
349string 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 NULL;
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 NULL;
367}
368
369string 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
389string 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    return "null";
396  }
397
398  // Switch on cpp_type since we need to know which default_value_* method
399  // of FieldDescriptor to call.
400  switch (field->cpp_type()) {
401    case FieldDescriptor::CPPTYPE_INT32:
402      return SimpleItoa(field->default_value_int32());
403    case FieldDescriptor::CPPTYPE_UINT32:
404      // Need to print as a signed int since Java has no unsigned.
405      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
406    case FieldDescriptor::CPPTYPE_INT64:
407      return SimpleItoa(field->default_value_int64()) + "L";
408    case FieldDescriptor::CPPTYPE_UINT64:
409      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
410             "L";
411    case FieldDescriptor::CPPTYPE_DOUBLE: {
412      double value = field->default_value_double();
413      if (value == numeric_limits<double>::infinity()) {
414        return "Double.POSITIVE_INFINITY";
415      } else if (value == -numeric_limits<double>::infinity()) {
416        return "Double.NEGATIVE_INFINITY";
417      } else if (value != value) {
418        return "Double.NaN";
419      } else {
420        return SimpleDtoa(value) + "D";
421      }
422    }
423    case FieldDescriptor::CPPTYPE_FLOAT: {
424      float value = field->default_value_float();
425      if (value == numeric_limits<float>::infinity()) {
426        return "Float.POSITIVE_INFINITY";
427      } else if (value == -numeric_limits<float>::infinity()) {
428        return "Float.NEGATIVE_INFINITY";
429      } else if (value != value) {
430        return "Float.NaN";
431      } else {
432        return SimpleFtoa(value) + "F";
433      }
434    }
435    case FieldDescriptor::CPPTYPE_BOOL:
436      return field->default_value_bool() ? "true" : "false";
437    case FieldDescriptor::CPPTYPE_STRING:
438      if (!field->default_value_string().empty()) {
439        // Point it to the static final in the generated code.
440        return FieldDefaultConstantName(field);
441      } else {
442        if (field->type() == FieldDescriptor::TYPE_BYTES) {
443          return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
444        } else {
445          return "\"\"";
446        }
447      }
448
449    case FieldDescriptor::CPPTYPE_ENUM:
450      return ClassName(params, field->enum_type()) + "." +
451             field->default_value_enum()->name();
452
453    case FieldDescriptor::CPPTYPE_MESSAGE:
454      return "null";
455
456    // No default because we want the compiler to complain if any new
457    // types are added.
458  }
459
460  GOOGLE_LOG(FATAL) << "Can't get here.";
461  return "";
462}
463
464
465static const char* kBitMasks[] = {
466  "0x00000001",
467  "0x00000002",
468  "0x00000004",
469  "0x00000008",
470  "0x00000010",
471  "0x00000020",
472  "0x00000040",
473  "0x00000080",
474
475  "0x00000100",
476  "0x00000200",
477  "0x00000400",
478  "0x00000800",
479  "0x00001000",
480  "0x00002000",
481  "0x00004000",
482  "0x00008000",
483
484  "0x00010000",
485  "0x00020000",
486  "0x00040000",
487  "0x00080000",
488  "0x00100000",
489  "0x00200000",
490  "0x00400000",
491  "0x00800000",
492
493  "0x01000000",
494  "0x02000000",
495  "0x04000000",
496  "0x08000000",
497  "0x10000000",
498  "0x20000000",
499  "0x40000000",
500  "0x80000000",
501};
502
503string GetBitFieldName(int index) {
504  string var_name = "bitField";
505  var_name += SimpleItoa(index);
506  var_name += "_";
507  return var_name;
508}
509
510string GetBitFieldNameForBit(int bit_index) {
511  return GetBitFieldName(bit_index / 32);
512}
513
514string GenerateGetBit(int bit_index) {
515  string var_name = GetBitFieldNameForBit(bit_index);
516  int bit_in_var_index = bit_index % 32;
517
518  string mask = kBitMasks[bit_in_var_index];
519  string result = "((" + var_name + " & " + mask + ") != 0)";
520  return result;
521}
522
523string GenerateSetBit(int bit_index) {
524  string var_name = GetBitFieldNameForBit(bit_index);
525  int bit_in_var_index = bit_index % 32;
526
527  string mask = kBitMasks[bit_in_var_index];
528  string result = var_name + " |= " + mask;
529  return result;
530}
531
532string GenerateClearBit(int bit_index) {
533  string var_name = GetBitFieldNameForBit(bit_index);
534  int bit_in_var_index = bit_index % 32;
535
536  string mask = kBitMasks[bit_in_var_index];
537  string result = var_name + " = (" + var_name + " & ~" + mask + ")";
538  return result;
539}
540
541string GenerateDifferentBit(int bit_index) {
542  string var_name = GetBitFieldNameForBit(bit_index);
543  int bit_in_var_index = bit_index % 32;
544
545  string mask = kBitMasks[bit_in_var_index];
546  string result = "((" + var_name + " & " + mask
547      + ") != (other." + var_name + " & " + mask + "))";
548  return result;
549}
550
551void SetBitOperationVariables(const string name,
552    int bitIndex, map<string, string>* variables) {
553  (*variables)["get_" + name] = GenerateGetBit(bitIndex);
554  (*variables)["set_" + name] = GenerateSetBit(bitIndex);
555  (*variables)["clear_" + name] = GenerateClearBit(bitIndex);
556  (*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
557}
558
559}  // namespace javanano
560}  // namespace compiler
561}  // namespace protobuf
562}  // namespace google
563