1 #include "Errors.h"
2 #include "stream_proto_utils.h"
3 #include "string_utils.h"
4 
5 #include <stdio.h>
6 #include <iomanip>
7 #include <iostream>
8 #include <sstream>
9 #include <map>
10 
11 using namespace android::stream_proto;
12 using namespace google::protobuf::io;
13 using namespace std;
14 
15 /**
16  * If the descriptor gives us a class name, use that. Otherwise make one up from
17  * the filename of the .proto file.
18  */
19 static string
make_outer_class_name(const FileDescriptorProto & file_descriptor)20 make_outer_class_name(const FileDescriptorProto& file_descriptor)
21 {
22     string name = file_descriptor.options().java_outer_classname();
23     if (name.size() == 0) {
24         name = to_camel_case(file_base_name(file_descriptor.name()));
25         if (name.size() == 0) {
26             ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE,
27                     "Unable to make an outer class name for file: %s",
28                     file_descriptor.name().c_str());
29             name = "Unknown";
30         }
31     }
32     return name;
33 }
34 
35 /**
36  * Figure out the package name that we are generating.
37  */
38 static string
make_java_package(const FileDescriptorProto & file_descriptor)39 make_java_package(const FileDescriptorProto& file_descriptor) {
40     if (file_descriptor.options().has_java_package()) {
41         return file_descriptor.options().java_package();
42     } else {
43         return file_descriptor.package();
44     }
45 }
46 
47 /**
48  * Figure out the name of the file we are generating.
49  */
50 static string
make_file_name(const FileDescriptorProto & file_descriptor,const string & class_name)51 make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
52 {
53     string const package = make_java_package(file_descriptor);
54     string result;
55     if (package.size() > 0) {
56         result = replace_string(package, '.', '/');
57         result += '/';
58     }
59 
60     result += class_name;
61     result += ".java";
62 
63     return result;
64 }
65 
66 static string
indent_more(const string & indent)67 indent_more(const string& indent)
68 {
69     return indent + INDENT;
70 }
71 
72 /**
73  * Write the constants for an enum.
74  */
75 static void
write_enum(stringstream & text,const EnumDescriptorProto & enu,const string & indent)76 write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
77 {
78     const int N = enu.value_size();
79     text << indent << "// enum " << enu.name() << endl;
80     for (int i=0; i<N; i++) {
81         const EnumValueDescriptorProto& value = enu.value(i);
82         text << indent << "public static final int "
83                 << make_constant_name(value.name())
84                 << " = " << value.number() << ";" << endl;
85     }
86     text << endl;
87 }
88 
89 /**
90  * Write a field.
91  */
92 static void
write_field(stringstream & text,const FieldDescriptorProto & field,const string & indent)93 write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
94 {
95     string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
96             ? "optional " : "";
97     string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
98             ? "repeated " : "";
99     string proto_type = get_proto_type(field);
100     string packed_comment = field.options().packed()
101             ? " [packed=true]" : "";
102     text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
103             << field.name() << " = " << field.number() << packed_comment << ';' << endl;
104 
105     text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x";
106 
107     ios::fmtflags fmt(text.flags());
108     text << setfill('0') << setw(16) << hex << get_field_id(field);
109     text.flags(fmt);
110 
111     text << "L;" << endl;
112 
113     text << endl;
114 }
115 
116 /**
117  * Write a Message constants class.
118  */
119 static void
write_message(stringstream & text,const DescriptorProto & message,const string & indent)120 write_message(stringstream& text, const DescriptorProto& message, const string& indent)
121 {
122     int N;
123     const string indented = indent_more(indent);
124 
125     text << indent << "// message " << message.name() << endl;
126     text << indent << "public final class " << message.name() << " {" << endl;
127     text << endl;
128 
129     // Enums
130     N = message.enum_type_size();
131     for (int i=0; i<N; i++) {
132         write_enum(text, message.enum_type(i), indented);
133     }
134 
135     // Nested classes
136     N = message.nested_type_size();
137     for (int i=0; i<N; i++) {
138         write_message(text, message.nested_type(i), indented);
139     }
140 
141     // Fields
142     N = message.field_size();
143     for (int i=0; i<N; i++) {
144         write_field(text, message.field(i), indented);
145     }
146 
147     text << indent << "}" << endl;
148     text << endl;
149 }
150 
151 /**
152  * Write the contents of a file.
153  *
154  * If there are enums and generate_outer is false, invalid java code will be generated.
155  */
156 static void
write_file(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor,const string & filename,bool generate_outer,const vector<EnumDescriptorProto> & enums,const vector<DescriptorProto> & messages)157 write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
158         const string& filename, bool generate_outer,
159         const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
160 {
161     stringstream text;
162 
163     string const package_name = make_java_package(file_descriptor);
164     string const outer_class_name = make_outer_class_name(file_descriptor);
165 
166     text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl;
167     text << "// source: " << file_descriptor.name() << endl << endl;
168 
169     if (package_name.size() > 0) {
170         if (package_name.size() > 0) {
171             text << "package " << package_name << ";" << endl;
172             text << endl;
173         }
174     }
175 
176     // This bit of policy is android api rules specific: Raw proto classes
177     // must never be in the API
178     text << "/** @hide */" << endl;
179 //    text << "@android.annotation.TestApi" << endl;
180 
181     if (generate_outer) {
182         text << "public final class " << outer_class_name << " {" << endl;
183         text << endl;
184     }
185 
186     size_t N;
187     const string indented = generate_outer ? indent_more("") : string();
188 
189     N = enums.size();
190     for (size_t i=0; i<N; i++) {
191         write_enum(text, enums[i], indented);
192     }
193 
194     N = messages.size();
195     for (size_t i=0; i<N; i++) {
196         write_message(text, messages[i], indented);
197     }
198 
199     if (generate_outer) {
200         text << "}" << endl;
201     }
202 
203     CodeGeneratorResponse::File* file_response = response->add_file();
204     file_response->set_name(filename);
205     file_response->set_content(text.str());
206 }
207 
208 /**
209  * Write one file per class.  Put all of the enums into the "outer" class.
210  */
211 static void
write_multiple_files(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor)212 write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
213 {
214     // If there is anything to put in the outer class file, create one
215     if (file_descriptor.enum_type_size() > 0) {
216         vector<EnumDescriptorProto> enums;
217         int N = file_descriptor.enum_type_size();
218         for (int i=0; i<N; i++) {
219             enums.push_back(file_descriptor.enum_type(i));
220         }
221 
222         vector<DescriptorProto> messages;
223 
224         write_file(response, file_descriptor,
225                 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
226                 true, enums, messages);
227     }
228 
229     // For each of the message types, make a file
230     int N = file_descriptor.message_type_size();
231     for (int i=0; i<N; i++) {
232         vector<EnumDescriptorProto> enums;
233 
234         vector<DescriptorProto> messages;
235         messages.push_back(file_descriptor.message_type(i));
236 
237         write_file(response, file_descriptor,
238                 make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
239                 false, enums, messages);
240     }
241 }
242 
243 static void
write_single_file(CodeGeneratorResponse * response,const FileDescriptorProto & file_descriptor)244 write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
245 {
246     int N;
247 
248     vector<EnumDescriptorProto> enums;
249     N = file_descriptor.enum_type_size();
250     for (int i=0; i<N; i++) {
251         enums.push_back(file_descriptor.enum_type(i));
252     }
253 
254     vector<DescriptorProto> messages;
255     N = file_descriptor.message_type_size();
256     for (int i=0; i<N; i++) {
257         messages.push_back(file_descriptor.message_type(i));
258     }
259 
260     write_file(response, file_descriptor,
261             make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
262             true, enums, messages);
263 }
264 
265 /**
266  * Main.
267  */
268 int
main(int argc,char const * const * argv)269 main(int argc, char const*const* argv)
270 {
271     (void)argc;
272     (void)argv;
273 
274     GOOGLE_PROTOBUF_VERIFY_VERSION;
275 
276     CodeGeneratorRequest request;
277     CodeGeneratorResponse response;
278 
279     // Read the request
280     request.ParseFromIstream(&cin);
281 
282     // Build the files we need.
283     const int N = request.proto_file_size();
284     for (int i=0; i<N; i++) {
285         const FileDescriptorProto& file_descriptor = request.proto_file(i);
286         if (should_generate_for_file(request, file_descriptor.name())) {
287             if (file_descriptor.options().java_multiple_files()) {
288                 write_multiple_files(&response, file_descriptor);
289             } else {
290                 write_single_file(&response, file_descriptor);
291             }
292         }
293     }
294 
295     // If we had errors, don't write the response. Print the errors and exit.
296     if (ERRORS.HasErrors()) {
297         ERRORS.Print();
298         return 1;
299     }
300 
301     // If we didn't have errors, write the response and exit happily.
302     response.SerializeToOstream(&cout);
303     return 0;
304 }
305