blob: d2862137b216ee613ddbaf8bfef12a1f01502fc4 [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
97make_file_name(const FileDescriptorProto& file_descriptor)
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 += make_outer_class_name(file_descriptor);
107 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;
194 case FieldDescriptorProto::TYPE_FLOAT:
195 result |= FIELD_TYPE_FLOAT;
196 case FieldDescriptorProto::TYPE_INT64:
197 result |= FIELD_TYPE_INT64;
198 case FieldDescriptorProto::TYPE_UINT64:
199 result |= FIELD_TYPE_UINT64;
200 case FieldDescriptorProto::TYPE_INT32:
201 result |= FIELD_TYPE_INT32;
202 case FieldDescriptorProto::TYPE_FIXED64:
203 result |= FIELD_TYPE_FIXED64;
204 case FieldDescriptorProto::TYPE_FIXED32:
205 result |= FIELD_TYPE_FIXED32;
206 case FieldDescriptorProto::TYPE_BOOL:
207 result |= FIELD_TYPE_BOOL;
208 case FieldDescriptorProto::TYPE_STRING:
209 result |= FIELD_TYPE_STRING;
210 case FieldDescriptorProto::TYPE_MESSAGE:
211 result |= FIELD_TYPE_OBJECT;
212 case FieldDescriptorProto::TYPE_BYTES:
213 result |= FIELD_TYPE_BYTES;
214 case FieldDescriptorProto::TYPE_UINT32:
215 result |= FIELD_TYPE_UINT32;
216 case FieldDescriptorProto::TYPE_ENUM:
217 result |= FIELD_TYPE_ENUM;
218 case FieldDescriptorProto::TYPE_SFIXED32:
219 result |= FIELD_TYPE_SFIXED32;
220 case FieldDescriptorProto::TYPE_SFIXED64:
221 result |= FIELD_TYPE_SFIXED64;
222 case FieldDescriptorProto::TYPE_SINT32:
223 result |= FIELD_TYPE_SINT32;
224 case FieldDescriptorProto::TYPE_SINT64:
225 result |= FIELD_TYPE_SINT64;
226 default:
227 ;
228 }
229
230 // Count
231 if (field.options().packed()) {
232 result |= FIELD_COUNT_PACKED;
233 } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
234 result |= FIELD_COUNT_REPEATED;
235 } else {
236 result |= FIELD_COUNT_SINGLE;
237 }
238
239 return result;
240}
241
242/**
243 * Write a field.
244 */
245static void
246write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
247{
248 string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
249 ? "optional " : "";
250 string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
251 ? "repeated " : "";
252 string proto_type = get_proto_type(field);
253 string packed_comment = field.options().packed()
254 ? " [packed=true]" : "";
255 text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
256 << field.name() << " = " << field.number() << packed_comment << ';' << endl;
257
258 text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
259
260 ios::fmtflags fmt(text.flags());
261 text << setfill('0') << setw(16) << hex << get_field_id(field);
262 text.flags(fmt);
263
264 text << "L;" << endl;
265
266 text << endl;
267}
268
269/**
270 * Write a Message constants class.
271 */
272static void
273write_message(stringstream& text, const DescriptorProto& message, const string& indent)
274{
275 int N;
276 const string indented = indent_more(indent);
277
278 text << indent << "// message " << message.name() << endl;
279 text << indent << "public final class " << message.name() << " {" << endl;
280 text << endl;
281
282 // Enums
283 N = message.enum_type_size();
284 for (int i=0; i<N; i++) {
285 write_enum(text, message.enum_type(i), indented);
286 }
287
288 // Nested classes
289 N = message.nested_type_size();
290 for (int i=0; i<N; i++) {
291 write_message(text, message.nested_type(i), indented);
292 }
293
294 // Fields
295 N = message.field_size();
296 for (int i=0; i<N; i++) {
297 write_field(text, message.field(i), indented);
298 }
299
300 text << indent << "}" << endl;
301 text << endl;
302}
303
304/**
305 * Write the contents of a file.
306 */
307static void
308write_file(stringstream& text, const FileDescriptorProto& file_descriptor)
309{
310 string const package_name = make_java_package(file_descriptor);
311 string const outer_class_name = make_outer_class_name(file_descriptor);
312
313 text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
314 text << "// source: " << file_descriptor.name() << endl << endl;
315
316 if (package_name.size() > 0) {
317 if (package_name.size() > 0) {
318 text << "package " << package_name << ";" << endl;
319 text << endl;
320 }
321 }
322
323 // This bit of policy is android api rules specific: Raw proto classes
324 // must never be in the API, but they should all be available for testing.
325 text << "/** @hide */" << endl;
326 text << "@android.annotation.TestApi" << endl;
327
328 text << "public final class " << outer_class_name << " {" << endl;
329 text << endl;
330
331 int N;
332 const string indented = indent_more("");
333
334 N = file_descriptor.enum_type_size();
335 for (int i=0; i<N; i++) {
336 write_enum(text, file_descriptor.enum_type(i), indented);
337 }
338
339 N = file_descriptor.message_type_size();
340 for (int i=0; i<N; i++) {
341 write_message(text, file_descriptor.message_type(i), indented);
342 }
343
344 text << "}" << endl;
345}
346
347/**
348 * Main.
349 */
350int
351main(int argc, char const*const* argv)
352{
353 (void)argc;
354 (void)argv;
355
356 GOOGLE_PROTOBUF_VERIFY_VERSION;
357
358 CodeGeneratorRequest request;
359 CodeGeneratorResponse response;
360
361 // Read the request
362 request.ParseFromIstream(&cin);
363
364 // Build the files we need.
365 const int N = request.proto_file_size();
366 for (int i=0; i<N; i++) {
367 const FileDescriptorProto& file_descriptor = request.proto_file(i);
368 if (should_generate_for_file(request, file_descriptor.name())) {
369 // Generate the text
370 stringstream text;
371 write_file(text, file_descriptor);
372
373 // Put the text in the response
374 CodeGeneratorResponse::File* file_response = response.add_file();
375 file_response->set_name(make_file_name(file_descriptor));
376 file_response->set_content(text.str());
377
378 cerr << "writing file: " << file_response->name() << endl;
379 }
380 }
381
382 // If we had errors, don't write the response. Print the errors and exit.
383 if (ERRORS.HasErrors()) {
384 ERRORS.Print();
385 return 1;
386 }
387
388 // If we didn't have errors, write the response and exit happily.
389 response.SerializeToOstream(&cout);
390 return 0;
391}