OLD | NEW |
1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
4 // | 4 // |
5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
7 // met: | 7 // met: |
8 // | 8 // |
9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
(...skipping 30 matching lines...) Expand all Loading... |
41 | 41 |
42 using google::protobuf::internal::scoped_ptr; | 42 using google::protobuf::internal::scoped_ptr; |
43 | 43 |
44 namespace google { | 44 namespace google { |
45 namespace protobuf { | 45 namespace protobuf { |
46 namespace compiler { | 46 namespace compiler { |
47 namespace ruby { | 47 namespace ruby { |
48 | 48 |
49 // Forward decls. | 49 // Forward decls. |
50 std::string IntToString(int32 value); | 50 std::string IntToString(int32 value); |
51 std::string StripDotProto(const std::string& proto_file); | 51 std::string GetRequireName(const std::string& proto_file); |
52 std::string LabelForField(google::protobuf::FieldDescriptor* field); | 52 std::string LabelForField(google::protobuf::FieldDescriptor* field); |
53 std::string TypeName(google::protobuf::FieldDescriptor* field); | 53 std::string TypeName(google::protobuf::FieldDescriptor* field); |
54 void GenerateMessage(const google::protobuf::Descriptor* message, | 54 void GenerateMessage(const google::protobuf::Descriptor* message, |
55 google::protobuf::io::Printer* printer); | 55 google::protobuf::io::Printer* printer); |
56 void GenerateEnum(const google::protobuf::EnumDescriptor* en, | 56 void GenerateEnum(const google::protobuf::EnumDescriptor* en, |
57 google::protobuf::io::Printer* printer); | 57 google::protobuf::io::Printer* printer); |
58 void GenerateMessageAssignment( | 58 void GenerateMessageAssignment( |
59 const std::string& prefix, | 59 const std::string& prefix, |
60 const google::protobuf::Descriptor* message, | 60 const google::protobuf::Descriptor* message, |
61 google::protobuf::io::Printer* printer); | 61 google::protobuf::io::Printer* printer); |
62 void GenerateEnumAssignment( | 62 void GenerateEnumAssignment( |
63 const std::string& prefix, | 63 const std::string& prefix, |
64 const google::protobuf::EnumDescriptor* en, | 64 const google::protobuf::EnumDescriptor* en, |
65 google::protobuf::io::Printer* printer); | 65 google::protobuf::io::Printer* printer); |
66 | 66 |
67 std::string IntToString(int32 value) { | 67 std::string IntToString(int32 value) { |
68 std::ostringstream os; | 68 std::ostringstream os; |
69 os << value; | 69 os << value; |
70 return os.str(); | 70 return os.str(); |
71 } | 71 } |
72 | 72 |
73 std::string StripDotProto(const std::string& proto_file) { | 73 std::string GetRequireName(const std::string& proto_file) { |
74 int lastindex = proto_file.find_last_of("."); | 74 int lastindex = proto_file.find_last_of("."); |
75 return proto_file.substr(0, lastindex); | 75 return proto_file.substr(0, lastindex) + "_pb"; |
76 } | 76 } |
77 | 77 |
78 std::string GetOutputFilename(const std::string& proto_file) { | 78 std::string GetOutputFilename(const std::string& proto_file) { |
79 return StripDotProto(proto_file) + ".rb"; | 79 return GetRequireName(proto_file) + ".rb"; |
80 } | 80 } |
81 | 81 |
82 std::string LabelForField(const google::protobuf::FieldDescriptor* field) { | 82 std::string LabelForField(const google::protobuf::FieldDescriptor* field) { |
83 switch (field->label()) { | 83 switch (field->label()) { |
84 case FieldDescriptor::LABEL_OPTIONAL: return "optional"; | 84 case FieldDescriptor::LABEL_OPTIONAL: return "optional"; |
85 case FieldDescriptor::LABEL_REQUIRED: return "required"; | 85 case FieldDescriptor::LABEL_REQUIRED: return "required"; |
86 case FieldDescriptor::LABEL_REPEATED: return "repeated"; | 86 case FieldDescriptor::LABEL_REPEATED: return "repeated"; |
87 default: assert(false); return ""; | 87 default: assert(false); return ""; |
88 } | 88 } |
89 } | 89 } |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 "value :$name$, $number$\n", | 230 "value :$name$, $number$\n", |
231 "name", value->name(), | 231 "name", value->name(), |
232 "number", IntToString(value->number())); | 232 "number", IntToString(value->number())); |
233 } | 233 } |
234 | 234 |
235 printer->Outdent(); | 235 printer->Outdent(); |
236 printer->Print( | 236 printer->Print( |
237 "end\n"); | 237 "end\n"); |
238 } | 238 } |
239 | 239 |
240 // Module names, class names, and enum value names need to be Ruby constants, | 240 // Locale-agnostic utility functions. |
241 // which must start with a capital letter. | 241 bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } |
| 242 |
| 243 bool IsUpper(char ch) { return ch >= 'A' && ch <= 'Z'; } |
| 244 |
| 245 bool IsAlpha(char ch) { return IsLower(ch) || IsUpper(ch); } |
| 246 |
| 247 char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } |
| 248 |
| 249 |
| 250 // Package names in protobuf are snake_case by convention, but Ruby module |
| 251 // names must be PascalCased. |
| 252 // |
| 253 // foo_bar_baz -> FooBarBaz |
| 254 std::string PackageToModule(const std::string& name) { |
| 255 bool next_upper = true; |
| 256 std::string result; |
| 257 result.reserve(name.size()); |
| 258 |
| 259 for (int i = 0; i < name.size(); i++) { |
| 260 if (name[i] == '_') { |
| 261 next_upper = true; |
| 262 } else { |
| 263 if (next_upper) { |
| 264 result.push_back(ToUpper(name[i])); |
| 265 } else { |
| 266 result.push_back(name[i]); |
| 267 } |
| 268 next_upper = false; |
| 269 } |
| 270 } |
| 271 |
| 272 return result; |
| 273 } |
| 274 |
| 275 // Class and enum names in protobuf should be PascalCased by convention, but |
| 276 // since there is nothing enforcing this we need to ensure that they are valid |
| 277 // Ruby constants. That mainly means making sure that the first character is |
| 278 // an upper-case letter. |
242 std::string RubifyConstant(const std::string& name) { | 279 std::string RubifyConstant(const std::string& name) { |
243 std::string ret = name; | 280 std::string ret = name; |
244 if (!ret.empty()) { | 281 if (!ret.empty()) { |
245 if (ret[0] >= 'a' && ret[0] <= 'z') { | 282 if (IsLower(ret[0])) { |
246 // If it starts with a lowercase letter, capitalize it. | 283 // If it starts with a lowercase letter, capitalize it. |
247 ret[0] = ret[0] - 'a' + 'A'; | 284 ret[0] = ToUpper(ret[0]); |
248 } else if (ret[0] < 'A' || ret[0] > 'Z') { | 285 } else if (!IsAlpha(ret[0])) { |
249 // Otherwise (e.g. if it begins with an underscore), we need to come up | 286 // Otherwise (e.g. if it begins with an underscore), we need to come up |
250 // with some prefix that starts with a capital letter. We could be smarter | 287 // with some prefix that starts with a capital letter. We could be smarter |
251 // here, e.g. try to strip leading underscores, but this may cause other | 288 // here, e.g. try to strip leading underscores, but this may cause other |
252 // problems if the user really intended the name. So let's just prepend a | 289 // problems if the user really intended the name. So let's just prepend a |
253 // well-known suffix. | 290 // well-known suffix. |
254 ret = "PB_" + ret; | 291 ret = "PB_" + ret; |
255 } | 292 } |
256 } | 293 } |
| 294 |
257 return ret; | 295 return ret; |
258 } | 296 } |
259 | 297 |
260 void GenerateMessageAssignment( | 298 void GenerateMessageAssignment( |
261 const std::string& prefix, | 299 const std::string& prefix, |
262 const google::protobuf::Descriptor* message, | 300 const google::protobuf::Descriptor* message, |
263 google::protobuf::io::Printer* printer) { | 301 google::protobuf::io::Printer* printer) { |
264 | 302 |
265 // Don't generate MapEntry messages -- we use the Ruby extension's native | 303 // Don't generate MapEntry messages -- we use the Ruby extension's native |
266 // support for map fields instead. | 304 // support for map fields instead. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 while (!package_name.empty()) { | 345 while (!package_name.empty()) { |
308 size_t dot_index = package_name.find("."); | 346 size_t dot_index = package_name.find("."); |
309 string component; | 347 string component; |
310 if (dot_index == string::npos) { | 348 if (dot_index == string::npos) { |
311 component = package_name; | 349 component = package_name; |
312 package_name = ""; | 350 package_name = ""; |
313 } else { | 351 } else { |
314 component = package_name.substr(0, dot_index); | 352 component = package_name.substr(0, dot_index); |
315 package_name = package_name.substr(dot_index + 1); | 353 package_name = package_name.substr(dot_index + 1); |
316 } | 354 } |
317 component = RubifyConstant(component); | 355 component = PackageToModule(component); |
318 printer->Print( | 356 printer->Print( |
319 "module $name$\n", | 357 "module $name$\n", |
320 "name", component); | 358 "name", component); |
321 printer->Indent(); | 359 printer->Indent(); |
322 levels++; | 360 levels++; |
323 } | 361 } |
324 return levels; | 362 return levels; |
325 } | 363 } |
326 | 364 |
327 void EndPackageModules( | 365 void EndPackageModules( |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 | 422 |
385 // Ok to omit this proto2 dependency -- so we won't print anything. | 423 // Ok to omit this proto2 dependency -- so we won't print anything. |
386 GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name() | 424 GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name() |
387 << "' from proto3 output file '" | 425 << "' from proto3 output file '" |
388 << GetOutputFilename(from->name()) | 426 << GetOutputFilename(from->name()) |
389 << "' because we don't support proto2 and no proto2 " | 427 << "' because we don't support proto2 and no proto2 " |
390 "types from that file are being used."; | 428 "types from that file are being used."; |
391 return true; | 429 return true; |
392 } else { | 430 } else { |
393 printer->Print( | 431 printer->Print( |
394 "require '$name$'\n", "name", StripDotProto(import->name())); | 432 "require '$name$'\n", "name", GetRequireName(import->name())); |
395 return true; | 433 return true; |
396 } | 434 } |
397 } | 435 } |
398 | 436 |
399 bool GenerateFile(const FileDescriptor* file, io::Printer* printer, | 437 bool GenerateFile(const FileDescriptor* file, io::Printer* printer, |
400 string* error) { | 438 string* error) { |
401 printer->Print( | 439 printer->Print( |
402 "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" | 440 "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
403 "# source: $filename$\n" | 441 "# source: $filename$\n" |
404 "\n", | 442 "\n", |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 generator_context->Open(GetOutputFilename(file->name()))); | 492 generator_context->Open(GetOutputFilename(file->name()))); |
455 io::Printer printer(output.get(), '$'); | 493 io::Printer printer(output.get(), '$'); |
456 | 494 |
457 return GenerateFile(file, &printer, error); | 495 return GenerateFile(file, &printer, error); |
458 } | 496 } |
459 | 497 |
460 } // namespace ruby | 498 } // namespace ruby |
461 } // namespace compiler | 499 } // namespace compiler |
462 } // namespace protobuf | 500 } // namespace protobuf |
463 } // namespace google | 501 } // namespace google |
OLD | NEW |