javanano_helpers.cc revision 47dee56155c7bdb9855e51ff08c99db306d11a2d
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  // Enums and extensions need the outer class as the scope.
201  if (file->enum_type_count() != 0 || file->extension_count() != 0) {
202    return true;
203  }
204  // Messages need the outer class only if java_multiple_files is false.
205  return !params.java_multiple_files(file->name());
206}
207
208string ToJavaName(const Params& params, const string& name, bool is_class,
209    const Descriptor* parent, const FileDescriptor* file) {
210  string result;
211  if (parent != NULL) {
212    result.append(ClassName(params, parent));
213  } else if (is_class && params.java_multiple_files(file->name())) {
214    result.append(FileJavaPackage(params, file));
215  } else {
216    result.append(ClassName(params, file));
217  }
218  if (!result.empty()) result.append(1, '.');
219  result.append(RenameJavaKeywords(name));
220  return result;
221}
222
223string ClassName(const Params& params, const FileDescriptor* descriptor) {
224  string result = FileJavaPackage(params, descriptor);
225  if (!result.empty()) result += '.';
226  result += FileClassName(params, descriptor);
227  return result;
228}
229
230string ClassName(const Params& params, const EnumDescriptor* descriptor) {
231  // An enum's class name is the enclosing message's class name or the outer
232  // class name.
233  const Descriptor* parent = descriptor->containing_type();
234  if (parent != NULL) {
235    return ClassName(params, parent);
236  } else {
237    return ClassName(params, descriptor->file());
238  }
239}
240
241string FieldConstantName(const FieldDescriptor *field) {
242  string name = field->name() + "_FIELD_NUMBER";
243  UpperString(&name);
244  return name;
245}
246
247string FieldDefaultConstantName(const FieldDescriptor *field) {
248  string name = field->name() + "_DEFAULT";
249  UpperString(&name);
250  return name;
251}
252
253JavaType GetJavaType(FieldDescriptor::Type field_type) {
254  switch (field_type) {
255    case FieldDescriptor::TYPE_INT32:
256    case FieldDescriptor::TYPE_UINT32:
257    case FieldDescriptor::TYPE_SINT32:
258    case FieldDescriptor::TYPE_FIXED32:
259    case FieldDescriptor::TYPE_SFIXED32:
260      return JAVATYPE_INT;
261
262    case FieldDescriptor::TYPE_INT64:
263    case FieldDescriptor::TYPE_UINT64:
264    case FieldDescriptor::TYPE_SINT64:
265    case FieldDescriptor::TYPE_FIXED64:
266    case FieldDescriptor::TYPE_SFIXED64:
267      return JAVATYPE_LONG;
268
269    case FieldDescriptor::TYPE_FLOAT:
270      return JAVATYPE_FLOAT;
271
272    case FieldDescriptor::TYPE_DOUBLE:
273      return JAVATYPE_DOUBLE;
274
275    case FieldDescriptor::TYPE_BOOL:
276      return JAVATYPE_BOOLEAN;
277
278    case FieldDescriptor::TYPE_STRING:
279      return JAVATYPE_STRING;
280
281    case FieldDescriptor::TYPE_BYTES:
282      return JAVATYPE_BYTES;
283
284    case FieldDescriptor::TYPE_ENUM:
285      return JAVATYPE_ENUM;
286
287    case FieldDescriptor::TYPE_GROUP:
288    case FieldDescriptor::TYPE_MESSAGE:
289      return JAVATYPE_MESSAGE;
290
291    // No default because we want the compiler to complain if any new
292    // types are added.
293  }
294
295  GOOGLE_LOG(FATAL) << "Can't get here.";
296  return JAVATYPE_INT;
297}
298
299const char* BoxedPrimitiveTypeName(JavaType type) {
300  switch (type) {
301    case JAVATYPE_INT    : return "java.lang.Integer";
302    case JAVATYPE_LONG   : return "java.lang.Long";
303    case JAVATYPE_FLOAT  : return "java.lang.Float";
304    case JAVATYPE_DOUBLE : return "java.lang.Double";
305    case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
306    case JAVATYPE_STRING : return "java.lang.String";
307    case JAVATYPE_BYTES  : return "byte[]";
308    case JAVATYPE_ENUM   : return "java.lang.Integer";
309    case JAVATYPE_MESSAGE: return NULL;
310
311    // No default because we want the compiler to complain if any new
312    // JavaTypes are added.
313  }
314
315  GOOGLE_LOG(FATAL) << "Can't get here.";
316  return NULL;
317}
318
319string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
320  switch (GetJavaType(field)) {
321    case JAVATYPE_INT    : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
322    case JAVATYPE_LONG   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY";
323    case JAVATYPE_FLOAT  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY";
324    case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY";
325    case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY";
326    case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY";
327    case JAVATYPE_BYTES  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY";
328    case JAVATYPE_ENUM   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
329    case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY";
330
331    // No default because we want the compiler to complain if any new
332    // JavaTypes are added.
333  }
334
335  GOOGLE_LOG(FATAL) << "Can't get here.";
336  return "";
337}
338
339string DefaultValue(const Params& params, const FieldDescriptor* field) {
340  if (field->label() == FieldDescriptor::LABEL_REPEATED) {
341    return EmptyArrayName(params, field);
342  }
343
344  // Switch on cpp_type since we need to know which default_value_* method
345  // of FieldDescriptor to call.
346  switch (field->cpp_type()) {
347    case FieldDescriptor::CPPTYPE_INT32:
348      return SimpleItoa(field->default_value_int32());
349    case FieldDescriptor::CPPTYPE_UINT32:
350      // Need to print as a signed int since Java has no unsigned.
351      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
352    case FieldDescriptor::CPPTYPE_INT64:
353      return SimpleItoa(field->default_value_int64()) + "L";
354    case FieldDescriptor::CPPTYPE_UINT64:
355      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
356             "L";
357    case FieldDescriptor::CPPTYPE_DOUBLE: {
358      double value = field->default_value_double();
359      if (value == numeric_limits<double>::infinity()) {
360        return "Double.POSITIVE_INFINITY";
361      } else if (value == -numeric_limits<double>::infinity()) {
362        return "Double.NEGATIVE_INFINITY";
363      } else if (value != value) {
364        return "Double.NaN";
365      } else {
366        return SimpleDtoa(value) + "D";
367      }
368    }
369    case FieldDescriptor::CPPTYPE_FLOAT: {
370      float value = field->default_value_float();
371      if (value == numeric_limits<float>::infinity()) {
372        return "Float.POSITIVE_INFINITY";
373      } else if (value == -numeric_limits<float>::infinity()) {
374        return "Float.NEGATIVE_INFINITY";
375      } else if (value != value) {
376        return "Float.NaN";
377      } else {
378        return SimpleFtoa(value) + "F";
379      }
380    }
381    case FieldDescriptor::CPPTYPE_BOOL:
382      return field->default_value_bool() ? "true" : "false";
383    case FieldDescriptor::CPPTYPE_STRING:
384      if (!field->default_value_string().empty()) {
385        // Point it to the static final in the generated code.
386        return FieldDefaultConstantName(field);
387      } else {
388        if (field->type() == FieldDescriptor::TYPE_BYTES) {
389          return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
390        } else {
391          return "\"\"";
392        }
393      }
394
395    case FieldDescriptor::CPPTYPE_ENUM:
396      return ClassName(params, field->enum_type()) + "." +
397             field->default_value_enum()->name();
398
399    case FieldDescriptor::CPPTYPE_MESSAGE:
400      return "null";
401
402    // No default because we want the compiler to complain if any new
403    // types are added.
404  }
405
406  GOOGLE_LOG(FATAL) << "Can't get here.";
407  return "";
408}
409
410
411static const char* kBitMasks[] = {
412  "0x00000001",
413  "0x00000002",
414  "0x00000004",
415  "0x00000008",
416  "0x00000010",
417  "0x00000020",
418  "0x00000040",
419  "0x00000080",
420
421  "0x00000100",
422  "0x00000200",
423  "0x00000400",
424  "0x00000800",
425  "0x00001000",
426  "0x00002000",
427  "0x00004000",
428  "0x00008000",
429
430  "0x00010000",
431  "0x00020000",
432  "0x00040000",
433  "0x00080000",
434  "0x00100000",
435  "0x00200000",
436  "0x00400000",
437  "0x00800000",
438
439  "0x01000000",
440  "0x02000000",
441  "0x04000000",
442  "0x08000000",
443  "0x10000000",
444  "0x20000000",
445  "0x40000000",
446  "0x80000000",
447};
448
449string GetBitFieldName(int index) {
450  string var_name = "bitField";
451  var_name += SimpleItoa(index);
452  var_name += "_";
453  return var_name;
454}
455
456string GetBitFieldNameForBit(int bit_index) {
457  return GetBitFieldName(bit_index / 32);
458}
459
460string GenerateGetBit(int bit_index) {
461  string var_name = GetBitFieldNameForBit(bit_index);
462  int bit_in_var_index = bit_index % 32;
463
464  string mask = kBitMasks[bit_in_var_index];
465  string result = "((" + var_name + " & " + mask + ") == " + mask + ")";
466  return result;
467}
468
469string GenerateSetBit(int bit_index) {
470  string var_name = GetBitFieldNameForBit(bit_index);
471  int bit_in_var_index = bit_index % 32;
472
473  string mask = kBitMasks[bit_in_var_index];
474  string result = var_name + " |= " + mask;
475  return result;
476}
477
478string GenerateClearBit(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 + " = (" + var_name + " & ~" + mask + ")";
484  return result;
485}
486
487}  // namespace javanano
488}  // namespace compiler
489}  // namespace protobuf
490}  // namespace google
491