1 #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 15 using namespace android::javastream_proto; 16 using namespace google::protobuf; 17 using namespace google::protobuf::compiler; 18 using namespace google::protobuf::io; 19 using namespace std; 20 21 const int FIELD_TYPE_SHIFT = 32; 22 const uint64_t FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; 23 const uint64_t FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; 24 const uint64_t FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT; 25 const uint64_t FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT; 26 const uint64_t FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT; 27 const uint64_t FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT; 28 const uint64_t FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT; 29 const uint64_t FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT; 30 const uint64_t FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT; 31 const uint64_t FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT; 32 const uint64_t FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT; 33 const uint64_t FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT; 34 const uint64_t FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT; 35 const uint64_t FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT; 36 const uint64_t FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT; 37 const uint64_t FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT; 38 const uint64_t FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT; 39 40 const int FIELD_COUNT_SHIFT = 40; 41 const uint64_t FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT; 42 const uint64_t FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT; 43 const 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 */ 49 static bool 50 should_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 */ 65 static string 66 make_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 */ 84 static string 85 make_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 */ 96 static string 97 make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name) 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 += class_name; 107 result += ".java"; 108 109 return result; 110 } 111 112 static string 113 indent_more(const string& indent) 114 { 115 return indent + " "; 116 } 117 118 /** 119 * Write the constants for an enum. 120 */ 121 static void 122 write_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 */ 138 static string 139 get_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 184 static uint64_t 185 get_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 break; 195 case FieldDescriptorProto::TYPE_FLOAT: 196 result |= FIELD_TYPE_FLOAT; 197 break; 198 case FieldDescriptorProto::TYPE_INT64: 199 result |= FIELD_TYPE_INT64; 200 break; 201 case FieldDescriptorProto::TYPE_UINT64: 202 result |= FIELD_TYPE_UINT64; 203 break; 204 case FieldDescriptorProto::TYPE_INT32: 205 result |= FIELD_TYPE_INT32; 206 break; 207 case FieldDescriptorProto::TYPE_FIXED64: 208 result |= FIELD_TYPE_FIXED64; 209 break; 210 case FieldDescriptorProto::TYPE_FIXED32: 211 result |= FIELD_TYPE_FIXED32; 212 break; 213 case FieldDescriptorProto::TYPE_BOOL: 214 result |= FIELD_TYPE_BOOL; 215 break; 216 case FieldDescriptorProto::TYPE_STRING: 217 result |= FIELD_TYPE_STRING; 218 break; 219 case FieldDescriptorProto::TYPE_MESSAGE: 220 result |= FIELD_TYPE_OBJECT; 221 break; 222 case FieldDescriptorProto::TYPE_BYTES: 223 result |= FIELD_TYPE_BYTES; 224 break; 225 case FieldDescriptorProto::TYPE_UINT32: 226 result |= FIELD_TYPE_UINT32; 227 break; 228 case FieldDescriptorProto::TYPE_ENUM: 229 result |= FIELD_TYPE_ENUM; 230 break; 231 case FieldDescriptorProto::TYPE_SFIXED32: 232 result |= FIELD_TYPE_SFIXED32; 233 break; 234 case FieldDescriptorProto::TYPE_SFIXED64: 235 result |= FIELD_TYPE_SFIXED64; 236 break; 237 case FieldDescriptorProto::TYPE_SINT32: 238 result |= FIELD_TYPE_SINT32; 239 break; 240 case FieldDescriptorProto::TYPE_SINT64: 241 result |= FIELD_TYPE_SINT64; 242 break; 243 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 */ 262 static void 263 write_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 */ 289 static void 290 write_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. 323 * 324 * If there are enums and generate_outer is false, invalid java code will be generated. 325 */ 326 static void 327 write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor, 328 const string& filename, bool generate_outer, 329 const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages) 330 { 331 stringstream text; 332 333 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 347 // must never be in the API 348 text << "/** @hide */" << endl; 349 // text << "@android.annotation.TestApi" << endl; 350 351 if (generate_outer) { 352 text << "public final class " << outer_class_name << " {" << endl; 353 text << endl; 354 } 355 356 size_t N; 357 const string indented = generate_outer ? indent_more("") : string(); 358 359 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 */ 381 static void 382 write_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 413 static void 414 write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) 415 { 416 int N; 417 418 vector<EnumDescriptorProto> enums; 419 N = file_descriptor.enum_type_size(); 420 for (int i=0; i<N; i++) { 421 enums.push_back(file_descriptor.enum_type(i)); 422 } 423 424 vector<DescriptorProto> messages; 425 N = file_descriptor.message_type_size(); 426 for (int i=0; i<N; i++) { 427 messages.push_back(file_descriptor.message_type(i)); 428 } 429 430 write_file(response, file_descriptor, 431 make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), 432 true, enums, messages); 433 } 434 435 /** 436 * Main. 437 */ 438 int 439 main(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())) { 457 if (file_descriptor.options().java_multiple_files()) { 458 write_multiple_files(&response, file_descriptor); 459 } else { 460 write_single_file(&response, file_descriptor); 461 } 462 } 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 } 475