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