blob: 5b4ba04b2b582670431198360625d5a9ee68b0fd [file] [log] [blame]
Joe Onorato6c9547d2016-09-07 18:43:49 -07001#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
15using namespace android::javastream_proto;
16using namespace google::protobuf;
17using namespace google::protobuf::compiler;
18using namespace google::protobuf::io;
19using namespace std;
20
21const int FIELD_TYPE_SHIFT = 32;
22const uint64_t FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
23const uint64_t FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
24const uint64_t FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT;
25const uint64_t FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT;
26const uint64_t FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT;
27const uint64_t FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT;
28const uint64_t FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT;
29const uint64_t FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT;
30const uint64_t FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT;
31const uint64_t FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT;
32const uint64_t FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT;
33const uint64_t FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT;
34const uint64_t FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT;
35const uint64_t FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT;
36const uint64_t FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT;
37const uint64_t FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT;
38const uint64_t FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT;
39
40const int FIELD_COUNT_SHIFT = 40;
41const uint64_t FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
42const uint64_t FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
43const 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 */
49static bool
50should_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 */
65static string
66make_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 */
84static string
85make_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 */
96static string
Joe Onoratob38ac0b2016-10-28 13:10:25 -070097make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
Joe Onorato6c9547d2016-09-07 18:43:49 -070098{
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
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700106 result += class_name;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700107 result += ".java";
108
109 return result;
110}
111
112static string
113indent_more(const string& indent)
114{
115 return indent + " ";
116}
117
118/**
119 * Write the constants for an enum.
120 */
121static void
122write_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 */
138static string
139get_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
184static uint64_t
185get_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;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700194 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700195 case FieldDescriptorProto::TYPE_FLOAT:
196 result |= FIELD_TYPE_FLOAT;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700197 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700198 case FieldDescriptorProto::TYPE_INT64:
199 result |= FIELD_TYPE_INT64;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700200 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700201 case FieldDescriptorProto::TYPE_UINT64:
202 result |= FIELD_TYPE_UINT64;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700203 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700204 case FieldDescriptorProto::TYPE_INT32:
205 result |= FIELD_TYPE_INT32;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700206 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700207 case FieldDescriptorProto::TYPE_FIXED64:
208 result |= FIELD_TYPE_FIXED64;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700209 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700210 case FieldDescriptorProto::TYPE_FIXED32:
211 result |= FIELD_TYPE_FIXED32;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700212 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700213 case FieldDescriptorProto::TYPE_BOOL:
214 result |= FIELD_TYPE_BOOL;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700215 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700216 case FieldDescriptorProto::TYPE_STRING:
217 result |= FIELD_TYPE_STRING;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700218 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700219 case FieldDescriptorProto::TYPE_MESSAGE:
220 result |= FIELD_TYPE_OBJECT;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700221 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700222 case FieldDescriptorProto::TYPE_BYTES:
223 result |= FIELD_TYPE_BYTES;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700224 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700225 case FieldDescriptorProto::TYPE_UINT32:
226 result |= FIELD_TYPE_UINT32;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700227 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700228 case FieldDescriptorProto::TYPE_ENUM:
229 result |= FIELD_TYPE_ENUM;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700230 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700231 case FieldDescriptorProto::TYPE_SFIXED32:
232 result |= FIELD_TYPE_SFIXED32;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700233 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700234 case FieldDescriptorProto::TYPE_SFIXED64:
235 result |= FIELD_TYPE_SFIXED64;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700236 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700237 case FieldDescriptorProto::TYPE_SINT32:
238 result |= FIELD_TYPE_SINT32;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700239 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700240 case FieldDescriptorProto::TYPE_SINT64:
241 result |= FIELD_TYPE_SINT64;
Joe Onorato3a5eb292016-10-20 11:27:37 -0700242 break;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700243 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 */
262static void
263write_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 */
289static void
290write_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.
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700323 *
324 * If there are enums and generate_outer is false, invalid java code will be generated.
Joe Onorato6c9547d2016-09-07 18:43:49 -0700325 */
326static void
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700327write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
328 const string& filename, bool generate_outer,
329 const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
Joe Onorato6c9547d2016-09-07 18:43:49 -0700330{
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700331 stringstream text;
332
Joe Onorato6c9547d2016-09-07 18:43:49 -0700333 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
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700347 // must never be in the API
Joe Onorato6c9547d2016-09-07 18:43:49 -0700348 text << "/** @hide */" << endl;
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700349// text << "@android.annotation.TestApi" << endl;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700350
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700351 if (generate_outer) {
352 text << "public final class " << outer_class_name << " {" << endl;
353 text << endl;
354 }
Joe Onorato6c9547d2016-09-07 18:43:49 -0700355
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700356 size_t N;
357 const string indented = generate_outer ? indent_more("") : string();
Joe Onorato6c9547d2016-09-07 18:43:49 -0700358
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700359 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 */
381static void
382write_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
413static void
414write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
415{
416 int N;
417
418 vector<EnumDescriptorProto> enums;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700419 N = file_descriptor.enum_type_size();
420 for (int i=0; i<N; i++) {
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700421 enums.push_back(file_descriptor.enum_type(i));
Joe Onorato6c9547d2016-09-07 18:43:49 -0700422 }
423
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700424 vector<DescriptorProto> messages;
Joe Onorato6c9547d2016-09-07 18:43:49 -0700425 N = file_descriptor.message_type_size();
426 for (int i=0; i<N; i++) {
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700427 messages.push_back(file_descriptor.message_type(i));
Joe Onorato6c9547d2016-09-07 18:43:49 -0700428 }
429
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700430 write_file(response, file_descriptor,
431 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
432 true, enums, messages);
Joe Onorato6c9547d2016-09-07 18:43:49 -0700433}
434
435/**
436 * Main.
437 */
438int
439main(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())) {
Joe Onoratob38ac0b2016-10-28 13:10:25 -0700457 if (file_descriptor.options().java_multiple_files()) {
458 write_multiple_files(&response, file_descriptor);
459 } else {
460 write_single_file(&response, file_descriptor);
461 }
Joe Onorato6c9547d2016-09-07 18:43:49 -0700462 }
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}