Home | History | Annotate | Download | only in javamicro
      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/javamicro/javamicro_helpers.h>
     39 #include <google/protobuf/compiler/javamicro/javamicro_params.h>
     40 #include <google/protobuf/descriptor.pb.h>
     41 #include <google/protobuf/stubs/strutil.h>
     42 #include <google/protobuf/stubs/substitute.h>
     43 
     44 namespace google {
     45 namespace protobuf {
     46 namespace compiler {
     47 namespace javamicro {
     48 
     49 const char kThickSeparator[] =
     50   "// ===================================================================\n";
     51 const char kThinSeparator[] =
     52   "// -------------------------------------------------------------------\n";
     53 
     54 namespace {
     55 
     56 const char* kDefaultPackage = "";
     57 
     58 const string& FieldName(const FieldDescriptor* field) {
     59   // Groups are hacky:  The name of the field is just the lower-cased name
     60   // of the group type.  In Java, though, we would like to retain the original
     61   // capitalization of the type name.
     62   if (field->type() == FieldDescriptor::TYPE_GROUP) {
     63     return field->message_type()->name();
     64   } else {
     65     return field->name();
     66   }
     67 }
     68 
     69 string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
     70   string result;
     71   // Note:  I distrust ctype.h due to locales.
     72   for (int i = 0; i < input.size(); i++) {
     73     if ('a' <= input[i] && input[i] <= 'z') {
     74       if (cap_next_letter) {
     75         result += input[i] + ('A' - 'a');
     76       } else {
     77         result += input[i];
     78       }
     79       cap_next_letter = false;
     80     } else if ('A' <= input[i] && input[i] <= 'Z') {
     81       if (i == 0 && !cap_next_letter) {
     82         // Force first letter to lower-case unless explicitly told to
     83         // capitalize it.
     84         result += input[i] + ('a' - 'A');
     85       } else {
     86         // Capital letters after the first are left as-is.
     87         result += input[i];
     88       }
     89       cap_next_letter = false;
     90     } else if ('0' <= input[i] && input[i] <= '9') {
     91       result += input[i];
     92       cap_next_letter = true;
     93     } else {
     94       cap_next_letter = true;
     95     }
     96   }
     97   return result;
     98 }
     99 
    100 }  // namespace
    101 
    102 string UnderscoresToCamelCase(const FieldDescriptor* field) {
    103   return UnderscoresToCamelCaseImpl(FieldName(field), false);
    104 }
    105 
    106 string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
    107   return UnderscoresToCamelCaseImpl(FieldName(field), true);
    108 }
    109 
    110 string UnderscoresToCamelCase(const MethodDescriptor* method) {
    111   return UnderscoresToCamelCaseImpl(method->name(), false);
    112 }
    113 
    114 string StripProto(const string& filename) {
    115   if (HasSuffixString(filename, ".protodevel")) {
    116     return StripSuffixString(filename, ".protodevel");
    117   } else {
    118     return StripSuffixString(filename, ".proto");
    119   }
    120 }
    121 
    122 string FileClassName(const Params& params, const FileDescriptor* file) {
    123   if (params.has_java_outer_classname(file->name())) {
    124     return params.java_outer_classname(file->name());
    125   } else {
    126     // Use the filename itself with underscores removed
    127     // and a CamelCase style name.
    128     string basename;
    129     string::size_type last_slash = file->name().find_last_of('/');
    130     if (last_slash == string::npos) {
    131       basename = file->name();
    132     } else {
    133       basename = file->name().substr(last_slash + 1);
    134     }
    135     return UnderscoresToCamelCaseImpl(StripProto(basename), true);
    136   }
    137 }
    138 
    139 string FileJavaPackage(const Params& params, const FileDescriptor* file) {
    140   if (params.has_java_package(file->name())) {
    141     return params.java_package(file->name());
    142   } else {
    143     string result = kDefaultPackage;
    144     if (!file->package().empty()) {
    145       if (!result.empty()) result += '.';
    146       result += file->package();
    147     }
    148     return result;
    149   }
    150 }
    151 
    152 bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) {
    153   // Enums and extensions need the outer class as the scope.
    154   if (file->enum_type_count() != 0 || file->extension_count() != 0) {
    155     return true;
    156   }
    157   // Messages need the outer class only if java_multiple_files is false.
    158   return !params.java_multiple_files(file->name());
    159 }
    160 
    161 string ToJavaName(const Params& params, const string& name, bool is_class,
    162     const Descriptor* parent, const FileDescriptor* file) {
    163   string result;
    164   if (parent != NULL) {
    165     result.append(ClassName(params, parent));
    166   } else if (is_class && params.java_multiple_files(file->name())) {
    167     result.append(FileJavaPackage(params, file));
    168   } else {
    169     result.append(ClassName(params, file));
    170   }
    171   if (!result.empty()) result.append(1, '.');
    172   result.append(name); // TODO(maxtroy): add '_' if name is a Java keyword.
    173   return result;
    174 }
    175 
    176 string ClassName(const Params& params, const FileDescriptor* descriptor) {
    177   string result = FileJavaPackage(params, descriptor);
    178   if (!result.empty()) result += '.';
    179   result += FileClassName(params, descriptor);
    180   return result;
    181 }
    182 
    183 string ClassName(const Params& params, const EnumDescriptor* descriptor) {
    184   // An enum's class name is the enclosing message's class name or the outer
    185   // class name.
    186   const Descriptor* parent = descriptor->containing_type();
    187   if (parent != NULL) {
    188     return ClassName(params, parent);
    189   } else {
    190     return ClassName(params, descriptor->file());
    191   }
    192 }
    193 
    194 string FieldConstantName(const FieldDescriptor *field) {
    195   string name = field->name() + "_FIELD_NUMBER";
    196   UpperString(&name);
    197   return name;
    198 }
    199 
    200 JavaType GetJavaType(FieldDescriptor::Type field_type) {
    201   switch (field_type) {
    202     case FieldDescriptor::TYPE_INT32:
    203     case FieldDescriptor::TYPE_UINT32:
    204     case FieldDescriptor::TYPE_SINT32:
    205     case FieldDescriptor::TYPE_FIXED32:
    206     case FieldDescriptor::TYPE_SFIXED32:
    207       return JAVATYPE_INT;
    208 
    209     case FieldDescriptor::TYPE_INT64:
    210     case FieldDescriptor::TYPE_UINT64:
    211     case FieldDescriptor::TYPE_SINT64:
    212     case FieldDescriptor::TYPE_FIXED64:
    213     case FieldDescriptor::TYPE_SFIXED64:
    214       return JAVATYPE_LONG;
    215 
    216     case FieldDescriptor::TYPE_FLOAT:
    217       return JAVATYPE_FLOAT;
    218 
    219     case FieldDescriptor::TYPE_DOUBLE:
    220       return JAVATYPE_DOUBLE;
    221 
    222     case FieldDescriptor::TYPE_BOOL:
    223       return JAVATYPE_BOOLEAN;
    224 
    225     case FieldDescriptor::TYPE_STRING:
    226       return JAVATYPE_STRING;
    227 
    228     case FieldDescriptor::TYPE_BYTES:
    229       return JAVATYPE_BYTES;
    230 
    231     case FieldDescriptor::TYPE_ENUM:
    232       return JAVATYPE_ENUM;
    233 
    234     case FieldDescriptor::TYPE_GROUP:
    235     case FieldDescriptor::TYPE_MESSAGE:
    236       return JAVATYPE_MESSAGE;
    237 
    238     // No default because we want the compiler to complain if any new
    239     // types are added.
    240   }
    241 
    242   GOOGLE_LOG(FATAL) << "Can't get here.";
    243   return JAVATYPE_INT;
    244 }
    245 
    246 const char* BoxedPrimitiveTypeName(JavaType type) {
    247   switch (type) {
    248     case JAVATYPE_INT    : return "java.lang.Integer";
    249     case JAVATYPE_LONG   : return "java.lang.Long";
    250     case JAVATYPE_FLOAT  : return "java.lang.Float";
    251     case JAVATYPE_DOUBLE : return "java.lang.Double";
    252     case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
    253     case JAVATYPE_STRING : return "java.lang.String";
    254     case JAVATYPE_BYTES  : return "com.google.protobuf.micro.ByteStringMicro";
    255     case JAVATYPE_ENUM   : return "java.lang.Integer";
    256     case JAVATYPE_MESSAGE: return NULL;
    257 
    258     // No default because we want the compiler to complain if any new
    259     // JavaTypes are added.
    260   }
    261 
    262   GOOGLE_LOG(FATAL) << "Can't get here.";
    263   return NULL;
    264 }
    265 
    266 bool AllAscii(const string& text) {
    267   for (int i = 0; i < text.size(); i++) {
    268     if ((text[i] & 0x80) != 0) {
    269       return false;
    270     }
    271   }
    272   return true;
    273 }
    274 
    275 string DefaultValue(const Params& params, const FieldDescriptor* field) {
    276   // Switch on cpp_type since we need to know which default_value_* method
    277   // of FieldDescriptor to call.
    278   switch (field->cpp_type()) {
    279     case FieldDescriptor::CPPTYPE_INT32:
    280       return SimpleItoa(field->default_value_int32());
    281     case FieldDescriptor::CPPTYPE_UINT32:
    282       // Need to print as a signed int since Java has no unsigned.
    283       return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
    284     case FieldDescriptor::CPPTYPE_INT64:
    285       return SimpleItoa(field->default_value_int64()) + "L";
    286     case FieldDescriptor::CPPTYPE_UINT64:
    287       return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
    288              "L";
    289     case FieldDescriptor::CPPTYPE_DOUBLE: {
    290      double value = field->default_value_double();
    291       if (value == numeric_limits<double>::infinity()) {
    292         return "Double.POSITIVE_INFINITY";
    293       } else if (value == -numeric_limits<double>::infinity()) {
    294         return "Double.NEGATIVE_INFINITY";
    295       } else if (value != value) {
    296         return "Double.NaN";
    297       } else {
    298         return SimpleDtoa(value) + "D";
    299       }
    300     }
    301     case FieldDescriptor::CPPTYPE_FLOAT: {
    302       float value = field->default_value_float();
    303       if (value == numeric_limits<float>::infinity()) {
    304         return "Float.POSITIVE_INFINITY";
    305       } else if (value == -numeric_limits<float>::infinity()) {
    306         return "Float.NEGATIVE_INFINITY";
    307       } else if (value != value) {
    308         return "Float.NaN";
    309       } else {
    310         return SimpleFtoa(value) + "F";
    311       }
    312     }
    313     case FieldDescriptor::CPPTYPE_BOOL:
    314       return field->default_value_bool() ? "true" : "false";
    315     case FieldDescriptor::CPPTYPE_STRING:
    316       if (field->type() == FieldDescriptor::TYPE_BYTES) {
    317         if (field->has_default_value()) {
    318           // See comments in Internal.java for gory details.
    319           return strings::Substitute(
    320             "com.google.protobuf.micro.ByteStringMicro.copyFromUtf8(\"$0\")",
    321             CEscape(field->default_value_string()));
    322         } else {
    323           return "com.google.protobuf.micro.ByteStringMicro.EMPTY";
    324         }
    325       } else {
    326         if (AllAscii(field->default_value_string())) {
    327           // All chars are ASCII.  In this case CEscape() works fine.
    328           return "\"" + CEscape(field->default_value_string()) + "\"";
    329         } else {
    330           // See comments in Internal.java for gory details.
    331           // BUG: Internal NOT SUPPORTED need to fix!!
    332           return strings::Substitute(
    333             "com.google.protobuf.micro.Internal.stringDefaultValue(\"$0\")",
    334             CEscape(field->default_value_string()));
    335         }
    336       }
    337 
    338     case FieldDescriptor::CPPTYPE_ENUM:
    339       return ClassName(params, field->enum_type()) + "." +
    340              field->default_value_enum()->name();
    341 
    342     case FieldDescriptor::CPPTYPE_MESSAGE:
    343       return ClassName(params, field->message_type()) + ".getDefaultInstance()";
    344 
    345     // No default because we want the compiler to complain if any new
    346     // types are added.
    347   }
    348 
    349   GOOGLE_LOG(FATAL) << "Can't get here.";
    350   return "";
    351 }
    352 
    353 }  // namespace javamicro
    354 }  // namespace compiler
    355 }  // namespace protobuf
    356 }  // namespace google
    357