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 UnderscoresToCamelCase(const OneofDescriptor* oneof) {
    158   return UnderscoresToCamelCaseImpl(oneof->name(), false);
    159 }
    160 
    161 string UnderscoresToCapitalizedCamelCase(const OneofDescriptor* oneof) {
    162   return UnderscoresToCamelCaseImpl(oneof->name(), true);
    163 }
    164 
    165 string RenameJavaKeywords(const string& input) {
    166   return sRenameKeywords.RenameJavaKeywordsImpl(input);
    167 }
    168 
    169 string StripProto(const string& filename) {
    170   if (HasSuffixString(filename, ".protodevel")) {
    171     return StripSuffixString(filename, ".protodevel");
    172   } else {
    173     return StripSuffixString(filename, ".proto");
    174   }
    175 }
    176 
    177 string FileClassName(const Params& params, const FileDescriptor* file) {
    178   if (params.has_java_outer_classname(file->name())) {
    179     return params.java_outer_classname(file->name());
    180   } else {
    181     // Use the filename itself with underscores removed
    182     // and a CamelCase style name.
    183     string basename;
    184     string::size_type last_slash = file->name().find_last_of('/');
    185     if (last_slash == string::npos) {
    186       basename = file->name();
    187     } else {
    188       basename = file->name().substr(last_slash + 1);
    189     }
    190     return UnderscoresToCamelCaseImpl(StripProto(basename), true);
    191   }
    192 }
    193 
    194 string FileJavaPackage(const Params& params, const FileDescriptor* file) {
    195   if (params.has_java_package(file->name())) {
    196     return params.java_package(file->name());
    197   } else {
    198     string result = kDefaultPackage;
    199     if (!file->package().empty()) {
    200       if (!result.empty()) result += '.';
    201       result += file->package();
    202     }
    203 
    204     if (!result.empty()) {
    205       result += ".";
    206     }
    207     result += "nano";
    208 
    209     return result;
    210   }
    211 }
    212 
    213 bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) {
    214   // If java_multiple_files is false, the outer class is always needed.
    215   if (!params.java_multiple_files(file->name())) {
    216     return true;
    217   }
    218 
    219   // File-scope extensions need the outer class as the scope.
    220   if (file->extension_count() != 0) {
    221     return true;
    222   }
    223 
    224   // If container interfaces are not generated, file-scope enums need the
    225   // outer class as the scope.
    226   if (file->enum_type_count() != 0 && !params.java_enum_style()) {
    227     return true;
    228   }
    229 
    230   return false;
    231 }
    232 
    233 string ToJavaName(const Params& params, const string& name, bool is_class,
    234     const Descriptor* parent, const FileDescriptor* file) {
    235   string result;
    236   if (parent != NULL) {
    237     result.append(ClassName(params, parent));
    238   } else if (is_class && params.java_multiple_files(file->name())) {
    239     result.append(FileJavaPackage(params, file));
    240   } else {
    241     result.append(ClassName(params, file));
    242   }
    243   if (!result.empty()) result.append(1, '.');
    244   result.append(RenameJavaKeywords(name));
    245   return result;
    246 }
    247 
    248 string ClassName(const Params& params, const FileDescriptor* descriptor) {
    249   string result = FileJavaPackage(params, descriptor);
    250   if (!result.empty()) result += '.';
    251   result += FileClassName(params, descriptor);
    252   return result;
    253 }
    254 
    255 string ClassName(const Params& params, const EnumDescriptor* descriptor) {
    256   const Descriptor* parent = descriptor->containing_type();
    257   // When using Java enum style, an enum's class name contains the enum name.
    258   // Use the standard ToJavaName translation.
    259   if (params.java_enum_style()) {
    260     return ToJavaName(params, descriptor->name(), true, parent,
    261                       descriptor->file());
    262   }
    263   // Otherwise the enum members are accessed from the enclosing class.
    264   if (parent != NULL) {
    265     return ClassName(params, parent);
    266   } else {
    267     return ClassName(params, descriptor->file());
    268   }
    269 }
    270 
    271 string FieldConstantName(const FieldDescriptor *field) {
    272   string name = field->name() + "_FIELD_NUMBER";
    273   UpperString(&name);
    274   return name;
    275 }
    276 
    277 string FieldDefaultConstantName(const FieldDescriptor *field) {
    278   return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default";
    279 }
    280 
    281 void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
    282   // We don't want to print group bodies so we cut off after the first line
    283   // (the second line for extensions).
    284   string def = field->DebugString();
    285   string::size_type first_line_end = def.find_first_of('\n');
    286   printer->Print("// $def$\n",
    287     "def", def.substr(0, first_line_end));
    288   if (field->is_extension()) {
    289     string::size_type second_line_start = first_line_end + 1;
    290     string::size_type second_line_length =
    291         def.find('\n', second_line_start) - second_line_start;
    292     printer->Print("// $def$\n",
    293       "def", def.substr(second_line_start, second_line_length));
    294   }
    295 }
    296 
    297 JavaType GetJavaType(FieldDescriptor::Type field_type) {
    298   switch (field_type) {
    299     case FieldDescriptor::TYPE_INT32:
    300     case FieldDescriptor::TYPE_UINT32:
    301     case FieldDescriptor::TYPE_SINT32:
    302     case FieldDescriptor::TYPE_FIXED32:
    303     case FieldDescriptor::TYPE_SFIXED32:
    304       return JAVATYPE_INT;
    305 
    306     case FieldDescriptor::TYPE_INT64:
    307     case FieldDescriptor::TYPE_UINT64:
    308     case FieldDescriptor::TYPE_SINT64:
    309     case FieldDescriptor::TYPE_FIXED64:
    310     case FieldDescriptor::TYPE_SFIXED64:
    311       return JAVATYPE_LONG;
    312 
    313     case FieldDescriptor::TYPE_FLOAT:
    314       return JAVATYPE_FLOAT;
    315 
    316     case FieldDescriptor::TYPE_DOUBLE:
    317       return JAVATYPE_DOUBLE;
    318 
    319     case FieldDescriptor::TYPE_BOOL:
    320       return JAVATYPE_BOOLEAN;
    321 
    322     case FieldDescriptor::TYPE_STRING:
    323       return JAVATYPE_STRING;
    324 
    325     case FieldDescriptor::TYPE_BYTES:
    326       return JAVATYPE_BYTES;
    327 
    328     case FieldDescriptor::TYPE_ENUM:
    329       return JAVATYPE_ENUM;
    330 
    331     case FieldDescriptor::TYPE_GROUP:
    332     case FieldDescriptor::TYPE_MESSAGE:
    333       return JAVATYPE_MESSAGE;
    334 
    335     // No default because we want the compiler to complain if any new
    336     // types are added.
    337   }
    338 
    339   GOOGLE_LOG(FATAL) << "Can't get here.";
    340   return JAVATYPE_INT;
    341 }
    342 
    343 string PrimitiveTypeName(JavaType type) {
    344   switch (type) {
    345     case JAVATYPE_INT    : return "int";
    346     case JAVATYPE_LONG   : return "long";
    347     case JAVATYPE_FLOAT  : return "float";
    348     case JAVATYPE_DOUBLE : return "double";
    349     case JAVATYPE_BOOLEAN: return "boolean";
    350     case JAVATYPE_STRING : return "java.lang.String";
    351     case JAVATYPE_BYTES  : return "byte[]";
    352     case JAVATYPE_ENUM   : return "int";
    353     case JAVATYPE_MESSAGE: return "";
    354 
    355     // No default because we want the compiler to complain if any new
    356     // JavaTypes are added.
    357   }
    358 
    359   GOOGLE_LOG(FATAL) << "Can't get here.";
    360   return "";
    361 }
    362 
    363 string BoxedPrimitiveTypeName(JavaType type) {
    364   switch (type) {
    365     case JAVATYPE_INT    : return "java.lang.Integer";
    366     case JAVATYPE_LONG   : return "java.lang.Long";
    367     case JAVATYPE_FLOAT  : return "java.lang.Float";
    368     case JAVATYPE_DOUBLE : return "java.lang.Double";
    369     case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
    370     case JAVATYPE_STRING : return "java.lang.String";
    371     case JAVATYPE_BYTES  : return "byte[]";
    372     case JAVATYPE_ENUM   : return "java.lang.Integer";
    373     case JAVATYPE_MESSAGE: return "";
    374 
    375     // No default because we want the compiler to complain if any new
    376     // JavaTypes are added.
    377   }
    378 
    379   GOOGLE_LOG(FATAL) << "Can't get here.";
    380   return "";
    381 }
    382 
    383 string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
    384   switch (GetJavaType(field)) {
    385     case JAVATYPE_INT    : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
    386     case JAVATYPE_LONG   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY";
    387     case JAVATYPE_FLOAT  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY";
    388     case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY";
    389     case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY";
    390     case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY";
    391     case JAVATYPE_BYTES  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY";
    392     case JAVATYPE_ENUM   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
    393     case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY";
    394 
    395     // No default because we want the compiler to complain if any new
    396     // JavaTypes are added.
    397   }
    398 
    399   GOOGLE_LOG(FATAL) << "Can't get here.";
    400   return "";
    401 }
    402 
    403 string DefaultValue(const Params& params, const FieldDescriptor* field) {
    404   if (field->label() == FieldDescriptor::LABEL_REPEATED) {
    405     return EmptyArrayName(params, field);
    406   }
    407 
    408   if (params.use_reference_types_for_primitives()) {
    409     if (params.reftypes_primitive_enums()
    410           && field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
    411       return "Integer.MIN_VALUE";
    412     }
    413     return "null";
    414   }
    415 
    416   // Switch on cpp_type since we need to know which default_value_* method
    417   // of FieldDescriptor to call.
    418   switch (field->cpp_type()) {
    419     case FieldDescriptor::CPPTYPE_INT32:
    420       return SimpleItoa(field->default_value_int32());
    421     case FieldDescriptor::CPPTYPE_UINT32:
    422       // Need to print as a signed int since Java has no unsigned.
    423       return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
    424     case FieldDescriptor::CPPTYPE_INT64:
    425       return SimpleItoa(field->default_value_int64()) + "L";
    426     case FieldDescriptor::CPPTYPE_UINT64:
    427       return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
    428              "L";
    429     case FieldDescriptor::CPPTYPE_DOUBLE: {
    430       double value = field->default_value_double();
    431       if (value == numeric_limits<double>::infinity()) {
    432         return "Double.POSITIVE_INFINITY";
    433       } else if (value == -numeric_limits<double>::infinity()) {
    434         return "Double.NEGATIVE_INFINITY";
    435       } else if (value != value) {
    436         return "Double.NaN";
    437       } else {
    438         return SimpleDtoa(value) + "D";
    439       }
    440     }
    441     case FieldDescriptor::CPPTYPE_FLOAT: {
    442       float value = field->default_value_float();
    443       if (value == numeric_limits<float>::infinity()) {
    444         return "Float.POSITIVE_INFINITY";
    445       } else if (value == -numeric_limits<float>::infinity()) {
    446         return "Float.NEGATIVE_INFINITY";
    447       } else if (value != value) {
    448         return "Float.NaN";
    449       } else {
    450         return SimpleFtoa(value) + "F";
    451       }
    452     }
    453     case FieldDescriptor::CPPTYPE_BOOL:
    454       return field->default_value_bool() ? "true" : "false";
    455     case FieldDescriptor::CPPTYPE_STRING:
    456       if (!field->default_value_string().empty()) {
    457         // Point it to the static final in the generated code.
    458         return FieldDefaultConstantName(field);
    459       } else {
    460         if (field->type() == FieldDescriptor::TYPE_BYTES) {
    461           return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
    462         } else {
    463           return "\"\"";
    464         }
    465       }
    466 
    467     case FieldDescriptor::CPPTYPE_ENUM:
    468       return ClassName(params, field->enum_type()) + "." +
    469              RenameJavaKeywords(field->default_value_enum()->name());
    470 
    471     case FieldDescriptor::CPPTYPE_MESSAGE:
    472       return "null";
    473 
    474     // No default because we want the compiler to complain if any new
    475     // types are added.
    476   }
    477 
    478   GOOGLE_LOG(FATAL) << "Can't get here.";
    479   return "";
    480 }
    481 
    482 
    483 static const char* kBitMasks[] = {
    484   "0x00000001",
    485   "0x00000002",
    486   "0x00000004",
    487   "0x00000008",
    488   "0x00000010",
    489   "0x00000020",
    490   "0x00000040",
    491   "0x00000080",
    492 
    493   "0x00000100",
    494   "0x00000200",
    495   "0x00000400",
    496   "0x00000800",
    497   "0x00001000",
    498   "0x00002000",
    499   "0x00004000",
    500   "0x00008000",
    501 
    502   "0x00010000",
    503   "0x00020000",
    504   "0x00040000",
    505   "0x00080000",
    506   "0x00100000",
    507   "0x00200000",
    508   "0x00400000",
    509   "0x00800000",
    510 
    511   "0x01000000",
    512   "0x02000000",
    513   "0x04000000",
    514   "0x08000000",
    515   "0x10000000",
    516   "0x20000000",
    517   "0x40000000",
    518   "0x80000000",
    519 };
    520 
    521 string GetBitFieldName(int index) {
    522   string var_name = "bitField";
    523   var_name += SimpleItoa(index);
    524   var_name += "_";
    525   return var_name;
    526 }
    527 
    528 string GetBitFieldNameForBit(int bit_index) {
    529   return GetBitFieldName(bit_index / 32);
    530 }
    531 
    532 string GenerateGetBit(int bit_index) {
    533   string var_name = GetBitFieldNameForBit(bit_index);
    534   int bit_in_var_index = bit_index % 32;
    535 
    536   string mask = kBitMasks[bit_in_var_index];
    537   string result = "((" + var_name + " & " + mask + ") != 0)";
    538   return result;
    539 }
    540 
    541 string GenerateSetBit(int bit_index) {
    542   string var_name = GetBitFieldNameForBit(bit_index);
    543   int bit_in_var_index = bit_index % 32;
    544 
    545   string mask = kBitMasks[bit_in_var_index];
    546   string result = var_name + " |= " + mask;
    547   return result;
    548 }
    549 
    550 string GenerateClearBit(int bit_index) {
    551   string var_name = GetBitFieldNameForBit(bit_index);
    552   int bit_in_var_index = bit_index % 32;
    553 
    554   string mask = kBitMasks[bit_in_var_index];
    555   string result = var_name + " = (" + var_name + " & ~" + mask + ")";
    556   return result;
    557 }
    558 
    559 string GenerateDifferentBit(int bit_index) {
    560   string var_name = GetBitFieldNameForBit(bit_index);
    561   int bit_in_var_index = bit_index % 32;
    562 
    563   string mask = kBitMasks[bit_in_var_index];
    564   string result = "((" + var_name + " & " + mask
    565       + ") != (other." + var_name + " & " + mask + "))";
    566   return result;
    567 }
    568 
    569 void SetBitOperationVariables(const string name,
    570     int bitIndex, map<string, string>* variables) {
    571   (*variables)["get_" + name] = GenerateGetBit(bitIndex);
    572   (*variables)["set_" + name] = GenerateSetBit(bitIndex);
    573   (*variables)["clear_" + name] = GenerateClearBit(bitIndex);
    574   (*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
    575 }
    576 
    577 bool HasMapField(const Descriptor* descriptor) {
    578   for (int i = 0; i < descriptor->field_count(); ++i) {
    579     const FieldDescriptor* field = descriptor->field(i);
    580     if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
    581         IsMapEntry(field->message_type())) {
    582       return true;
    583     }
    584   }
    585   return false;
    586 }
    587 
    588 }  // namespace javanano
    589 }  // namespace compiler
    590 }  // namespace protobuf
    591 }  // namespace google
    592