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