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   // 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 
    208 string 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 
    223 string 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 
    230 string 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 
    241 string FieldConstantName(const FieldDescriptor *field) {
    242   string name = field->name() + "_FIELD_NUMBER";
    243   UpperString(&name);
    244   return name;
    245 }
    246 
    247 string FieldDefaultConstantName(const FieldDescriptor *field) {
    248   string name = field->name() + "_DEFAULT";
    249   UpperString(&name);
    250   return name;
    251 }
    252 
    253 JavaType 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 
    299 const 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 
    319 string 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 
    339 string 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 }  // namespace javanano
    411 }  // namespace compiler
    412 }  // namespace protobuf
    413 }  // namespace google
    414