javanano_helpers.cc revision 5cc242074f189837b38e7768b57ccfb0bca258df
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
267JavaType GetJavaType(FieldDescriptor::Type field_type) {
268  switch (field_type) {
269    case FieldDescriptor::TYPE_INT32:
270    case FieldDescriptor::TYPE_UINT32:
271    case FieldDescriptor::TYPE_SINT32:
272    case FieldDescriptor::TYPE_FIXED32:
273    case FieldDescriptor::TYPE_SFIXED32:
274      return JAVATYPE_INT;
275
276    case FieldDescriptor::TYPE_INT64:
277    case FieldDescriptor::TYPE_UINT64:
278    case FieldDescriptor::TYPE_SINT64:
279    case FieldDescriptor::TYPE_FIXED64:
280    case FieldDescriptor::TYPE_SFIXED64:
281      return JAVATYPE_LONG;
282
283    case FieldDescriptor::TYPE_FLOAT:
284      return JAVATYPE_FLOAT;
285
286    case FieldDescriptor::TYPE_DOUBLE:
287      return JAVATYPE_DOUBLE;
288
289    case FieldDescriptor::TYPE_BOOL:
290      return JAVATYPE_BOOLEAN;
291
292    case FieldDescriptor::TYPE_STRING:
293      return JAVATYPE_STRING;
294
295    case FieldDescriptor::TYPE_BYTES:
296      return JAVATYPE_BYTES;
297
298    case FieldDescriptor::TYPE_ENUM:
299      return JAVATYPE_ENUM;
300
301    case FieldDescriptor::TYPE_GROUP:
302    case FieldDescriptor::TYPE_MESSAGE:
303      return JAVATYPE_MESSAGE;
304
305    // No default because we want the compiler to complain if any new
306    // types are added.
307  }
308
309  GOOGLE_LOG(FATAL) << "Can't get here.";
310  return JAVATYPE_INT;
311}
312
313const char* BoxedPrimitiveTypeName(JavaType type) {
314  switch (type) {
315    case JAVATYPE_INT    : return "java.lang.Integer";
316    case JAVATYPE_LONG   : return "java.lang.Long";
317    case JAVATYPE_FLOAT  : return "java.lang.Float";
318    case JAVATYPE_DOUBLE : return "java.lang.Double";
319    case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
320    case JAVATYPE_STRING : return "java.lang.String";
321    case JAVATYPE_BYTES  : return "byte[]";
322    case JAVATYPE_ENUM   : return "java.lang.Integer";
323    case JAVATYPE_MESSAGE: return NULL;
324
325    // No default because we want the compiler to complain if any new
326    // JavaTypes are added.
327  }
328
329  GOOGLE_LOG(FATAL) << "Can't get here.";
330  return NULL;
331}
332
333string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
334  switch (GetJavaType(field)) {
335    case JAVATYPE_INT    : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
336    case JAVATYPE_LONG   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY";
337    case JAVATYPE_FLOAT  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY";
338    case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY";
339    case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY";
340    case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY";
341    case JAVATYPE_BYTES  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY";
342    case JAVATYPE_ENUM   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
343    case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY";
344
345    // No default because we want the compiler to complain if any new
346    // JavaTypes are added.
347  }
348
349  GOOGLE_LOG(FATAL) << "Can't get here.";
350  return "";
351}
352
353string DefaultValue(const Params& params, const FieldDescriptor* field) {
354  if (field->label() == FieldDescriptor::LABEL_REPEATED) {
355    return EmptyArrayName(params, field);
356  }
357
358  if (params.use_reference_types_for_primitives()) {
359    return "null";
360  }
361
362  // Switch on cpp_type since we need to know which default_value_* method
363  // of FieldDescriptor to call.
364  switch (field->cpp_type()) {
365    case FieldDescriptor::CPPTYPE_INT32:
366      return SimpleItoa(field->default_value_int32());
367    case FieldDescriptor::CPPTYPE_UINT32:
368      // Need to print as a signed int since Java has no unsigned.
369      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
370    case FieldDescriptor::CPPTYPE_INT64:
371      return SimpleItoa(field->default_value_int64()) + "L";
372    case FieldDescriptor::CPPTYPE_UINT64:
373      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
374             "L";
375    case FieldDescriptor::CPPTYPE_DOUBLE: {
376      double value = field->default_value_double();
377      if (value == numeric_limits<double>::infinity()) {
378        return "Double.POSITIVE_INFINITY";
379      } else if (value == -numeric_limits<double>::infinity()) {
380        return "Double.NEGATIVE_INFINITY";
381      } else if (value != value) {
382        return "Double.NaN";
383      } else {
384        return SimpleDtoa(value) + "D";
385      }
386    }
387    case FieldDescriptor::CPPTYPE_FLOAT: {
388      float value = field->default_value_float();
389      if (value == numeric_limits<float>::infinity()) {
390        return "Float.POSITIVE_INFINITY";
391      } else if (value == -numeric_limits<float>::infinity()) {
392        return "Float.NEGATIVE_INFINITY";
393      } else if (value != value) {
394        return "Float.NaN";
395      } else {
396        return SimpleFtoa(value) + "F";
397      }
398    }
399    case FieldDescriptor::CPPTYPE_BOOL:
400      return field->default_value_bool() ? "true" : "false";
401    case FieldDescriptor::CPPTYPE_STRING:
402      if (!field->default_value_string().empty()) {
403        // Point it to the static final in the generated code.
404        return FieldDefaultConstantName(field);
405      } else {
406        if (field->type() == FieldDescriptor::TYPE_BYTES) {
407          return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
408        } else {
409          return "\"\"";
410        }
411      }
412
413    case FieldDescriptor::CPPTYPE_ENUM:
414      return ClassName(params, field->enum_type()) + "." +
415             field->default_value_enum()->name();
416
417    case FieldDescriptor::CPPTYPE_MESSAGE:
418      return "null";
419
420    // No default because we want the compiler to complain if any new
421    // types are added.
422  }
423
424  GOOGLE_LOG(FATAL) << "Can't get here.";
425  return "";
426}
427
428
429static const char* kBitMasks[] = {
430  "0x00000001",
431  "0x00000002",
432  "0x00000004",
433  "0x00000008",
434  "0x00000010",
435  "0x00000020",
436  "0x00000040",
437  "0x00000080",
438
439  "0x00000100",
440  "0x00000200",
441  "0x00000400",
442  "0x00000800",
443  "0x00001000",
444  "0x00002000",
445  "0x00004000",
446  "0x00008000",
447
448  "0x00010000",
449  "0x00020000",
450  "0x00040000",
451  "0x00080000",
452  "0x00100000",
453  "0x00200000",
454  "0x00400000",
455  "0x00800000",
456
457  "0x01000000",
458  "0x02000000",
459  "0x04000000",
460  "0x08000000",
461  "0x10000000",
462  "0x20000000",
463  "0x40000000",
464  "0x80000000",
465};
466
467string GetBitFieldName(int index) {
468  string var_name = "bitField";
469  var_name += SimpleItoa(index);
470  var_name += "_";
471  return var_name;
472}
473
474string GetBitFieldNameForBit(int bit_index) {
475  return GetBitFieldName(bit_index / 32);
476}
477
478string GenerateGetBit(int bit_index) {
479  string var_name = GetBitFieldNameForBit(bit_index);
480  int bit_in_var_index = bit_index % 32;
481
482  string mask = kBitMasks[bit_in_var_index];
483  string result = "((" + var_name + " & " + mask + ") != 0)";
484  return result;
485}
486
487string GenerateSetBit(int bit_index) {
488  string var_name = GetBitFieldNameForBit(bit_index);
489  int bit_in_var_index = bit_index % 32;
490
491  string mask = kBitMasks[bit_in_var_index];
492  string result = var_name + " |= " + mask;
493  return result;
494}
495
496string GenerateClearBit(int bit_index) {
497  string var_name = GetBitFieldNameForBit(bit_index);
498  int bit_in_var_index = bit_index % 32;
499
500  string mask = kBitMasks[bit_in_var_index];
501  string result = var_name + " = (" + var_name + " & ~" + mask + ")";
502  return result;
503}
504
505string GenerateDifferentBit(int bit_index) {
506  string var_name = GetBitFieldNameForBit(bit_index);
507  int bit_in_var_index = bit_index % 32;
508
509  string mask = kBitMasks[bit_in_var_index];
510  string result = "((" + var_name + " & " + mask
511      + ") != (other." + var_name + " & " + mask + "))";
512  return result;
513}
514
515void SetBitOperationVariables(const string name,
516    int bitIndex, map<string, string>* variables) {
517  (*variables)["get_" + name] = GenerateGetBit(bitIndex);
518  (*variables)["set_" + name] = GenerateSetBit(bitIndex);
519  (*variables)["clear_" + name] = GenerateClearBit(bitIndex);
520  (*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
521}
522
523}  // namespace javanano
524}  // namespace compiler
525}  // namespace protobuf
526}  // namespace google
527