Line data Source code
1 : // Protocol Buffers - Google's data interchange format
2 : // Copyright 2008 Google Inc. All rights reserved.
3 : // https://developers.google.com/protocol-buffers/
4 : //
5 : // Redistribution and use in source and binary forms, with or without
6 : // modification, are permitted provided that the following conditions are
7 : // met:
8 : //
9 : // * Redistributions of source code must retain the above copyright
10 : // notice, this list of conditions and the following disclaimer.
11 : // * Redistributions in binary form must reproduce the above
12 : // copyright notice, this list of conditions and the following disclaimer
13 : // in the documentation and/or other materials provided with the
14 : // distribution.
15 : // * Neither the name of Google Inc. nor the names of its
16 : // contributors may be used to endorse or promote products derived from
17 : // this software without specific prior written permission.
18 : //
19 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 :
31 : #include <sstream>
32 :
33 : #include <google/protobuf/compiler/code_generator.h>
34 : #include <google/protobuf/compiler/plugin.h>
35 : #include <google/protobuf/descriptor.h>
36 : #include <google/protobuf/descriptor.pb.h>
37 : #include <google/protobuf/io/printer.h>
38 : #include <google/protobuf/io/zero_copy_stream.h>
39 :
40 : #include <google/protobuf/compiler/ruby/ruby_generator.h>
41 :
42 : using google::protobuf::internal::scoped_ptr;
43 :
44 : namespace google {
45 : namespace protobuf {
46 : namespace compiler {
47 : namespace ruby {
48 :
49 : // Forward decls.
50 : std::string IntToString(int32 value);
51 : std::string StripDotProto(const std::string& proto_file);
52 : std::string LabelForField(google::protobuf::FieldDescriptor* field);
53 : std::string TypeName(google::protobuf::FieldDescriptor* field);
54 : void GenerateMessage(const google::protobuf::Descriptor* message,
55 : google::protobuf::io::Printer* printer);
56 : void GenerateEnum(const google::protobuf::EnumDescriptor* en,
57 : google::protobuf::io::Printer* printer);
58 : void GenerateMessageAssignment(
59 : const std::string& prefix,
60 : const google::protobuf::Descriptor* message,
61 : google::protobuf::io::Printer* printer);
62 : void GenerateEnumAssignment(
63 : const std::string& prefix,
64 : const google::protobuf::EnumDescriptor* en,
65 : google::protobuf::io::Printer* printer);
66 :
67 0 : std::string IntToString(int32 value) {
68 0 : std::ostringstream os;
69 0 : os << value;
70 0 : return os.str();
71 : }
72 :
73 0 : std::string StripDotProto(const std::string& proto_file) {
74 0 : int lastindex = proto_file.find_last_of(".");
75 0 : return proto_file.substr(0, lastindex);
76 : }
77 :
78 0 : std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
79 0 : switch (field->label()) {
80 0 : case FieldDescriptor::LABEL_OPTIONAL: return "optional";
81 0 : case FieldDescriptor::LABEL_REQUIRED: return "required";
82 0 : case FieldDescriptor::LABEL_REPEATED: return "repeated";
83 0 : default: assert(false); return "";
84 : }
85 : }
86 :
87 0 : std::string TypeName(const google::protobuf::FieldDescriptor* field) {
88 0 : switch (field->type()) {
89 0 : case FieldDescriptor::TYPE_INT32: return "int32";
90 0 : case FieldDescriptor::TYPE_INT64: return "int64";
91 0 : case FieldDescriptor::TYPE_UINT32: return "uint32";
92 0 : case FieldDescriptor::TYPE_UINT64: return "uint64";
93 0 : case FieldDescriptor::TYPE_SINT32: return "sint32";
94 0 : case FieldDescriptor::TYPE_SINT64: return "sint64";
95 0 : case FieldDescriptor::TYPE_FIXED32: return "fixed32";
96 0 : case FieldDescriptor::TYPE_FIXED64: return "fixed64";
97 0 : case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
98 0 : case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
99 0 : case FieldDescriptor::TYPE_DOUBLE: return "double";
100 0 : case FieldDescriptor::TYPE_FLOAT: return "float";
101 0 : case FieldDescriptor::TYPE_BOOL: return "bool";
102 0 : case FieldDescriptor::TYPE_ENUM: return "enum";
103 0 : case FieldDescriptor::TYPE_STRING: return "string";
104 0 : case FieldDescriptor::TYPE_BYTES: return "bytes";
105 0 : case FieldDescriptor::TYPE_MESSAGE: return "message";
106 0 : case FieldDescriptor::TYPE_GROUP: return "group";
107 0 : default: assert(false); return "";
108 : }
109 : }
110 :
111 0 : void GenerateField(const google::protobuf::FieldDescriptor* field,
112 : google::protobuf::io::Printer* printer) {
113 :
114 0 : if (field->is_map()) {
115 : const FieldDescriptor* key_field =
116 0 : field->message_type()->FindFieldByNumber(1);
117 0 : const FieldDescriptor* value_field =
118 0 : field->message_type()->FindFieldByNumber(2);
119 :
120 : printer->Print(
121 : "map :$name$, :$key_type$, :$value_type$, $number$",
122 0 : "name", field->name(),
123 : "key_type", TypeName(key_field),
124 : "value_type", TypeName(value_field),
125 0 : "number", IntToString(field->number()));
126 :
127 0 : if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
128 : printer->Print(
129 : ", \"$subtype$\"\n",
130 0 : "subtype", value_field->message_type()->full_name());
131 0 : } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
132 : printer->Print(
133 : ", \"$subtype$\"\n",
134 0 : "subtype", value_field->enum_type()->full_name());
135 : } else {
136 0 : printer->Print("\n");
137 : }
138 : } else {
139 :
140 : printer->Print(
141 : "$label$ :$name$, ",
142 : "label", LabelForField(field),
143 0 : "name", field->name());
144 : printer->Print(
145 : ":$type$, $number$",
146 : "type", TypeName(field),
147 0 : "number", IntToString(field->number()));
148 :
149 0 : if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
150 : printer->Print(
151 : ", \"$subtype$\"\n",
152 0 : "subtype", field->message_type()->full_name());
153 0 : } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
154 : printer->Print(
155 : ", \"$subtype$\"\n",
156 0 : "subtype", field->enum_type()->full_name());
157 : } else {
158 0 : printer->Print("\n");
159 : }
160 : }
161 0 : }
162 :
163 0 : void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
164 : google::protobuf::io::Printer* printer) {
165 : printer->Print(
166 : "oneof :$name$ do\n",
167 0 : "name", oneof->name());
168 0 : printer->Indent();
169 :
170 0 : for (int i = 0; i < oneof->field_count(); i++) {
171 0 : const FieldDescriptor* field = oneof->field(i);
172 0 : GenerateField(field, printer);
173 : }
174 :
175 0 : printer->Outdent();
176 0 : printer->Print("end\n");
177 0 : }
178 :
179 0 : void GenerateMessage(const google::protobuf::Descriptor* message,
180 : google::protobuf::io::Printer* printer) {
181 :
182 : // Don't generate MapEntry messages -- we use the Ruby extension's native
183 : // support for map fields instead.
184 0 : if (message->options().map_entry()) {
185 0 : return;
186 : }
187 :
188 : printer->Print(
189 : "add_message \"$name$\" do\n",
190 0 : "name", message->full_name());
191 0 : printer->Indent();
192 :
193 0 : for (int i = 0; i < message->field_count(); i++) {
194 0 : const FieldDescriptor* field = message->field(i);
195 0 : if (!field->containing_oneof()) {
196 0 : GenerateField(field, printer);
197 : }
198 : }
199 :
200 0 : for (int i = 0; i < message->oneof_decl_count(); i++) {
201 0 : const OneofDescriptor* oneof = message->oneof_decl(i);
202 0 : GenerateOneof(oneof, printer);
203 : }
204 :
205 0 : printer->Outdent();
206 0 : printer->Print("end\n");
207 :
208 0 : for (int i = 0; i < message->nested_type_count(); i++) {
209 0 : GenerateMessage(message->nested_type(i), printer);
210 : }
211 0 : for (int i = 0; i < message->enum_type_count(); i++) {
212 0 : GenerateEnum(message->enum_type(i), printer);
213 : }
214 : }
215 :
216 0 : void GenerateEnum(const google::protobuf::EnumDescriptor* en,
217 : google::protobuf::io::Printer* printer) {
218 : printer->Print(
219 : "add_enum \"$name$\" do\n",
220 0 : "name", en->full_name());
221 0 : printer->Indent();
222 :
223 0 : for (int i = 0; i < en->value_count(); i++) {
224 0 : const EnumValueDescriptor* value = en->value(i);
225 : printer->Print(
226 : "value :$name$, $number$\n",
227 0 : "name", value->name(),
228 0 : "number", IntToString(value->number()));
229 : }
230 :
231 0 : printer->Outdent();
232 : printer->Print(
233 0 : "end\n");
234 0 : }
235 :
236 : // Module names, class names, and enum value names need to be Ruby constants,
237 : // which must start with a capital letter.
238 0 : std::string RubifyConstant(const std::string& name) {
239 0 : std::string ret = name;
240 0 : if (!ret.empty()) {
241 0 : if (ret[0] >= 'a' && ret[0] <= 'z') {
242 : // If it starts with a lowercase letter, capitalize it.
243 0 : ret[0] = ret[0] - 'a' + 'A';
244 0 : } else if (ret[0] < 'A' || ret[0] > 'Z') {
245 : // Otherwise (e.g. if it begins with an underscore), we need to come up
246 : // with some prefix that starts with a capital letter. We could be smarter
247 : // here, e.g. try to strip leading underscores, but this may cause other
248 : // problems if the user really intended the name. So let's just prepend a
249 : // well-known suffix.
250 0 : ret = "PB_" + ret;
251 : }
252 : }
253 0 : return ret;
254 : }
255 :
256 0 : void GenerateMessageAssignment(
257 : const std::string& prefix,
258 0 : const google::protobuf::Descriptor* message,
259 : google::protobuf::io::Printer* printer) {
260 :
261 : // Don't generate MapEntry messages -- we use the Ruby extension's native
262 : // support for map fields instead.
263 0 : if (message->options().map_entry()) {
264 0 : return;
265 : }
266 :
267 : printer->Print(
268 : "$prefix$$name$ = ",
269 : "prefix", prefix,
270 0 : "name", RubifyConstant(message->name()));
271 : printer->Print(
272 : "Google::Protobuf::DescriptorPool.generated_pool."
273 : "lookup(\"$full_name$\").msgclass\n",
274 0 : "full_name", message->full_name());
275 :
276 0 : std::string nested_prefix = prefix + message->name() + "::";
277 0 : for (int i = 0; i < message->nested_type_count(); i++) {
278 0 : GenerateMessageAssignment(nested_prefix, message->nested_type(i), printer);
279 : }
280 0 : for (int i = 0; i < message->enum_type_count(); i++) {
281 0 : GenerateEnumAssignment(nested_prefix, message->enum_type(i), printer);
282 : }
283 : }
284 :
285 0 : void GenerateEnumAssignment(
286 : const std::string& prefix,
287 0 : const google::protobuf::EnumDescriptor* en,
288 : google::protobuf::io::Printer* printer) {
289 : printer->Print(
290 : "$prefix$$name$ = ",
291 : "prefix", prefix,
292 0 : "name", RubifyConstant(en->name()));
293 : printer->Print(
294 : "Google::Protobuf::DescriptorPool.generated_pool."
295 : "lookup(\"$full_name$\").enummodule\n",
296 0 : "full_name", en->full_name());
297 0 : }
298 :
299 0 : int GeneratePackageModules(
300 : std::string package_name,
301 : google::protobuf::io::Printer* printer) {
302 0 : int levels = 0;
303 0 : while (!package_name.empty()) {
304 0 : size_t dot_index = package_name.find(".");
305 : string component;
306 0 : if (dot_index == string::npos) {
307 : component = package_name;
308 : package_name = "";
309 : } else {
310 0 : component = package_name.substr(0, dot_index);
311 0 : package_name = package_name.substr(dot_index + 1);
312 : }
313 0 : component = RubifyConstant(component);
314 : printer->Print(
315 : "module $name$\n",
316 0 : "name", component);
317 0 : printer->Indent();
318 0 : levels++;
319 : }
320 0 : return levels;
321 : }
322 :
323 0 : void EndPackageModules(
324 : int levels,
325 : google::protobuf::io::Printer* printer) {
326 0 : while (levels > 0) {
327 0 : levels--;
328 0 : printer->Outdent();
329 : printer->Print(
330 0 : "end\n");
331 : }
332 0 : }
333 :
334 0 : void GenerateFile(const google::protobuf::FileDescriptor* file,
335 : google::protobuf::io::Printer* printer) {
336 : printer->Print(
337 : "# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
338 : "# source: $filename$\n"
339 : "\n",
340 0 : "filename", file->name());
341 :
342 : printer->Print(
343 0 : "require 'google/protobuf'\n\n");
344 :
345 0 : for (int i = 0; i < file->dependency_count(); i++) {
346 0 : const std::string& name = file->dependency(i)->name();
347 : printer->Print(
348 0 : "require '$name$'\n", "name", StripDotProto(name));
349 : }
350 :
351 : printer->Print(
352 0 : "Google::Protobuf::DescriptorPool.generated_pool.build do\n");
353 0 : printer->Indent();
354 0 : for (int i = 0; i < file->message_type_count(); i++) {
355 0 : GenerateMessage(file->message_type(i), printer);
356 : }
357 0 : for (int i = 0; i < file->enum_type_count(); i++) {
358 0 : GenerateEnum(file->enum_type(i), printer);
359 : }
360 0 : printer->Outdent();
361 : printer->Print(
362 0 : "end\n\n");
363 :
364 0 : int levels = GeneratePackageModules(file->package(), printer);
365 0 : for (int i = 0; i < file->message_type_count(); i++) {
366 0 : GenerateMessageAssignment("", file->message_type(i), printer);
367 : }
368 0 : for (int i = 0; i < file->enum_type_count(); i++) {
369 0 : GenerateEnumAssignment("", file->enum_type(i), printer);
370 : }
371 0 : EndPackageModules(levels, printer);
372 0 : }
373 :
374 0 : bool Generator::Generate(
375 0 : const FileDescriptor* file,
376 : const string& parameter,
377 : GeneratorContext* generator_context,
378 : string* error) const {
379 :
380 0 : if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
381 : *error =
382 : "Can only generate Ruby code for proto3 .proto files.\n"
383 : "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
384 : return false;
385 : }
386 :
387 : std::string filename =
388 0 : StripDotProto(file->name()) + ".rb";
389 : scoped_ptr<io::ZeroCopyOutputStream> output(
390 0 : generator_context->Open(filename));
391 0 : io::Printer printer(output.get(), '$');
392 :
393 0 : GenerateFile(file, &printer);
394 :
395 : return true;
396 : }
397 :
398 : } // namespace ruby
399 : } // namespace compiler
400 : } // namespace protobuf
401 : } // namespace google
|