Home | History | Annotate | Download | only in streaming_proto
      1 #include "Errors.h"
      2 
      3 #include "string_utils.h"
      4 
      5 #include "google/protobuf/compiler/plugin.pb.h"
      6 #include "google/protobuf/io/zero_copy_stream_impl.h"
      7 #include "google/protobuf/text_format.h"
      8 
      9 #include <stdio.h>
     10 #include <iomanip>
     11 #include <iostream>
     12 #include <sstream>
     13 #include <map>
     14 
     15 using namespace android::javastream_proto;
     16 using namespace google::protobuf;
     17 using namespace google::protobuf::compiler;
     18 using namespace google::protobuf::io;
     19 using namespace std;
     20 
     21 const int FIELD_TYPE_SHIFT = 32;
     22 const uint64_t FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
     23 const uint64_t FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
     24 const uint64_t FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT;
     25 const uint64_t FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT;
     26 const uint64_t FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT;
     27 const uint64_t FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT;
     28 const uint64_t FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT;
     29 const uint64_t FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT;
     30 const uint64_t FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT;
     31 const uint64_t FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT;
     32 const uint64_t FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT;
     33 const uint64_t FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT;
     34 const uint64_t FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT;
     35 const uint64_t FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT;
     36 const uint64_t FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT;
     37 const uint64_t FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT;
     38 const uint64_t FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT;
     39 
     40 const int FIELD_COUNT_SHIFT = 40;
     41 const uint64_t FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
     42 const uint64_t FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
     43 const uint64_t FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
     44 
     45 
     46 /**
     47  * See if this is the file for this request, and not one of the imported ones.
     48  */
     49 static bool
     50 should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
     51 {
     52     const int N = request.file_to_generate_size();
     53     for (int i=0; i<N; i++) {
     54         if (request.file_to_generate(i) == file) {
     55             return true;
     56         }
     57     }
     58     return false;
     59 }
     60 
     61 /**
     62  * If the descriptor gives us a class name, use that. Otherwise make one up from
     63  * the filename of the .proto file.
     64  */
     65 static string
     66 make_outer_class_name(const FileDescriptorProto& file_descriptor)
     67 {
     68     string name = file_descriptor.options().java_outer_classname();
     69     if (name.size() == 0) {
     70         name = to_camel_case(file_base_name(file_descriptor.name()));
     71         if (name.size() == 0) {
     72             ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE,
     73                     "Unable to make an outer class name for file: %s",
     74                     file_descriptor.name().c_str());
     75             name = "Unknown";
     76         }
     77     }
     78     return name;
     79 }
     80 
     81 /**
     82  * Figure out the package name that we are generating.
     83  */
     84 static string
     85 make_java_package(const FileDescriptorProto& file_descriptor) {
     86     if (file_descriptor.options().has_java_package()) {
     87         return file_descriptor.options().java_package();
     88     } else {
     89         return file_descriptor.package();
     90     }
     91 }
     92 
     93 /**
     94  * Figure out the name of the file we are generating.
     95  */
     96 static string
     97 make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
     98 {
     99     string const package = make_java_package(file_descriptor);
    100     string result;
    101     if (package.size() > 0) {
    102         result = replace_string(package, '.', '/');
    103         result += '/';
    104     }
    105 
    106     result += class_name;
    107     result += ".java";
    108 
    109     return result;
    110 }
    111 
    112 static string
    113 indent_more(const string& indent)
    114 {
    115     return indent + "    ";
    116 }
    117 
    118 /**
    119  * Write the constants for an enum.
    120  */
    121 static void
    122 write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
    123 {
    124     const int N = enu.value_size();
    125     text << indent << "// enum " << enu.name() << endl;
    126     for (int i=0; i<N; i++) {
    127         const EnumValueDescriptorProto& value = enu.value(i);
    128         text << indent << "public static final int "
    129                 << make_constant_name(value.name())
    130                 << " = " << value.number() << ";" << endl;
    131     }
    132     text << endl;
    133 }
    134 
    135 /**
    136  * Get the string name for a field.
    137  */
    138 static string
    139 get_proto_type(const FieldDescriptorProto& field)
    140 {
    141     switch (field.type()) {
    142         case FieldDescriptorProto::TYPE_DOUBLE:
    143             return "double";
    144         case FieldDescriptorProto::TYPE_FLOAT:
    145             return "float";
    146         case FieldDescriptorProto::TYPE_INT64:
    147             return "int64";
    148         case FieldDescriptorProto::TYPE_UINT64:
    149             return "uint64";
    150         case FieldDescriptorProto::TYPE_INT32:
    151             return "int32";
    152         case FieldDescriptorProto::TYPE_FIXED64:
    153             return "fixed64";
    154         case FieldDescriptorProto::TYPE_FIXED32:
    155             return "fixed32";
    156         case FieldDescriptorProto::TYPE_BOOL:
    157             return "bool";
    158         case FieldDescriptorProto::TYPE_STRING:
    159             return "string";
    160         case FieldDescriptorProto::TYPE_GROUP:
    161             return "group<unsupported!>";
    162         case FieldDescriptorProto::TYPE_MESSAGE:
    163             return field.type_name();
    164         case FieldDescriptorProto::TYPE_BYTES:
    165             return "bytes";
    166         case FieldDescriptorProto::TYPE_UINT32:
    167             return "uint32";
    168         case FieldDescriptorProto::TYPE_ENUM:
    169             return field.type_name();
    170         case FieldDescriptorProto::TYPE_SFIXED32:
    171             return "sfixed32";
    172         case FieldDescriptorProto::TYPE_SFIXED64:
    173             return "sfixed64";
    174         case FieldDescriptorProto::TYPE_SINT32:
    175             return "sint32";
    176         case FieldDescriptorProto::TYPE_SINT64:
    177             return "sint64";
    178         default:
    179             // won't happen
    180             return "void";
    181     }
    182 }
    183 
    184 static uint64_t
    185 get_field_id(const FieldDescriptorProto& field)
    186 {
    187     // Number
    188     uint64_t result = (uint32_t)field.number();
    189 
    190     // Type
    191     switch (field.type()) {
    192         case FieldDescriptorProto::TYPE_DOUBLE:
    193             result |= FIELD_TYPE_DOUBLE;
    194             break;
    195         case FieldDescriptorProto::TYPE_FLOAT:
    196             result |= FIELD_TYPE_FLOAT;
    197             break;
    198         case FieldDescriptorProto::TYPE_INT64:
    199             result |= FIELD_TYPE_INT64;
    200             break;
    201         case FieldDescriptorProto::TYPE_UINT64:
    202             result |= FIELD_TYPE_UINT64;
    203             break;
    204         case FieldDescriptorProto::TYPE_INT32:
    205             result |= FIELD_TYPE_INT32;
    206             break;
    207         case FieldDescriptorProto::TYPE_FIXED64:
    208             result |= FIELD_TYPE_FIXED64;
    209             break;
    210         case FieldDescriptorProto::TYPE_FIXED32:
    211             result |= FIELD_TYPE_FIXED32;
    212             break;
    213         case FieldDescriptorProto::TYPE_BOOL:
    214             result |= FIELD_TYPE_BOOL;
    215             break;
    216         case FieldDescriptorProto::TYPE_STRING:
    217             result |= FIELD_TYPE_STRING;
    218             break;
    219         case FieldDescriptorProto::TYPE_MESSAGE:
    220             result |= FIELD_TYPE_OBJECT;
    221             break;
    222         case FieldDescriptorProto::TYPE_BYTES:
    223             result |= FIELD_TYPE_BYTES;
    224             break;
    225         case FieldDescriptorProto::TYPE_UINT32:
    226             result |= FIELD_TYPE_UINT32;
    227             break;
    228         case FieldDescriptorProto::TYPE_ENUM:
    229             result |= FIELD_TYPE_ENUM;
    230             break;
    231         case FieldDescriptorProto::TYPE_SFIXED32:
    232             result |= FIELD_TYPE_SFIXED32;
    233             break;
    234         case FieldDescriptorProto::TYPE_SFIXED64:
    235             result |= FIELD_TYPE_SFIXED64;
    236             break;
    237         case FieldDescriptorProto::TYPE_SINT32:
    238             result |= FIELD_TYPE_SINT32;
    239             break;
    240         case FieldDescriptorProto::TYPE_SINT64:
    241             result |= FIELD_TYPE_SINT64;
    242             break;
    243         default:
    244             ;
    245     }
    246 
    247     // Count
    248     if (field.options().packed()) {
    249         result |= FIELD_COUNT_PACKED;
    250     } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
    251         result |= FIELD_COUNT_REPEATED;
    252     } else {
    253         result |= FIELD_COUNT_SINGLE;
    254     }
    255 
    256     return result;
    257 }
    258 
    259 /**
    260  * Write a field.
    261  */
    262 static void
    263 write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
    264 {
    265     string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
    266             ? "optional " : "";
    267     string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
    268             ? "repeated " : "";
    269     string proto_type = get_proto_type(field);
    270     string packed_comment = field.options().packed()
    271             ? " [packed=true]" : "";
    272     text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
    273             << field.name() << " = " << field.number() << packed_comment << ';' << endl;
    274 
    275     text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
    276 
    277     ios::fmtflags fmt(text.flags());
    278     text << setfill('0') << setw(16) << hex << get_field_id(field);
    279     text.flags(fmt);
    280 
    281     text << "L;" << endl;
    282 
    283     text << endl;
    284 }
    285 
    286 /**
    287  * Write a Message constants class.
    288  */
    289 static void
    290 write_message(stringstream& text, const DescriptorProto& message, const string& indent)
    291 {
    292     int N;
    293     const string indented = indent_more(indent);
    294 
    295     text << indent << "// message " << message.name() << endl;
    296     text << indent << "public final class " << message.name() << " {" << endl;
    297     text << endl;
    298 
    299     // Enums
    300     N = message.enum_type_size();
    301     for (int i=0; i<N; i++) {
    302         write_enum(text, message.enum_type(i), indented);
    303     }
    304 
    305     // Nested classes
    306     N = message.nested_type_size();
    307     for (int i=0; i<N; i++) {
    308         write_message(text, message.nested_type(i), indented);
    309     }
    310 
    311     // Fields
    312     N = message.field_size();
    313     for (int i=0; i<N; i++) {
    314         write_field(text, message.field(i), indented);
    315     }
    316 
    317     text << indent << "}" << endl;
    318     text << endl;
    319 }
    320 
    321 /**
    322  * Write the contents of a file.
    323  *
    324  * If there are enums and generate_outer is false, invalid java code will be generated.
    325  */
    326 static void
    327 write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
    328         const string& filename, bool generate_outer,
    329         const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
    330 {
    331     stringstream text;
    332 
    333     string const package_name = make_java_package(file_descriptor);
    334     string const outer_class_name = make_outer_class_name(file_descriptor);
    335 
    336     text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
    337     text << "// source: " << file_descriptor.name() << endl << endl;
    338 
    339     if (package_name.size() > 0) {
    340         if (package_name.size() > 0) {
    341             text << "package " << package_name << ";" << endl;
    342             text << endl;
    343         }
    344     }
    345 
    346     // This bit of policy is android api rules specific: Raw proto classes
    347     // must never be in the API
    348     text << "/** @hide */" << endl;
    349 //    text << "@android.annotation.TestApi" << endl;
    350 
    351     if (generate_outer) {
    352         text << "public final class " << outer_class_name << " {" << endl;
    353         text << endl;
    354     }
    355 
    356     size_t N;
    357     const string indented = generate_outer ? indent_more("") : string();
    358 
    359     N = enums.size();
    360     for (size_t i=0; i<N; i++) {
    361         write_enum(text, enums[i], indented);
    362     }
    363 
    364     N = messages.size();
    365     for (size_t i=0; i<N; i++) {
    366         write_message(text, messages[i], indented);
    367     }
    368 
    369     if (generate_outer) {
    370         text << "}" << endl;
    371     }
    372 
    373     CodeGeneratorResponse::File* file_response = response->add_file();
    374     file_response->set_name(filename);
    375     file_response->set_content(text.str());
    376 }
    377 
    378 /**
    379  * Write one file per class.  Put all of the enums into the "outer" class.
    380  */
    381 static void
    382 write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
    383 {
    384     // If there is anything to put in the outer class file, create one
    385     if (file_descriptor.enum_type_size() > 0) {
    386         vector<EnumDescriptorProto> enums;
    387         int N = file_descriptor.enum_type_size();
    388         for (int i=0; i<N; i++) {
    389             enums.push_back(file_descriptor.enum_type(i));
    390         }
    391 
    392         vector<DescriptorProto> messages;
    393 
    394         write_file(response, file_descriptor,
    395                 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
    396                 true, enums, messages);
    397     }
    398 
    399     // For each of the message types, make a file
    400     int N = file_descriptor.message_type_size();
    401     for (int i=0; i<N; i++) {
    402         vector<EnumDescriptorProto> enums;
    403 
    404         vector<DescriptorProto> messages;
    405         messages.push_back(file_descriptor.message_type(i));
    406 
    407         write_file(response, file_descriptor,
    408                 make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
    409                 false, enums, messages);
    410     }
    411 }
    412 
    413 static void
    414 write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
    415 {
    416     int N;
    417 
    418     vector<EnumDescriptorProto> enums;
    419     N = file_descriptor.enum_type_size();
    420     for (int i=0; i<N; i++) {
    421         enums.push_back(file_descriptor.enum_type(i));
    422     }
    423 
    424     vector<DescriptorProto> messages;
    425     N = file_descriptor.message_type_size();
    426     for (int i=0; i<N; i++) {
    427         messages.push_back(file_descriptor.message_type(i));
    428     }
    429 
    430     write_file(response, file_descriptor,
    431             make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
    432             true, enums, messages);
    433 }
    434 
    435 /**
    436  * Main.
    437  */
    438 int
    439 main(int argc, char const*const* argv)
    440 {
    441     (void)argc;
    442     (void)argv;
    443 
    444     GOOGLE_PROTOBUF_VERIFY_VERSION;
    445 
    446     CodeGeneratorRequest request;
    447     CodeGeneratorResponse response;
    448 
    449     // Read the request
    450     request.ParseFromIstream(&cin);
    451 
    452     // Build the files we need.
    453     const int N = request.proto_file_size();
    454     for (int i=0; i<N; i++) {
    455         const FileDescriptorProto& file_descriptor = request.proto_file(i);
    456         if (should_generate_for_file(request, file_descriptor.name())) {
    457             if (file_descriptor.options().java_multiple_files()) {
    458                 write_multiple_files(&response, file_descriptor);
    459             } else {
    460                 write_single_file(&response, file_descriptor);
    461             }
    462         }
    463     }
    464 
    465     // If we had errors, don't write the response. Print the errors and exit.
    466     if (ERRORS.HasErrors()) {
    467         ERRORS.Print();
    468         return 1;
    469     }
    470 
    471     // If we didn't have errors, write the response and exit happily.
    472     response.SerializeToOstream(&cout);
    473     return 0;
    474 }
    475