1 /* 2 * Copyright 2014 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // independent from idl_parser, since this code is not needed for most clients 18 19 #include "flatbuffers/flatbuffers.h" 20 #include "flatbuffers/idl.h" 21 #include "flatbuffers/util.h" 22 #include "flatbuffers/code_generators.h" 23 24 #if defined(FLATBUFFERS_CPP98_STL) 25 #include <cctype> 26 #endif // defined(FLATBUFFERS_CPP98_STL) 27 28 namespace flatbuffers { 29 30 // Convert an underscore_based_indentifier in to camelCase. 31 // Also uppercases the first character if first is true. 32 std::string MakeCamel(const std::string &in, bool first) { 33 std::string s; 34 for (size_t i = 0; i < in.length(); i++) { 35 if (!i && first) 36 s += static_cast<char>(toupper(in[0])); 37 else if (in[i] == '_' && i + 1 < in.length()) 38 s += static_cast<char>(toupper(in[++i])); 39 else 40 s += in[i]; 41 } 42 return s; 43 } 44 45 // These arrays need to correspond to the IDLOptions::k enum. 46 47 struct LanguageParameters { 48 IDLOptions::Language language; 49 // Whether function names in the language typically start with uppercase. 50 bool first_camel_upper; 51 std::string file_extension; 52 std::string string_type; 53 std::string bool_type; 54 std::string open_curly; 55 std::string accessor_type; 56 std::string const_decl; 57 std::string unsubclassable_decl; 58 std::string enum_decl; 59 std::string enum_separator; 60 std::string getter_prefix; 61 std::string getter_suffix; 62 std::string inheritance_marker; 63 std::string namespace_ident; 64 std::string namespace_begin; 65 std::string namespace_end; 66 std::string set_bb_byteorder; 67 std::string get_bb_position; 68 std::string get_fbb_offset; 69 std::string accessor_prefix; 70 std::string accessor_prefix_static; 71 std::string optional_suffix; 72 std::string includes; 73 std::string class_annotation; 74 CommentConfig comment_config; 75 }; 76 77 const LanguageParameters& GetLangParams(IDLOptions::Language lang) { 78 static LanguageParameters language_parameters[] = { 79 { 80 IDLOptions::kJava, 81 false, 82 ".java", 83 "String", 84 "boolean ", 85 " {\n", 86 "class ", 87 " final ", 88 "final ", 89 "final class ", 90 ";\n", 91 "()", 92 "", 93 " extends ", 94 "package ", 95 ";", 96 "", 97 "_bb.order(ByteOrder.LITTLE_ENDIAN); ", 98 "position()", 99 "offset()", 100 "", 101 "", 102 "", 103 "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\nimport com.google.flatbuffers.*;\n", 104 "\n@SuppressWarnings(\"unused\")\n", 105 { 106 "/**", 107 " *", 108 " */", 109 }, 110 }, 111 { 112 IDLOptions::kCSharp, 113 true, 114 ".cs", 115 "string", 116 "bool ", 117 "\n{\n", 118 "struct ", 119 " readonly ", 120 "", 121 "enum ", 122 ",\n", 123 " { get", 124 "} ", 125 " : ", 126 "namespace ", 127 "\n{", 128 "\n}\n", 129 "", 130 "Position", 131 "Offset", 132 "__p.", 133 "Table.", 134 "?", 135 "using global::System;\nusing global::FlatBuffers;\n\n", 136 "", 137 { 138 nullptr, 139 "///", 140 nullptr, 141 }, 142 }, 143 }; 144 145 if (lang == IDLOptions::kJava) { 146 return language_parameters[0]; 147 } else { 148 assert(lang == IDLOptions::kCSharp); 149 return language_parameters[1]; 150 } 151 } 152 153 namespace general { 154 class GeneralGenerator : public BaseGenerator { 155 public: 156 GeneralGenerator(const Parser &parser, const std::string &path, 157 const std::string &file_name) 158 : BaseGenerator(parser, path, file_name, "", "."), 159 lang_(GetLangParams(parser_.opts.lang)), 160 cur_name_space_( nullptr ) { 161 } 162 163 GeneralGenerator &operator=(const GeneralGenerator &); 164 bool generate() { 165 std::string one_file_code; 166 cur_name_space_ = parser_.current_namespace_; 167 168 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); 169 ++it) { 170 std::string enumcode; 171 auto &enum_def = **it; 172 if (!parser_.opts.one_file) 173 cur_name_space_ = enum_def.defined_namespace; 174 GenEnum(enum_def, &enumcode); 175 if (parser_.opts.one_file) { 176 one_file_code += enumcode; 177 } else { 178 if (!SaveType(enum_def.name, *enum_def.defined_namespace, 179 enumcode, false)) return false; 180 } 181 } 182 183 for (auto it = parser_.structs_.vec.begin(); 184 it != parser_.structs_.vec.end(); ++it) { 185 std::string declcode; 186 auto &struct_def = **it; 187 if (!parser_.opts.one_file) 188 cur_name_space_ = struct_def.defined_namespace; 189 GenStruct(struct_def, &declcode); 190 if (parser_.opts.one_file) { 191 one_file_code += declcode; 192 } else { 193 if (!SaveType(struct_def.name, *struct_def.defined_namespace, 194 declcode, true)) return false; 195 } 196 } 197 198 if (parser_.opts.one_file) { 199 return SaveType(file_name_, *parser_.current_namespace_, 200 one_file_code, true); 201 } 202 return true; 203 } 204 205 // Save out the generated code for a single class while adding 206 // declaration boilerplate. 207 bool SaveType(const std::string &defname, const Namespace &ns, 208 const std::string &classcode, bool needs_includes) { 209 if (!classcode.length()) return true; 210 211 std::string code; 212 if (lang_.language == IDLOptions::kCSharp) { 213 code = "// <auto-generated>\n" 214 "// " + std::string(FlatBuffersGeneratedWarning()) + "\n" 215 "// </auto-generated>\n\n"; 216 } else { 217 code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n"; 218 } 219 220 std::string namespace_name = FullNamespace(".", ns); 221 if (!namespace_name.empty()) { 222 code += lang_.namespace_ident + namespace_name + lang_.namespace_begin; 223 code += "\n\n"; 224 } 225 if (needs_includes) { 226 code += lang_.includes; 227 if (parser_.opts.gen_nullable) { 228 code += "\nimport javax.annotation.Nullable;\n"; 229 } 230 code += lang_.class_annotation; 231 } 232 code += classcode; 233 if (!namespace_name.empty()) code += lang_.namespace_end; 234 auto filename = NamespaceDir(ns) + defname + lang_.file_extension; 235 return SaveFile(filename.c_str(), code, false); 236 } 237 238 const Namespace *CurrentNameSpace() const { return cur_name_space_; } 239 240 std::string FunctionStart(char upper) { 241 return std::string() + (lang_.language == IDLOptions::kJava 242 ? static_cast<char>(tolower(upper)) 243 : upper); 244 } 245 246 std::string GenNullableAnnotation(const Type& t) { 247 return lang_.language == IDLOptions::kJava 248 && parser_.opts.gen_nullable 249 && !IsScalar(DestinationType(t, true).base_type) ? " @Nullable ": ""; 250 } 251 252 static bool IsEnum(const Type& type) { 253 return type.enum_def != nullptr && IsInteger(type.base_type); 254 } 255 256 std::string GenTypeBasic(const Type &type, bool enableLangOverrides) { 257 static const char *java_typename[] = { 258 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ 259 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 260 #JTYPE, 261 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 262 #undef FLATBUFFERS_TD 263 }; 264 265 static const char *csharp_typename[] = { 266 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ 267 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 268 #NTYPE, 269 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 270 #undef FLATBUFFERS_TD 271 }; 272 273 if (enableLangOverrides) { 274 if (lang_.language == IDLOptions::kCSharp) { 275 if (IsEnum(type)) return WrapInNameSpace(*type.enum_def); 276 if (type.base_type == BASE_TYPE_STRUCT) { 277 return "Offset<" + WrapInNameSpace(*type.struct_def) + ">"; 278 } 279 } 280 } 281 282 if (lang_.language == IDLOptions::kJava) { 283 return java_typename[type.base_type]; 284 } else { 285 assert(lang_.language == IDLOptions::kCSharp); 286 return csharp_typename[type.base_type]; 287 } 288 } 289 290 std::string GenTypeBasic(const Type &type) { 291 return GenTypeBasic(type, true); 292 } 293 294 std::string GenTypePointer(const Type &type) { 295 switch (type.base_type) { 296 case BASE_TYPE_STRING: 297 return lang_.string_type; 298 case BASE_TYPE_VECTOR: 299 return GenTypeGet(type.VectorType()); 300 case BASE_TYPE_STRUCT: 301 return WrapInNameSpace(*type.struct_def); 302 case BASE_TYPE_UNION: 303 // Unions in C# use a generic Table-derived type for better type safety 304 if (lang_.language == IDLOptions::kCSharp) return "TTable"; 305 // fall through 306 default: 307 return "Table"; 308 } 309 } 310 311 std::string GenTypeGet(const Type &type) { 312 return IsScalar(type.base_type) 313 ? GenTypeBasic(type) 314 : GenTypePointer(type); 315 } 316 317 // Find the destination type the user wants to receive the value in (e.g. 318 // one size higher signed types for unsigned serialized values in Java). 319 Type DestinationType(const Type &type, bool vectorelem) { 320 if (lang_.language != IDLOptions::kJava) return type; 321 switch (type.base_type) { 322 // We use int for both uchar/ushort, since that generally means less casting 323 // than using short for uchar. 324 case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT); 325 case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT); 326 case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG); 327 case BASE_TYPE_VECTOR: 328 if (vectorelem) 329 return DestinationType(type.VectorType(), vectorelem); 330 // else fall thru 331 default: return type; 332 } 333 } 334 335 std::string GenOffsetType(const StructDef &struct_def) { 336 if(lang_.language == IDLOptions::kCSharp) { 337 return "Offset<" + WrapInNameSpace(struct_def) + ">"; 338 } else { 339 return "int"; 340 } 341 } 342 343 std::string GenOffsetConstruct(const StructDef &struct_def, 344 const std::string &variable_name) 345 { 346 if(lang_.language == IDLOptions::kCSharp) { 347 return "new Offset<" + WrapInNameSpace(struct_def) + ">(" + variable_name + 348 ")"; 349 } 350 return variable_name; 351 } 352 353 std::string GenVectorOffsetType() { 354 if(lang_.language == IDLOptions::kCSharp) { 355 return "VectorOffset"; 356 } else { 357 return "int"; 358 } 359 } 360 361 // Generate destination type name 362 std::string GenTypeNameDest(const Type &type) 363 { 364 return GenTypeGet(DestinationType(type, true)); 365 } 366 367 // Mask to turn serialized value into destination type value. 368 std::string DestinationMask(const Type &type, bool vectorelem) { 369 if (lang_.language != IDLOptions::kJava) return ""; 370 switch (type.base_type) { 371 case BASE_TYPE_UCHAR: return " & 0xFF"; 372 case BASE_TYPE_USHORT: return " & 0xFFFF"; 373 case BASE_TYPE_UINT: return " & 0xFFFFFFFFL"; 374 case BASE_TYPE_VECTOR: 375 if (vectorelem) 376 return DestinationMask(type.VectorType(), vectorelem); 377 // else fall thru 378 default: return ""; 379 } 380 } 381 382 // Casts necessary to correctly read serialized data 383 std::string DestinationCast(const Type &type) { 384 if (type.base_type == BASE_TYPE_VECTOR) { 385 return DestinationCast(type.VectorType()); 386 } else { 387 switch (lang_.language) { 388 case IDLOptions::kJava: 389 // Cast necessary to correctly read serialized unsigned values. 390 if (type.base_type == BASE_TYPE_UINT) return "(long)"; 391 break; 392 393 case IDLOptions::kCSharp: 394 // Cast from raw integral types to enum. 395 if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")"; 396 break; 397 398 default: 399 break; 400 } 401 } 402 return ""; 403 } 404 405 // Cast statements for mutator method parameters. 406 // In Java, parameters representing unsigned numbers need to be cast down to 407 // their respective type. For example, a long holding an unsigned int value 408 // would be cast down to int before being put onto the buffer. In C#, one cast 409 // directly cast an Enum to its underlying type, which is essential before 410 // putting it onto the buffer. 411 std::string SourceCast(const Type &type, bool castFromDest) { 412 if (type.base_type == BASE_TYPE_VECTOR) { 413 return SourceCast(type.VectorType(), castFromDest); 414 } else { 415 switch (lang_.language) { 416 case IDLOptions::kJava: 417 if (castFromDest) { 418 if (type.base_type == BASE_TYPE_UINT) return "(int)"; 419 else if (type.base_type == BASE_TYPE_USHORT) return "(short)"; 420 else if (type.base_type == BASE_TYPE_UCHAR) return "(byte)"; 421 } 422 break; 423 case IDLOptions::kCSharp: 424 if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")"; 425 break; 426 default: 427 break; 428 } 429 } 430 return ""; 431 } 432 433 std::string SourceCast(const Type &type) { 434 return SourceCast(type, true); 435 } 436 437 std::string SourceCastBasic(const Type &type, bool castFromDest) { 438 return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : ""; 439 } 440 441 std::string SourceCastBasic(const Type &type) { 442 return SourceCastBasic(type, true); 443 } 444 445 446 std::string GenEnumDefaultValue(const Value &value) { 447 auto enum_def = value.type.enum_def; 448 auto vec = enum_def->vals.vec; 449 auto default_value = StringToInt(value.constant.c_str()); 450 451 auto result = value.constant; 452 for (auto it = vec.begin(); it != vec.end(); ++it) { 453 auto enum_val = **it; 454 if (enum_val.value == default_value) { 455 result = WrapInNameSpace(*enum_def) + "." + enum_val.name; 456 break; 457 } 458 } 459 460 return result; 461 } 462 463 std::string GenDefaultValue(const Value &value, bool enableLangOverrides) { 464 if (enableLangOverrides) { 465 // handles both enum case and vector of enum case 466 if (lang_.language == IDLOptions::kCSharp && 467 value.type.enum_def != nullptr && 468 value.type.base_type != BASE_TYPE_UNION) { 469 return GenEnumDefaultValue(value); 470 } 471 } 472 473 auto longSuffix = lang_.language == IDLOptions::kJava ? "L" : ""; 474 switch (value.type.base_type) { 475 case BASE_TYPE_FLOAT: return value.constant + "f"; 476 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true"; 477 case BASE_TYPE_ULONG: 478 { 479 if (lang_.language != IDLOptions::kJava) 480 return value.constant; 481 // Converts the ulong into its bits signed equivalent 482 uint64_t defaultValue = StringToUInt(value.constant.c_str()); 483 return NumToString(static_cast<int64_t>(defaultValue)) + longSuffix; 484 } 485 case BASE_TYPE_UINT: 486 case BASE_TYPE_LONG: return value.constant + longSuffix; 487 default: return value.constant; 488 } 489 } 490 491 std::string GenDefaultValue(const Value &value) { 492 return GenDefaultValue(value, true); 493 } 494 495 std::string GenDefaultValueBasic(const Value &value, bool enableLangOverrides) { 496 if (!IsScalar(value.type.base_type)) { 497 if (enableLangOverrides) { 498 if (lang_.language == IDLOptions::kCSharp) { 499 switch (value.type.base_type) { 500 case BASE_TYPE_STRING: 501 return "default(StringOffset)"; 502 case BASE_TYPE_STRUCT: 503 return "default(Offset<" + WrapInNameSpace(*value.type.struct_def) + 504 ">)"; 505 case BASE_TYPE_VECTOR: 506 return "default(VectorOffset)"; 507 default: 508 break; 509 } 510 } 511 } 512 return "0"; 513 } 514 return GenDefaultValue(value, enableLangOverrides); 515 } 516 517 std::string GenDefaultValueBasic(const Value &value) { 518 return GenDefaultValueBasic(value, true); 519 } 520 521 void GenEnum(EnumDef &enum_def, std::string *code_ptr) { 522 std::string &code = *code_ptr; 523 if (enum_def.generated) return; 524 525 // Generate enum definitions of the form: 526 // public static (final) int name = value; 527 // In Java, we use ints rather than the Enum feature, because we want them 528 // to map directly to how they're used in C/C++ and file formats. 529 // That, and Java Enums are expensive, and not universally liked. 530 GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config); 531 code += std::string("public ") + lang_.enum_decl + enum_def.name; 532 if (lang_.language == IDLOptions::kCSharp) { 533 code += lang_.inheritance_marker + 534 GenTypeBasic(enum_def.underlying_type, false); 535 } 536 code += lang_.open_curly; 537 if (lang_.language == IDLOptions::kJava) { 538 code += " private " + enum_def.name + "() { }\n"; 539 } 540 for (auto it = enum_def.vals.vec.begin(); 541 it != enum_def.vals.vec.end(); 542 ++it) { 543 auto &ev = **it; 544 GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, " "); 545 if (lang_.language != IDLOptions::kCSharp) { 546 code += " public static"; 547 code += lang_.const_decl; 548 code += GenTypeBasic(enum_def.underlying_type, false); 549 } 550 code += " " + ev.name + " = "; 551 code += NumToString(ev.value); 552 code += lang_.enum_separator; 553 } 554 555 // Generate a generate string table for enum values. 556 // We do not do that for C# where this functionality is native. 557 if (lang_.language != IDLOptions::kCSharp) { 558 // Problem is, if values are very sparse that could generate really big 559 // tables. Ideally in that case we generate a map lookup instead, but for 560 // the moment we simply don't output a table at all. 561 auto range = enum_def.vals.vec.back()->value - 562 enum_def.vals.vec.front()->value + 1; 563 // Average distance between values above which we consider a table 564 // "too sparse". Change at will. 565 static const int kMaxSparseness = 5; 566 if (range / static_cast<int64_t>(enum_def.vals.vec.size()) < kMaxSparseness) { 567 code += "\n public static"; 568 code += lang_.const_decl; 569 code += lang_.string_type; 570 code += "[] names = { "; 571 auto val = enum_def.vals.vec.front()->value; 572 for (auto it = enum_def.vals.vec.begin(); 573 it != enum_def.vals.vec.end(); 574 ++it) { 575 while (val++ != (*it)->value) code += "\"\", "; 576 code += "\"" + (*it)->name + "\", "; 577 } 578 code += "};\n\n"; 579 code += " public static "; 580 code += lang_.string_type; 581 code += " " + MakeCamel("name", lang_.first_camel_upper); 582 code += "(int e) { return names[e"; 583 if (enum_def.vals.vec.front()->value) 584 code += " - " + enum_def.vals.vec.front()->name; 585 code += "]; }\n"; 586 } 587 } 588 589 // Close the class 590 code += "}"; 591 // Java does not need the closing semi-colon on class definitions. 592 code += (lang_.language != IDLOptions::kJava) ? ";" : ""; 593 code += "\n\n"; 594 } 595 596 // Returns the function name that is able to read a value of the given type. 597 std::string GenGetter(const Type &type) { 598 switch (type.base_type) { 599 case BASE_TYPE_STRING: return lang_.accessor_prefix + "__string"; 600 case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct"; 601 case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union"; 602 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); 603 default: { 604 std::string getter = 605 lang_.accessor_prefix + "bb." + FunctionStart('G') + "et"; 606 if (type.base_type == BASE_TYPE_BOOL) { 607 getter = "0!=" + getter; 608 } else if (GenTypeBasic(type, false) != "byte") { 609 getter += MakeCamel(GenTypeBasic(type, false)); 610 } 611 return getter; 612 } 613 } 614 } 615 616 // Returns the function name that is able to read a value of the given type. 617 std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field, 618 const std::string &data_buffer, 619 const char *num = nullptr) { 620 auto type = key_field->value.type; 621 auto dest_mask = DestinationMask(type, true); 622 auto dest_cast = DestinationCast(type); 623 auto getter = data_buffer + "." + FunctionStart('G') + "et"; 624 if (GenTypeBasic(type, false) != "byte") { 625 getter += MakeCamel(GenTypeBasic(type, false)); 626 } 627 getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" 628 + dest_mask; 629 return getter; 630 } 631 632 // Direct mutation is only allowed for scalar fields. 633 // Hence a setter method will only be generated for such fields. 634 std::string GenSetter(const Type &type) { 635 if (IsScalar(type.base_type)) { 636 std::string setter = 637 lang_.accessor_prefix + "bb." + FunctionStart('P') + "ut"; 638 if (GenTypeBasic(type, false) != "byte" && 639 type.base_type != BASE_TYPE_BOOL) { 640 setter += MakeCamel(GenTypeBasic(type, false)); 641 } 642 return setter; 643 } else { 644 return ""; 645 } 646 } 647 648 // Returns the method name for use with add/put calls. 649 std::string GenMethod(const Type &type) { 650 return IsScalar(type.base_type) 651 ? MakeCamel(GenTypeBasic(type, false)) 652 : (IsStruct(type) ? "Struct" : "Offset"); 653 } 654 655 // Recursively generate arguments for a constructor, to deal with nested 656 // structs. 657 void GenStructArgs(const StructDef &struct_def, std::string *code_ptr, 658 const char *nameprefix) { 659 std::string &code = *code_ptr; 660 for (auto it = struct_def.fields.vec.begin(); 661 it != struct_def.fields.vec.end(); 662 ++it) { 663 auto &field = **it; 664 if (IsStruct(field.value.type)) { 665 // Generate arguments for a struct inside a struct. To ensure names 666 // don't clash, and to make it obvious these arguments are constructing 667 // a nested struct, prefix the name with the field name. 668 GenStructArgs(*field.value.type.struct_def, code_ptr, 669 (nameprefix + (field.name + "_")).c_str()); 670 } else { 671 code += ", "; 672 code += GenTypeBasic(DestinationType(field.value.type, false)); 673 code += " "; 674 code += nameprefix; 675 code += MakeCamel(field.name, lang_.first_camel_upper); 676 } 677 } 678 } 679 680 // Recusively generate struct construction statements of the form: 681 // builder.putType(name); 682 // and insert manual padding. 683 void GenStructBody(const StructDef &struct_def, std::string *code_ptr, 684 const char *nameprefix) { 685 std::string &code = *code_ptr; 686 code += " builder." + FunctionStart('P') + "rep("; 687 code += NumToString(struct_def.minalign) + ", "; 688 code += NumToString(struct_def.bytesize) + ");\n"; 689 for (auto it = struct_def.fields.vec.rbegin(); 690 it != struct_def.fields.vec.rend(); ++it) { 691 auto &field = **it; 692 if (field.padding) { 693 code += " builder." + FunctionStart('P') + "ad("; 694 code += NumToString(field.padding) + ");\n"; 695 } 696 if (IsStruct(field.value.type)) { 697 GenStructBody(*field.value.type.struct_def, code_ptr, 698 (nameprefix + (field.name + "_")).c_str()); 699 } else { 700 code += " builder." + FunctionStart('P') + "ut"; 701 code += GenMethod(field.value.type) + "("; 702 code += SourceCast(field.value.type); 703 auto argname = nameprefix + MakeCamel(field.name, lang_.first_camel_upper); 704 code += argname; 705 code += ");\n"; 706 } 707 } 708 } 709 710 std::string GenByteBufferLength(const char *bb_name) { 711 std::string bb_len = bb_name; 712 if (lang_.language == IDLOptions::kCSharp) bb_len += ".Length"; 713 else bb_len += ".capacity()"; 714 return bb_len; 715 } 716 717 std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, 718 const char *num = nullptr) { 719 std::string key_offset = ""; 720 key_offset += lang_.accessor_prefix_static + "__offset(" + 721 NumToString(key_field->value.offset) + ", "; 722 if (num) { 723 key_offset += num; 724 key_offset += (lang_.language == IDLOptions::kCSharp ? 725 ".Value, builder.DataBuffer)" : ", _bb)"); 726 } else { 727 key_offset += GenByteBufferLength("bb"); 728 key_offset += " - tableOffset, bb)"; 729 } 730 return key_offset; 731 } 732 733 std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) { 734 std::string key_getter = " "; 735 key_getter += "int tableOffset = " + lang_.accessor_prefix_static; 736 key_getter += "__indirect(vectorLocation + 4 * (start + middle)"; 737 key_getter += ", bb);\n "; 738 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 739 key_getter += "int comp = " + lang_.accessor_prefix_static; 740 key_getter += FunctionStart('C') + "ompareStrings("; 741 key_getter += GenOffsetGetter(key_field); 742 key_getter += ", byteKey, bb);\n"; 743 } else { 744 auto get_val = GenGetterForLookupByKey(key_field, "bb"); 745 if (lang_.language == IDLOptions::kCSharp) { 746 key_getter += "int comp = " + get_val + ".CompareTo(key);\n"; 747 } else { 748 key_getter += GenTypeNameDest(key_field->value.type) + " val = "; 749 key_getter += get_val + ";\n"; 750 key_getter += " int comp = val > key ? 1 : val < key ? -1 : 0;\n"; 751 } 752 } 753 return key_getter; 754 } 755 756 757 std::string GenKeyGetter(flatbuffers::FieldDef *key_field) { 758 std::string key_getter = ""; 759 auto data_buffer = (lang_.language == IDLOptions::kCSharp) ? 760 "builder.DataBuffer" : "_bb"; 761 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 762 if (lang_.language == IDLOptions::kJava) 763 key_getter += " return "; 764 key_getter += lang_.accessor_prefix_static; 765 key_getter += FunctionStart('C') + "ompareStrings("; 766 key_getter += GenOffsetGetter(key_field, "o1") + ", "; 767 key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")"; 768 if (lang_.language == IDLOptions::kJava) 769 key_getter += ";"; 770 } 771 else { 772 auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1"); 773 if (lang_.language == IDLOptions::kCSharp) { 774 key_getter += field_getter; 775 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2"); 776 key_getter += ".CompareTo(" + field_getter + ")"; 777 } 778 else { 779 key_getter += "\n " + GenTypeNameDest(key_field->value.type) + " val_1 = "; 780 key_getter += field_getter + ";\n " + GenTypeNameDest(key_field->value.type); 781 key_getter += " val_2 = "; 782 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2"); 783 key_getter += field_getter + ";\n"; 784 key_getter += " return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n "; 785 } 786 } 787 return key_getter; 788 } 789 790 void GenStruct(StructDef &struct_def, std::string *code_ptr) { 791 if (struct_def.generated) return; 792 std::string &code = *code_ptr; 793 794 // Generate a struct accessor class, with methods of the form: 795 // public type name() { return bb.getType(i + offset); } 796 // or for tables of the form: 797 // public type name() { 798 // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default; 799 // } 800 GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config); 801 code += "public "; 802 if (lang_.language == IDLOptions::kCSharp && 803 struct_def.attributes.Lookup("csharp_partial")) { 804 // generate a partial class for this C# struct/table 805 code += "partial "; 806 } else { 807 code += lang_.unsubclassable_decl; 808 } 809 code += lang_.accessor_type + struct_def.name; 810 if (lang_.language == IDLOptions::kCSharp) { 811 code += " : IFlatbufferObject"; 812 code += lang_.open_curly; 813 code += " private "; 814 code += struct_def.fixed ? "Struct" : "Table"; 815 code += " __p;\n"; 816 817 if (lang_.language == IDLOptions::kCSharp) { 818 code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n"; 819 } 820 821 } else { 822 code += lang_.inheritance_marker; 823 code += struct_def.fixed ? "Struct" : "Table"; 824 code += lang_.open_curly; 825 } 826 if (!struct_def.fixed) { 827 // Generate a special accessor for the table that when used as the root 828 // of a FlatBuffer 829 std::string method_name = FunctionStart('G') + "etRootAs" + struct_def.name; 830 std::string method_signature = " public static " + struct_def.name + " " + 831 method_name; 832 833 // create convenience method that doesn't require an existing object 834 code += method_signature + "(ByteBuffer _bb) "; 835 code += "{ return " + method_name + "(_bb, new " + struct_def.name+ "()); }\n"; 836 837 // create method that allows object reuse 838 code += method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { "; 839 code += lang_.set_bb_byteorder; 840 code += "return (obj.__assign(_bb." + FunctionStart('G') + "etInt(_bb."; 841 code += lang_.get_bb_position; 842 code += ") + _bb."; 843 code += lang_.get_bb_position; 844 code += ", _bb)); }\n"; 845 if (parser_.root_struct_def_ == &struct_def) { 846 if (parser_.file_identifier_.length()) { 847 // Check if a buffer has the identifier. 848 code += " public static "; 849 code += lang_.bool_type + struct_def.name; 850 code += "BufferHasIdentifier(ByteBuffer _bb) { return "; 851 code += lang_.accessor_prefix_static + "__has_identifier(_bb, \""; 852 code += parser_.file_identifier_; 853 code += "\"); }\n"; 854 } 855 } 856 } 857 // Generate the __init method that sets the field in a pre-existing 858 // accessor object. This is to allow object reuse. 859 code += " public void __init(int _i, ByteBuffer _bb) "; 860 code += "{ " + lang_.accessor_prefix + "bb_pos = _i; "; 861 code += lang_.accessor_prefix + "bb = _bb; }\n"; 862 code += " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) "; 863 code += "{ __init(_i, _bb); return this; }\n\n"; 864 for (auto it = struct_def.fields.vec.begin(); 865 it != struct_def.fields.vec.end(); 866 ++it) { 867 auto &field = **it; 868 if (field.deprecated) continue; 869 GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " "); 870 std::string type_name = GenTypeGet(field.value.type); 871 std::string type_name_dest = GenTypeNameDest(field.value.type); 872 std::string conditional_cast = ""; 873 std::string optional = ""; 874 if (lang_.language == IDLOptions::kCSharp && 875 !struct_def.fixed && 876 (field.value.type.base_type == BASE_TYPE_STRUCT || 877 field.value.type.base_type == BASE_TYPE_UNION || 878 (field.value.type.base_type == BASE_TYPE_VECTOR && 879 field.value.type.element == BASE_TYPE_STRUCT))) { 880 optional = lang_.optional_suffix; 881 conditional_cast = "(" + type_name_dest + optional + ")"; 882 } 883 std::string dest_mask = DestinationMask(field.value.type, true); 884 std::string dest_cast = DestinationCast(field.value.type); 885 std::string src_cast = SourceCast(field.value.type); 886 std::string method_start = " public " + GenNullableAnnotation(field.value.type) + 887 type_name_dest + optional + " " + 888 MakeCamel(field.name, lang_.first_camel_upper); 889 std::string obj = lang_.language == IDLOptions::kCSharp 890 ? "(new " + type_name + "())" 891 : "obj"; 892 893 // Most field accessors need to retrieve and test the field offset first, 894 // this is the prefix code for that: 895 auto offset_prefix = " { int o = " + lang_.accessor_prefix + "__offset(" + 896 NumToString(field.value.offset) + 897 "); return o != 0 ? "; 898 // Generate the accessors that don't do object reuse. 899 if (field.value.type.base_type == BASE_TYPE_STRUCT) { 900 // Calls the accessor that takes an accessor object with a new object. 901 if (lang_.language != IDLOptions::kCSharp) { 902 code += method_start + "() { return "; 903 code += MakeCamel(field.name, lang_.first_camel_upper); 904 code += "(new "; 905 code += type_name + "()); }\n"; 906 } 907 } else if (field.value.type.base_type == BASE_TYPE_VECTOR && 908 field.value.type.element == BASE_TYPE_STRUCT) { 909 // Accessors for vectors of structs also take accessor objects, this 910 // generates a variant without that argument. 911 if (lang_.language != IDLOptions::kCSharp) { 912 code += method_start + "(int j) { return "; 913 code += MakeCamel(field.name, lang_.first_camel_upper); 914 code += "(new " + type_name + "(), j); }\n"; 915 } 916 } else if (field.value.type.base_type == BASE_TYPE_UNION) { 917 if (lang_.language == IDLOptions::kCSharp) { 918 // Union types in C# use generic Table-derived type for better type 919 // safety. 920 method_start += "<TTable>"; 921 type_name = type_name_dest; 922 } 923 } 924 std::string getter = dest_cast + GenGetter(field.value.type); 925 code += method_start; 926 std::string default_cast = ""; 927 // only create default casts for c# scalars or vectors of scalars 928 if (lang_.language == IDLOptions::kCSharp && 929 (IsScalar(field.value.type.base_type) || 930 (field.value.type.base_type == BASE_TYPE_VECTOR && 931 IsScalar(field.value.type.element)))) { 932 // For scalars, default value will be returned by GetDefaultValue(). 933 // If the scalar is an enum, GetDefaultValue() returns an actual c# enum 934 // that doesn't need to be casted. However, default values for enum 935 // elements of vectors are integer literals ("0") and are still casted 936 // for clarity. 937 if (field.value.type.enum_def == nullptr || 938 field.value.type.base_type == BASE_TYPE_VECTOR) { 939 default_cast = "(" + type_name_dest + ")"; 940 } 941 } 942 std::string member_suffix = "; "; 943 if (IsScalar(field.value.type.base_type)) { 944 code += lang_.getter_prefix; 945 member_suffix += lang_.getter_suffix; 946 if (struct_def.fixed) { 947 code += " { return " + getter; 948 code += "(" + lang_.accessor_prefix + "bb_pos + "; 949 code += NumToString(field.value.offset) + ")"; 950 code += dest_mask; 951 } else { 952 code += offset_prefix + getter; 953 code += "(o + " + lang_.accessor_prefix + "bb_pos)" + dest_mask; 954 code += " : " + default_cast; 955 code += GenDefaultValue(field.value); 956 } 957 } else { 958 switch (field.value.type.base_type) { 959 case BASE_TYPE_STRUCT: 960 if (lang_.language != IDLOptions::kCSharp) { 961 code += "(" + type_name + " obj" + ")"; 962 } else { 963 code += lang_.getter_prefix; 964 member_suffix += lang_.getter_suffix; 965 } 966 if (struct_def.fixed) { 967 code += " { return " + obj + ".__assign(" + lang_.accessor_prefix; 968 code += "bb_pos + " + NumToString(field.value.offset) + ", "; 969 code += lang_.accessor_prefix + "bb)"; 970 } else { 971 code += offset_prefix + conditional_cast; 972 code += obj + ".__assign("; 973 code += field.value.type.struct_def->fixed 974 ? "o + " + lang_.accessor_prefix + "bb_pos" 975 : lang_.accessor_prefix + "__indirect(o + " + 976 lang_.accessor_prefix + "bb_pos)"; 977 code += ", " + lang_.accessor_prefix + "bb) : null"; 978 } 979 break; 980 case BASE_TYPE_STRING: 981 code += lang_.getter_prefix; 982 member_suffix += lang_.getter_suffix; 983 code += offset_prefix + getter + "(o + " + lang_.accessor_prefix; 984 code += "bb_pos) : null"; 985 break; 986 case BASE_TYPE_VECTOR: { 987 auto vectortype = field.value.type.VectorType(); 988 code += "("; 989 if (vectortype.base_type == BASE_TYPE_STRUCT) { 990 if (lang_.language != IDLOptions::kCSharp) 991 code += type_name + " obj, "; 992 getter = obj + ".__assign"; 993 } 994 code += "int j)" + offset_prefix + conditional_cast + getter +"("; 995 auto index = lang_.accessor_prefix + "__vector(o) + j * " + 996 NumToString(InlineSize(vectortype)); 997 if (vectortype.base_type == BASE_TYPE_STRUCT) { 998 code += vectortype.struct_def->fixed 999 ? index 1000 : lang_.accessor_prefix + "__indirect(" + index + ")"; 1001 code += ", " + lang_.accessor_prefix + "bb"; 1002 } else { 1003 code += index; 1004 } 1005 code += ")" + dest_mask + " : "; 1006 1007 code += field.value.type.element == BASE_TYPE_BOOL ? "false" : 1008 (IsScalar(field.value.type.element) ? default_cast + "0" : "null"); 1009 break; 1010 } 1011 case BASE_TYPE_UNION: 1012 if (lang_.language == IDLOptions::kCSharp) { 1013 code += "() where TTable : struct, IFlatbufferObject"; 1014 code += offset_prefix + "(TTable?)" + getter; 1015 code += "<TTable>(o) : null"; 1016 } else { 1017 code += "(" + type_name + " obj)" + offset_prefix + getter; 1018 code += "(obj, o) : null"; 1019 } 1020 break; 1021 default: 1022 assert(0); 1023 } 1024 } 1025 code += member_suffix; 1026 code += "}\n"; 1027 if (field.value.type.base_type == BASE_TYPE_VECTOR) { 1028 code += " public int " + MakeCamel(field.name, lang_.first_camel_upper); 1029 code += "Length"; 1030 code += lang_.getter_prefix; 1031 code += offset_prefix; 1032 code += lang_.accessor_prefix + "__vector_len(o) : 0; "; 1033 code += lang_.getter_suffix; 1034 code += "}\n"; 1035 // See if we should generate a by-key accessor. 1036 if (field.value.type.element == BASE_TYPE_STRUCT && 1037 !field.value.type.struct_def->fixed) { 1038 auto &sd = *field.value.type.struct_def; 1039 auto &fields = sd.fields.vec; 1040 for (auto kit = fields.begin(); kit != fields.end(); ++kit) { 1041 auto &key_field = **kit; 1042 if (key_field.key) { 1043 code += " public " + sd.name + lang_.optional_suffix + " "; 1044 code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey("; 1045 code += GenTypeNameDest(key_field.value.type) + " key)"; 1046 code += offset_prefix; 1047 code += sd.name + ".__lookup_by_key("; 1048 code += lang_.accessor_prefix + "__vector(o), key, "; 1049 code += lang_.accessor_prefix + "bb) : null; "; 1050 code += "}\n"; 1051 break; 1052 } 1053 } 1054 } 1055 } 1056 // Generate a ByteBuffer accessor for strings & vectors of scalars. 1057 if ((field.value.type.base_type == BASE_TYPE_VECTOR && 1058 IsScalar(field.value.type.VectorType().base_type)) || 1059 field.value.type.base_type == BASE_TYPE_STRING) { 1060 switch (lang_.language) { 1061 case IDLOptions::kJava: 1062 code += " public ByteBuffer "; 1063 code += MakeCamel(field.name, lang_.first_camel_upper); 1064 code += "AsByteBuffer() { return "; 1065 code += lang_.accessor_prefix + "__vector_as_bytebuffer("; 1066 code += NumToString(field.value.offset) + ", "; 1067 code += NumToString(field.value.type.base_type == BASE_TYPE_STRING 1068 ? 1 1069 : InlineSize(field.value.type.VectorType())); 1070 code += "); }\n"; 1071 break; 1072 case IDLOptions::kCSharp: 1073 code += " public ArraySegment<byte>? Get"; 1074 code += MakeCamel(field.name, lang_.first_camel_upper); 1075 code += "Bytes() { return "; 1076 code += lang_.accessor_prefix + "__vector_as_arraysegment("; 1077 code += NumToString(field.value.offset); 1078 code += "); }\n"; 1079 break; 1080 default: 1081 break; 1082 } 1083 } 1084 // generate object accessors if is nested_flatbuffer 1085 if (field.nested_flatbuffer) { 1086 auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer); 1087 auto nested_method_name = MakeCamel(field.name, lang_.first_camel_upper) 1088 + "As" + nested_type_name; 1089 auto get_nested_method_name = nested_method_name; 1090 if (lang_.language == IDLOptions::kCSharp) { 1091 get_nested_method_name = "Get" + nested_method_name; 1092 conditional_cast = "(" + nested_type_name + lang_.optional_suffix + ")"; 1093 } 1094 if (lang_.language != IDLOptions::kCSharp) { 1095 code += " public " + nested_type_name + lang_.optional_suffix + " "; 1096 code += nested_method_name + "() { return "; 1097 code += get_nested_method_name + "(new " + nested_type_name + "()); }\n"; 1098 } else { 1099 obj = "(new " + nested_type_name + "())"; 1100 } 1101 code += " public " + nested_type_name + lang_.optional_suffix + " "; 1102 code += get_nested_method_name + "("; 1103 if (lang_.language != IDLOptions::kCSharp) 1104 code += nested_type_name + " obj"; 1105 code += ") { int o = " + lang_.accessor_prefix + "__offset("; 1106 code += NumToString(field.value.offset) +"); "; 1107 code += "return o != 0 ? " + conditional_cast + obj + ".__assign("; 1108 code += lang_.accessor_prefix; 1109 code += "__indirect(" + lang_.accessor_prefix + "__vector(o)), "; 1110 code += lang_.accessor_prefix + "bb) : null; }\n"; 1111 } 1112 // Generate mutators for scalar fields or vectors of scalars. 1113 if (parser_.opts.mutable_buffer) { 1114 auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR 1115 ? field.value.type.VectorType() 1116 : field.value.type; 1117 // Boolean parameters have to be explicitly converted to byte 1118 // representation. 1119 auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL 1120 ? "(byte)(" + field.name + " ? 1 : 0)" 1121 : field.name; 1122 auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper); 1123 // A vector mutator also needs the index of the vector element it should 1124 // mutate. 1125 auto mutator_params = (field.value.type.base_type == BASE_TYPE_VECTOR 1126 ? "(int j, " 1127 : "(") + GenTypeNameDest(underlying_type) + " " + field.name + ") { "; 1128 auto setter_index = field.value.type.base_type == BASE_TYPE_VECTOR 1129 ? lang_.accessor_prefix + "__vector(o) + j * " + 1130 NumToString(InlineSize(underlying_type)) 1131 : (struct_def.fixed 1132 ? lang_.accessor_prefix + "bb_pos + " + 1133 NumToString(field.value.offset) 1134 : "o + " + lang_.accessor_prefix + "bb_pos"); 1135 if (IsScalar(field.value.type.base_type) || 1136 (field.value.type.base_type == BASE_TYPE_VECTOR && 1137 IsScalar(field.value.type.VectorType().base_type))) { 1138 code += " public "; 1139 code += struct_def.fixed ? "void " : lang_.bool_type; 1140 code += mutator_prefix + MakeCamel(field.name, true); 1141 code += mutator_params; 1142 if (struct_def.fixed) { 1143 code += GenSetter(underlying_type) + "(" + setter_index + ", "; 1144 code += src_cast + setter_parameter + "); }\n"; 1145 } else { 1146 code += "int o = " + lang_.accessor_prefix + "__offset("; 1147 code += NumToString(field.value.offset) + ");"; 1148 code += " if (o != 0) { " + GenSetter(underlying_type); 1149 code += "(" + setter_index + ", " + src_cast + setter_parameter + 1150 "); return true; } else { return false; } }\n"; 1151 } 1152 } 1153 } 1154 } 1155 code += "\n"; 1156 flatbuffers::FieldDef *key_field = nullptr; 1157 if (struct_def.fixed) { 1158 // create a struct constructor function 1159 code += " public static " + GenOffsetType(struct_def) + " "; 1160 code += FunctionStart('C') + "reate"; 1161 code += struct_def.name + "(FlatBufferBuilder builder"; 1162 GenStructArgs(struct_def, code_ptr, ""); 1163 code += ") {\n"; 1164 GenStructBody(struct_def, code_ptr, ""); 1165 code += " return "; 1166 code += GenOffsetConstruct(struct_def, 1167 "builder." + std::string(lang_.get_fbb_offset)); 1168 code += ";\n }\n"; 1169 } else { 1170 // Generate a method that creates a table in one go. This is only possible 1171 // when the table has no struct fields, since those have to be created 1172 // inline, and there's no way to do so in Java. 1173 bool has_no_struct_fields = true; 1174 int num_fields = 0; 1175 for (auto it = struct_def.fields.vec.begin(); 1176 it != struct_def.fields.vec.end(); ++it) { 1177 auto &field = **it; 1178 if (field.deprecated) continue; 1179 if (IsStruct(field.value.type)) { 1180 has_no_struct_fields = false; 1181 } else { 1182 num_fields++; 1183 } 1184 } 1185 // JVM specifications restrict default constructor params to be < 255. 1186 // Longs and doubles take up 2 units, so we set the limit to be < 127. 1187 if (has_no_struct_fields && num_fields && num_fields < 127) { 1188 // Generate a table constructor of the form: 1189 // public static int createName(FlatBufferBuilder builder, args...) 1190 code += " public static " + GenOffsetType(struct_def) + " "; 1191 code += FunctionStart('C') + "reate" + struct_def.name; 1192 code += "(FlatBufferBuilder builder"; 1193 for (auto it = struct_def.fields.vec.begin(); 1194 it != struct_def.fields.vec.end(); ++it) { 1195 auto &field = **it; 1196 if (field.deprecated) continue; 1197 code += ",\n "; 1198 code += GenTypeBasic(DestinationType(field.value.type, false)); 1199 code += " "; 1200 code += field.name; 1201 if (!IsScalar(field.value.type.base_type)) code += "Offset"; 1202 1203 // Java doesn't have defaults, which means this method must always 1204 // supply all arguments, and thus won't compile when fields are added. 1205 if (lang_.language != IDLOptions::kJava) { 1206 code += " = "; 1207 code += GenDefaultValueBasic(field.value); 1208 } 1209 } 1210 code += ") {\n builder."; 1211 code += FunctionStart('S') + "tartObject("; 1212 code += NumToString(struct_def.fields.vec.size()) + ");\n"; 1213 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; 1214 size; 1215 size /= 2) { 1216 for (auto it = struct_def.fields.vec.rbegin(); 1217 it != struct_def.fields.vec.rend(); ++it) { 1218 auto &field = **it; 1219 if (!field.deprecated && 1220 (!struct_def.sortbysize || 1221 size == SizeOf(field.value.type.base_type))) { 1222 code += " " + struct_def.name + "."; 1223 code += FunctionStart('A') + "dd"; 1224 code += MakeCamel(field.name) + "(builder, " + field.name; 1225 if (!IsScalar(field.value.type.base_type)) code += "Offset"; 1226 code += ");\n"; 1227 } 1228 } 1229 } 1230 code += " return " + struct_def.name + "."; 1231 code += FunctionStart('E') + "nd" + struct_def.name; 1232 code += "(builder);\n }\n\n"; 1233 } 1234 // Generate a set of static methods that allow table construction, 1235 // of the form: 1236 // public static void addName(FlatBufferBuilder builder, short name) 1237 // { builder.addShort(id, name, default); } 1238 // Unlike the Create function, these always work. 1239 code += " public static void " + FunctionStart('S') + "tart"; 1240 code += struct_def.name; 1241 code += "(FlatBufferBuilder builder) { builder."; 1242 code += FunctionStart('S') + "tartObject("; 1243 code += NumToString(struct_def.fields.vec.size()) + "); }\n"; 1244 for (auto it = struct_def.fields.vec.begin(); 1245 it != struct_def.fields.vec.end(); ++it) { 1246 auto &field = **it; 1247 if (field.deprecated) continue; 1248 if (field.key) key_field = &field; 1249 code += " public static void " + FunctionStart('A') + "dd"; 1250 code += MakeCamel(field.name); 1251 code += "(FlatBufferBuilder builder, "; 1252 code += GenTypeBasic(DestinationType(field.value.type, false)); 1253 auto argname = MakeCamel(field.name, false); 1254 if (!IsScalar(field.value.type.base_type)) argname += "Offset"; 1255 code += " " + argname + ") { builder." + FunctionStart('A') + "dd"; 1256 code += GenMethod(field.value.type) + "("; 1257 code += NumToString(it - struct_def.fields.vec.begin()) + ", "; 1258 code += SourceCastBasic(field.value.type); 1259 code += argname; 1260 if (!IsScalar(field.value.type.base_type) && 1261 field.value.type.base_type != BASE_TYPE_UNION && 1262 lang_.language == IDLOptions::kCSharp) { 1263 code += ".Value"; 1264 } 1265 code += ", "; 1266 if (lang_.language == IDLOptions::kJava) 1267 code += SourceCastBasic( field.value.type ); 1268 code += GenDefaultValue(field.value, false); 1269 code += "); }\n"; 1270 if (field.value.type.base_type == BASE_TYPE_VECTOR) { 1271 auto vector_type = field.value.type.VectorType(); 1272 auto alignment = InlineAlignment(vector_type); 1273 auto elem_size = InlineSize(vector_type); 1274 if (!IsStruct(vector_type)) { 1275 // Generate a method to create a vector from a Java array. 1276 code += " public static " + GenVectorOffsetType() + " "; 1277 code += FunctionStart('C') + "reate"; 1278 code += MakeCamel(field.name); 1279 code += "Vector(FlatBufferBuilder builder, "; 1280 code += GenTypeBasic(vector_type) + "[] data) "; 1281 code += "{ builder." + FunctionStart('S') + "tartVector("; 1282 code += NumToString(elem_size); 1283 code += ", data." + FunctionStart('L') + "ength, "; 1284 code += NumToString(alignment); 1285 code += "); for (int i = data."; 1286 code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder."; 1287 code += FunctionStart('A') + "dd"; 1288 code += GenMethod(vector_type); 1289 code += "("; 1290 code += SourceCastBasic(vector_type, false); 1291 code += "data[i]"; 1292 if (lang_.language == IDLOptions::kCSharp && 1293 (vector_type.base_type == BASE_TYPE_STRUCT || 1294 vector_type.base_type == BASE_TYPE_STRING)) 1295 code += ".Value"; 1296 code += "); return "; 1297 code += "builder." + FunctionStart('E') + "ndVector(); }\n"; 1298 } 1299 // Generate a method to start a vector, data to be added manually after. 1300 code += " public static void " + FunctionStart('S') + "tart"; 1301 code += MakeCamel(field.name); 1302 code += "Vector(FlatBufferBuilder builder, int numElems) "; 1303 code += "{ builder." + FunctionStart('S') + "tartVector("; 1304 code += NumToString(elem_size); 1305 code += ", numElems, " + NumToString(alignment); 1306 code += "); }\n"; 1307 } 1308 } 1309 code += " public static " + GenOffsetType(struct_def) + " "; 1310 code += FunctionStart('E') + "nd" + struct_def.name; 1311 code += "(FlatBufferBuilder builder) {\n int o = builder."; 1312 code += FunctionStart('E') + "ndObject();\n"; 1313 for (auto it = struct_def.fields.vec.begin(); 1314 it != struct_def.fields.vec.end(); 1315 ++it) { 1316 auto &field = **it; 1317 if (!field.deprecated && field.required) { 1318 code += " builder." + FunctionStart('R') + "equired(o, "; 1319 code += NumToString(field.value.offset); 1320 code += "); // " + field.name + "\n"; 1321 } 1322 } 1323 code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n"; 1324 if (parser_.root_struct_def_ == &struct_def) { 1325 code += " public static void "; 1326 code += FunctionStart('F') + "inish" + struct_def.name; 1327 code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def); 1328 code += " offset) {"; 1329 code += " builder." + FunctionStart('F') + "inish(offset"; 1330 if (lang_.language == IDLOptions::kCSharp) { 1331 code += ".Value"; 1332 } 1333 1334 if (parser_.file_identifier_.length()) 1335 code += ", \"" + parser_.file_identifier_ + "\""; 1336 code += "); }\n"; 1337 } 1338 } 1339 // Only generate key compare function for table, 1340 // because `key_field` is not set for struct 1341 if (struct_def.has_key && !struct_def.fixed) { 1342 if (lang_.language == IDLOptions::kJava) { 1343 code += "\n @Override\n protected int keysCompare("; 1344 code += "Integer o1, Integer o2, ByteBuffer _bb) {"; 1345 code += GenKeyGetter(key_field); 1346 code += " }\n"; 1347 } 1348 else { 1349 code += "\n public static VectorOffset "; 1350 code += "CreateSortedVectorOf" + struct_def.name; 1351 code += "(FlatBufferBuilder builder, "; 1352 code += "Offset<" + struct_def.name + ">"; 1353 code += "[] offsets) {\n"; 1354 code += " Array.Sort(offsets, (Offset<" + struct_def.name + 1355 "> o1, Offset<" + struct_def.name + "> o2) => " + GenKeyGetter(key_field); 1356 code += ");\n"; 1357 code += " return builder.CreateVectorOfTables(offsets);\n }\n"; 1358 } 1359 1360 code += "\n public static " + struct_def.name + lang_.optional_suffix; 1361 code += " __lookup_by_key(int vectorLocation, "; 1362 code += GenTypeNameDest(key_field->value.type); 1363 code += " key, ByteBuffer bb) {\n"; 1364 if (key_field->value.type.base_type == BASE_TYPE_STRING) { 1365 code += " byte[] byteKey = "; 1366 if (lang_.language == IDLOptions::kJava) 1367 code += "key.getBytes(Table.UTF8_CHARSET.get());\n"; 1368 else 1369 code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; 1370 } 1371 code += " int span = "; 1372 code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n"; 1373 code += " int start = 0;\n"; 1374 code += " while (span != 0) {\n"; 1375 code += " int middle = span / 2;\n"; 1376 code += GenLookupKeyGetter(key_field); 1377 code += " if (comp > 0) {\n"; 1378 code += " span = middle;\n"; 1379 code += " } else if (comp < 0) {\n"; 1380 code += " middle++;\n"; 1381 code += " start += middle;\n"; 1382 code += " span -= middle;\n"; 1383 code += " } else {\n"; 1384 code += " return new " + struct_def.name; 1385 code += "().__assign(tableOffset, bb);\n"; 1386 code += " }\n }\n"; 1387 code += " return null;\n"; 1388 code += " }\n"; 1389 } 1390 code += "}"; 1391 // Java does not need the closing semi-colon on class definitions. 1392 code += (lang_.language != IDLOptions::kJava) ? ";" : ""; 1393 code += "\n\n"; 1394 } 1395 const LanguageParameters& lang_; 1396 // This tracks the current namespace used to determine if a type need to be prefixed by its namespace 1397 const Namespace *cur_name_space_; 1398 }; 1399 } // namespace general 1400 1401 bool GenerateGeneral(const Parser &parser, const std::string &path, 1402 const std::string &file_name) { 1403 general::GeneralGenerator generator(parser, path, file_name); 1404 return generator.generate(); 1405 } 1406 1407 std::string GeneralMakeRule(const Parser &parser, const std::string &path, 1408 const std::string &file_name) { 1409 assert(parser.opts.lang <= IDLOptions::kMAX); 1410 const auto &lang = GetLangParams(parser.opts.lang); 1411 1412 std::string make_rule; 1413 1414 for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end(); 1415 ++it) { 1416 auto &enum_def = **it; 1417 if (make_rule != "") make_rule += " "; 1418 std::string directory = 1419 BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace); 1420 make_rule += directory + enum_def.name + lang.file_extension; 1421 } 1422 1423 for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end(); 1424 ++it) { 1425 auto &struct_def = **it; 1426 if (make_rule != "") make_rule += " "; 1427 std::string directory = 1428 BaseGenerator::NamespaceDir(parser, path, 1429 *struct_def.defined_namespace); 1430 make_rule += directory + struct_def.name + lang.file_extension; 1431 } 1432 1433 make_rule += ": "; 1434 auto included_files = parser.GetIncludedFilesRecursive(file_name); 1435 for (auto it = included_files.begin(); it != included_files.end(); ++it) { 1436 make_rule += " " + *it; 1437 } 1438 return make_rule; 1439 } 1440 1441 std::string BinaryFileName(const Parser &parser, 1442 const std::string &path, 1443 const std::string &file_name) { 1444 auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin"; 1445 return path + file_name + "." + ext; 1446 } 1447 1448 bool GenerateBinary(const Parser &parser, 1449 const std::string &path, 1450 const std::string &file_name) { 1451 return !parser.builder_.GetSize() || 1452 flatbuffers::SaveFile( 1453 BinaryFileName(parser, path, file_name).c_str(), 1454 reinterpret_cast<char *>(parser.builder_.GetBufferPointer()), 1455 parser.builder_.GetSize(), 1456 true); 1457 } 1458 1459 std::string BinaryMakeRule(const Parser &parser, 1460 const std::string &path, 1461 const std::string &file_name) { 1462 if (!parser.builder_.GetSize()) return ""; 1463 std::string filebase = flatbuffers::StripPath( 1464 flatbuffers::StripExtension(file_name)); 1465 std::string make_rule = BinaryFileName(parser, path, filebase) + ": " + 1466 file_name; 1467 auto included_files = parser.GetIncludedFilesRecursive( 1468 parser.root_struct_def_->file); 1469 for (auto it = included_files.begin(); 1470 it != included_files.end(); ++it) { 1471 make_rule += " " + *it; 1472 } 1473 return make_rule; 1474 } 1475 1476 } // namespace flatbuffers 1477