javanano_helpers.cc revision ccc48faf20dbf3b3cddcffe78d198876d543529b
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  if (params.use_reference_types_for_primitives()) {
361    return "null";
362  }
363
364  // Switch on cpp_type since we need to know which default_value_* method
365  // of FieldDescriptor to call.
366  switch (field->cpp_type()) {
367    case FieldDescriptor::CPPTYPE_INT32:
368      return SimpleItoa(field->default_value_int32());
369    case FieldDescriptor::CPPTYPE_UINT32:
370      // Need to print as a signed int since Java has no unsigned.
371      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
372    case FieldDescriptor::CPPTYPE_INT64:
373      return SimpleItoa(field->default_value_int64()) + "L";
374    case FieldDescriptor::CPPTYPE_UINT64:
375      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
376             "L";
377    case FieldDescriptor::CPPTYPE_DOUBLE: {
378      double value = field->default_value_double();
379      if (value == numeric_limits<double>::infinity()) {
380        return "Double.POSITIVE_INFINITY";
381      } else if (value == -numeric_limits<double>::infinity()) {
382        return "Double.NEGATIVE_INFINITY";
383      } else if (value != value) {
384        return "Double.NaN";
385      } else {
386        return SimpleDtoa(value) + "D";
387      }
388    }
389    case FieldDescriptor::CPPTYPE_FLOAT: {
390      float value = field->default_value_float();
391      if (value == numeric_limits<float>::infinity()) {
392        return "Float.POSITIVE_INFINITY";
393      } else if (value == -numeric_limits<float>::infinity()) {
394        return "Float.NEGATIVE_INFINITY";
395      } else if (value != value) {
396        return "Float.NaN";
397      } else {
398        return SimpleFtoa(value) + "F";
399      }
400    }
401    case FieldDescriptor::CPPTYPE_BOOL:
402      return field->default_value_bool() ? "true" : "false";
403    case FieldDescriptor::CPPTYPE_STRING:
404      if (!field->default_value_string().empty()) {
405        // Point it to the static final in the generated code.
406        return FieldDefaultConstantName(field);
407      } else {
408        if (field->type() == FieldDescriptor::TYPE_BYTES) {
409          return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
410        } else {
411          return "\"\"";
412        }
413      }
414
415    case FieldDescriptor::CPPTYPE_ENUM:
416      return ClassName(params, field->enum_type()) + "." +
417             field->default_value_enum()->name();
418
419    case FieldDescriptor::CPPTYPE_MESSAGE:
420      return "null";
421
422    // No default because we want the compiler to complain if any new
423    // types are added.
424  }
425
426  GOOGLE_LOG(FATAL) << "Can't get here.";
427  return "";
428}
429
430
431static const char* kBitMasks[] = {
432  "0x00000001",
433  "0x00000002",
434  "0x00000004",
435  "0x00000008",
436  "0x00000010",
437  "0x00000020",
438  "0x00000040",
439  "0x00000080",
440
441  "0x00000100",
442  "0x00000200",
443  "0x00000400",
444  "0x00000800",
445  "0x00001000",
446  "0x00002000",
447  "0x00004000",
448  "0x00008000",
449
450  "0x00010000",
451  "0x00020000",
452  "0x00040000",
453  "0x00080000",
454  "0x00100000",
455  "0x00200000",
456  "0x00400000",
457  "0x00800000",
458
459  "0x01000000",
460  "0x02000000",
461  "0x04000000",
462  "0x08000000",
463  "0x10000000",
464  "0x20000000",
465  "0x40000000",
466  "0x80000000",
467};
468
469string GetBitFieldName(int index) {
470  string var_name = "bitField";
471  var_name += SimpleItoa(index);
472  var_name += "_";
473  return var_name;
474}
475
476string GetBitFieldNameForBit(int bit_index) {
477  return GetBitFieldName(bit_index / 32);
478}
479
480string GenerateGetBit(int bit_index) {
481  string var_name = GetBitFieldNameForBit(bit_index);
482  int bit_in_var_index = bit_index % 32;
483
484  string mask = kBitMasks[bit_in_var_index];
485  string result = "((" + var_name + " & " + mask + ") != 0)";
486  return result;
487}
488
489string GenerateSetBit(int bit_index) {
490  string var_name = GetBitFieldNameForBit(bit_index);
491  int bit_in_var_index = bit_index % 32;
492
493  string mask = kBitMasks[bit_in_var_index];
494  string result = var_name + " |= " + mask;
495  return result;
496}
497
498string GenerateClearBit(int bit_index) {
499  string var_name = GetBitFieldNameForBit(bit_index);
500  int bit_in_var_index = bit_index % 32;
501
502  string mask = kBitMasks[bit_in_var_index];
503  string result = var_name + " = (" + var_name + " & ~" + mask + ")";
504  return result;
505}
506
507string GenerateDifferentBit(int bit_index) {
508  string var_name = GetBitFieldNameForBit(bit_index);
509  int bit_in_var_index = bit_index % 32;
510
511  string mask = kBitMasks[bit_in_var_index];
512  string result = "((" + var_name + " & " + mask
513      + ") != (other." + var_name + " & " + mask + "))";
514  return result;
515}
516
517void SetBitOperationVariables(const string name,
518    int bitIndex, map<string, string>* variables) {
519  (*variables)["get_" + name] = GenerateGetBit(bitIndex);
520  (*variables)["set_" + name] = GenerateSetBit(bitIndex);
521  (*variables)["clear_" + name] = GenerateClearBit(bitIndex);
522  (*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
523}
524
525}  // namespace javanano
526}  // namespace compiler
527}  // namespace protobuf
528}  // namespace google
529