• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
should_generate_for_file(const CodeGeneratorRequest & request,const string & file)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
make_outer_class_name(const FileDescriptorProto & file_descriptor)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
make_java_package(const FileDescriptorProto & file_descriptor)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
make_file_name(const FileDescriptorProto & file_descriptor,const string & class_name)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
indent_more(const string & indent)113 indent_more(const string& indent)
114 {
115     return indent + "    ";
116 }
117 
118 /**
119  * Write the constants for an enum.
120  */
121 static void
write_enum(stringstream & text,const EnumDescriptorProto & enu,const string & indent)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
get_proto_type(const FieldDescriptorProto & field)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
get_field_id(const FieldDescriptorProto & field)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
write_field(stringstream & text,const FieldDescriptorProto & field,const string & indent)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
write_message(stringstream & text,const DescriptorProto & message,const string & indent)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
write_file(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor,const string & filename,bool generate_outer,const vector<EnumDescriptorProto> & enums,const vector<DescriptorProto> & messages)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
write_multiple_files(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor)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
write_single_file(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor)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
main(int argc,char const * const * argv)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