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 <unordered_set>
20
21#include "flatbuffers/code_generators.h"
22#include "flatbuffers/flatbuffers.h"
23#include "flatbuffers/idl.h"
24#include "flatbuffers/util.h"
25
26namespace flatbuffers {
27
28static TypedFloatConstantGenerator CSharpFloatGen("Double.", "Single.", "NaN",
29 "PositiveInfinity",
30 "NegativeInfinity");
31static CommentConfig comment_config = {
32 nullptr,
33 "///",
34 nullptr,
35};
36
37namespace csharp {
38class CSharpGenerator : public BaseGenerator {
39 struct FieldArrayLength {
40 std::string name;
41 int length;
42 };
43
44 public:
45 CSharpGenerator(const Parser &parser, const std::string &path,
46 const std::string &file_name)
47 : BaseGenerator(parser, path, file_name,
48 parser.opts.cs_global_alias ? "global::" : "", ".", "cs"),
49 cur_name_space_(nullptr) {
50 // clang-format off
51
52 // List of keywords retrieved from here:
53 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
54
55 // One per line to ease comparisons to that list are easier
56
57 static const char *const keywords[] = {
58 "abstract",
59 "as",
60 "base",
61 "bool",
62 "break",
63 "byte",
64 "case",
65 "catch",
66 "char",
67 "checked",
68 "class",
69 "const",
70 "continue",
71 "decimal",
72 "default",
73 "delegate",
74 "do",
75 "double",
76 "else",
77 "enum",
78 "event",
79 "explicit",
80 "extern",
81 "false",
82 "finally",
83 "fixed",
84 "float",
85 "for",
86 "foreach",
87 "goto",
88 "if",
89 "implicit",
90 "in",
91 "int",
92 "interface",
93 "internal",
94 "is",
95 "lock",
96 "long",
97 "namespace",
98 "new",
99 "null",
100 "object",
101 "operator",
102 "out",
103 "override",
104 "params",
105 "private",
106 "protected",
107 "public",
108 "readonly",
109 "ref",
110 "return",
111 "sbyte",
112 "sealed",
113 "short",
114 "sizeof",
115 "stackalloc",
116 "static",
117 "string",
118 "struct",
119 "switch",
120 "this",
121 "throw",
122 "true",
123 "try",
124 "typeof",
125 "uint",
126 "ulong",
127 "unchecked",
128 "unsafe",
129 "ushort",
130 "using",
131 "virtual",
132 "void",
133 "volatile",
134 "while",
135 nullptr,
136 // clang-format on
137 };
138
139 for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
140 }
141
142 CSharpGenerator &operator=(const CSharpGenerator &);
143
144 bool generate() {
145 std::string one_file_code;
146 cur_name_space_ = parser_.current_namespace_;
147
148 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
149 ++it) {
150 std::string enumcode;
151 auto &enum_def = **it;
152 if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
153 GenEnum(enum_def, &enumcode, parser_.opts);
154 if (parser_.opts.one_file) {
155 one_file_code += enumcode;
156 } else {
157 if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
158 false, parser_.opts))
159 return false;
160 }
161 }
162
163 for (auto it = parser_.structs_.vec.begin();
164 it != parser_.structs_.vec.end(); ++it) {
165 std::string declcode;
166 auto &struct_def = **it;
167 if (!parser_.opts.one_file)
168 cur_name_space_ = struct_def.defined_namespace;
169 GenStruct(struct_def, &declcode, parser_.opts);
170 if (parser_.opts.one_file) {
171 one_file_code += declcode;
172 } else {
173 if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
174 true, parser_.opts))
175 return false;
176 }
177 }
178
179 if (parser_.opts.one_file) {
180 return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
181 true, parser_.opts);
182 }
183 return true;
184 }
185
186 private:
187 std::unordered_set<std::string> keywords_;
188
189 std::string EscapeKeyword(const std::string &name) const {
190 return keywords_.find(name) == keywords_.end() ? name : "@" + name;
191 }
192
193 std::string Name(const FieldDef &field) const {
194 std::string name = MakeCamel(field.name, true);
195 return EscapeKeyword(name);
196 }
197
198 std::string Name(const Definition &def) const {
199 return EscapeKeyword(def.name);
200 }
201
202 std::string NamespacedName(const Definition &def) const {
203 return WrapInNameSpace(def.defined_namespace, Name(def));
204 }
205
206 std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
207
208 // Save out the generated code for a single class while adding
209 // declaration boilerplate.
210 bool SaveType(const std::string &defname, const Namespace &ns,
211 const std::string &classcode, bool needs_includes,
212 const IDLOptions &options) const {
213 if (!classcode.length()) return true;
214
215 std::string code =
216 "// <auto-generated>\n"
217 "// " +
218 std::string(FlatBuffersGeneratedWarning()) +
219 "\n"
220 "// </auto-generated>\n\n";
221
222 std::string namespace_name = FullNamespace(".", ns);
223 if (!namespace_name.empty()) {
224 code += "namespace " + namespace_name + "\n{\n\n";
225 }
226 if (needs_includes) {
227 code += "using global::System;\n";
228 code += "using global::System.Collections.Generic;\n";
229 code += "using global::FlatBuffers;\n\n";
230 }
231 code += classcode;
232 if (!namespace_name.empty()) { code += "\n}\n"; }
233 auto filename = NamespaceDir(ns) + defname;
234 if (options.one_file) { filename += options.filename_suffix; }
235 filename +=
236 options.filename_extension.empty() ? ".cs" : options.filename_extension;
237 return SaveFile(filename.c_str(), code, false);
238 }
239
240 const Namespace *CurrentNameSpace() const { return cur_name_space_; }
241
242 std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
243 // clang-format off
244 static const char * const csharp_typename[] = {
245 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, ...) \
246 #NTYPE,
247 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
248 #undef FLATBUFFERS_TD
249 };
250 // clang-format on
251
252 if (enableLangOverrides) {
253 if (IsEnum(type)) return NamespacedName(*type.enum_def);
254 if (type.base_type == BASE_TYPE_STRUCT) {
255 return "Offset<" + NamespacedName(*type.struct_def) + ">";
256 }
257 }
258
259 return csharp_typename[type.base_type];
260 }
261
262 inline std::string GenTypeBasic(const Type &type) const {
263 return GenTypeBasic(type, true);
264 }
265
266 std::string GenTypePointer(const Type &type) const {
267 switch (type.base_type) {
268 case BASE_TYPE_STRING: return "string";
269 case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
270 case BASE_TYPE_STRUCT: return NamespacedName(*type.struct_def);
271 case BASE_TYPE_UNION: return "TTable";
272 default: return "Table";
273 }
274 }
275
276 std::string GenTypeGet(const Type &type) const {
277 return IsScalar(type.base_type)
278 ? GenTypeBasic(type)
279 : (IsArray(type) ? GenTypeGet(type.VectorType())
280 : GenTypePointer(type));
281 }
282
283 std::string GenOffsetType(const StructDef &struct_def) const {
284 return "Offset<" + NamespacedName(struct_def) + ">";
285 }
286
287 std::string GenOffsetConstruct(const StructDef &struct_def,
288 const std::string &variable_name) const {
289 return "new Offset<" + NamespacedName(struct_def) + ">(" + variable_name +
290 ")";
291 }
292
293 // Casts necessary to correctly read serialized data
294 std::string DestinationCast(const Type &type) const {
295 if (IsSeries(type)) {
296 return DestinationCast(type.VectorType());
297 } else {
298 if (IsEnum(type)) return "(" + NamespacedName(*type.enum_def) + ")";
299 }
300 return "";
301 }
302
303 // Cast statements for mutator method parameters.
304 // In Java, parameters representing unsigned numbers need to be cast down to
305 // their respective type. For example, a long holding an unsigned int value
306 // would be cast down to int before being put onto the buffer. In C#, one cast
307 // directly cast an Enum to its underlying type, which is essential before
308 // putting it onto the buffer.
309 std::string SourceCast(const Type &type,
310 const bool isOptional = false) const {
311 if (IsSeries(type)) {
312 return SourceCast(type.VectorType());
313 } else {
314 if (IsEnum(type))
315 return "(" + GenTypeBasic(type, false) + (isOptional ? "?" : "") + ")";
316 }
317 return "";
318 }
319
320 std::string SourceCastBasic(const Type &type, const bool isOptional) const {
321 return IsScalar(type.base_type) ? SourceCast(type, isOptional) : "";
322 }
323
324 std::string GenEnumDefaultValue(const FieldDef &field) const {
325 auto &value = field.value;
326 FLATBUFFERS_ASSERT(value.type.enum_def);
327 auto &enum_def = *value.type.enum_def;
328 auto enum_val = enum_def.FindByValue(value.constant);
329 return enum_val ? (NamespacedName(enum_def) + "." + Name(*enum_val))
330 : value.constant;
331 }
332
333 std::string GenDefaultValue(const FieldDef &field,
334 bool enableLangOverrides) const {
335 // If it is an optional scalar field, the default is null
336 if (field.IsScalarOptional()) { return "null"; }
337
338 auto &value = field.value;
339 if (enableLangOverrides) {
340 // handles both enum case and vector of enum case
341 if (value.type.enum_def != nullptr &&
342 value.type.base_type != BASE_TYPE_UNION) {
343 return GenEnumDefaultValue(field);
344 }
345 }
346
347 auto longSuffix = "";
348 switch (value.type.base_type) {
349 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
350 case BASE_TYPE_ULONG: return value.constant;
351 case BASE_TYPE_UINT:
352 case BASE_TYPE_LONG: return value.constant + longSuffix;
353 default:
354 if (IsFloat(value.type.base_type))
355 return CSharpFloatGen.GenFloatConstant(field);
356 else
357 return value.constant;
358 }
359 }
360
361 std::string GenDefaultValue(const FieldDef &field) const {
362 return GenDefaultValue(field, true);
363 }
364
365 std::string GenDefaultValueBasic(const FieldDef &field,
366 bool enableLangOverrides) const {
367 auto &value = field.value;
368 if (!IsScalar(value.type.base_type)) {
369 if (enableLangOverrides) {
370 switch (value.type.base_type) {
371 case BASE_TYPE_STRING: return "default(StringOffset)";
372 case BASE_TYPE_STRUCT:
373 return "default(Offset<" + NamespacedName(*value.type.struct_def) +
374 ">)";
375 case BASE_TYPE_VECTOR: return "default(VectorOffset)";
376 default: break;
377 }
378 }
379 return "0";
380 }
381 return GenDefaultValue(field, enableLangOverrides);
382 }
383
384 std::string GenDefaultValueBasic(const FieldDef &field) const {
385 return GenDefaultValueBasic(field, true);
386 }
387
388 void GenEnum(EnumDef &enum_def, std::string *code_ptr,
389 const IDLOptions &opts) const {
390 std::string &code = *code_ptr;
391 if (enum_def.generated) return;
392
393 // Generate enum definitions of the form:
394 // public static (final) int name = value;
395 // In Java, we use ints rather than the Enum feature, because we want them
396 // to map directly to how they're used in C/C++ and file formats.
397 // That, and Java Enums are expensive, and not universally liked.
398 GenComment(enum_def.doc_comment, code_ptr, &comment_config);
399
400 if (opts.cs_gen_json_serializer && opts.generate_object_based_api) {
401 code +=
402 "[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters."
403 "StringEnumConverter))]\n";
404 }
405 // In C# this indicates enumeration values can be treated as bit flags.
406 if (enum_def.attributes.Lookup("bit_flags")) {
407 code += "[System.FlagsAttribute]\n";
408 }
409 if (enum_def.attributes.Lookup("private")) {
410 code += "internal ";
411 } else {
412 code += "public ";
413 }
414 code += "enum " + Name(enum_def);
415 code += " : " + GenTypeBasic(enum_def.underlying_type, false);
416 code += "\n{\n";
417 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
418 auto &ev = **it;
419 GenComment(ev.doc_comment, code_ptr, &comment_config, " ");
420 code += " ";
421 code += Name(ev) + " = ";
422 code += enum_def.ToString(ev);
423 code += ",\n";
424 }
425 // Close the class
426 code += "};\n\n";
427
428 if (opts.generate_object_based_api) {
429 GenEnum_ObjectAPI(enum_def, code_ptr, opts);
430 }
431 }
432
433 bool HasUnionStringValue(const EnumDef &enum_def) const {
434 if (!enum_def.is_union) return false;
435 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
436 auto &val = **it;
437 if (IsString(val.union_type)) { return true; }
438 }
439 return false;
440 }
441
442 // Returns the function name that is able to read a value of the given type.
443 std::string GenGetter(const Type &type) const {
444 switch (type.base_type) {
445 case BASE_TYPE_STRING: return "__p.__string";
446 case BASE_TYPE_STRUCT: return "__p.__struct";
447 case BASE_TYPE_UNION: return "__p.__union";
448 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
449 case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
450 default: {
451 std::string getter = "__p.bb.Get";
452 if (type.base_type == BASE_TYPE_BOOL) {
453 getter = "0!=" + getter;
454 } else if (GenTypeBasic(type, false) != "byte") {
455 getter += MakeCamel(GenTypeBasic(type, false));
456 }
457 return getter;
458 }
459 }
460 }
461
462 // Returns the function name that is able to read a value of the given type.
463 std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
464 const std::string &data_buffer,
465 const char *num = nullptr) const {
466 auto type = key_field->value.type;
467 auto dest_mask = "";
468 auto dest_cast = DestinationCast(type);
469 auto getter = data_buffer + ".Get";
470 if (GenTypeBasic(type, false) != "byte") {
471 getter += MakeCamel(GenTypeBasic(type, false));
472 }
473 getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
474 dest_mask;
475 return getter;
476 }
477
478 // Direct mutation is only allowed for scalar fields.
479 // Hence a setter method will only be generated for such fields.
480 std::string GenSetter(const Type &type) const {
481 if (IsScalar(type.base_type)) {
482 std::string setter = "__p.bb.Put";
483 if (GenTypeBasic(type, false) != "byte" &&
484 type.base_type != BASE_TYPE_BOOL) {
485 setter += MakeCamel(GenTypeBasic(type, false));
486 }
487 return setter;
488 } else {
489 return "";
490 }
491 }
492
493 // Returns the method name for use with add/put calls.
494 std::string GenMethod(const Type &type) const {
495 return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
496 : (IsStruct(type) ? "Struct" : "Offset");
497 }
498
499 // Recursively generate arguments for a constructor, to deal with nested
500 // structs.
501 void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
502 const char *nameprefix, size_t array_count = 0) const {
503 std::string &code = *code_ptr;
504 for (auto it = struct_def.fields.vec.begin();
505 it != struct_def.fields.vec.end(); ++it) {
506 auto &field = **it;
507 const auto &field_type = field.value.type;
508 const auto array_field = IsArray(field_type);
509 const auto &type = array_field ? field_type.VectorType() : field_type;
510 const auto array_cnt = array_field ? (array_count + 1) : array_count;
511 if (IsStruct(type)) {
512 // Generate arguments for a struct inside a struct. To ensure names
513 // don't clash, and to make it obvious these arguments are constructing
514 // a nested struct, prefix the name with the field name.
515 GenStructArgs(*field_type.struct_def, code_ptr,
516 (nameprefix + (EscapeKeyword(field.name) + "_")).c_str(),
517 array_cnt);
518 } else {
519 code += ", ";
520 code += GenTypeBasic(type);
521 if (field.IsScalarOptional()) { code += "?"; }
522 if (array_cnt > 0) {
523 code += "[";
524 for (size_t i = 1; i < array_cnt; i++) code += ",";
525 code += "]";
526 }
527 code += " ";
528 code += nameprefix;
529 code += Name(field);
530 }
531 }
532 }
533
534 // Recusively generate struct construction statements of the form:
535 // builder.putType(name);
536 // and insert manual padding.
537 void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
538 const char *nameprefix, size_t index = 0,
539 bool in_array = false) const {
540 std::string &code = *code_ptr;
541 std::string indent((index + 1) * 2, ' ');
542 code += indent + " builder.Prep(";
543 code += NumToString(struct_def.minalign) + ", ";
544 code += NumToString(struct_def.bytesize) + ");\n";
545 for (auto it = struct_def.fields.vec.rbegin();
546 it != struct_def.fields.vec.rend(); ++it) {
547 auto &field = **it;
548 const auto &field_type = field.value.type;
549 if (field.padding) {
550 code += indent + " builder.Pad(";
551 code += NumToString(field.padding) + ");\n";
552 }
553 if (IsStruct(field_type)) {
554 GenStructBody(*field_type.struct_def, code_ptr,
555 (nameprefix + (field.name + "_")).c_str(), index,
556 in_array);
557 } else {
558 const auto &type =
559 IsArray(field_type) ? field_type.VectorType() : field_type;
560 const auto index_var = "_idx" + NumToString(index);
561 if (IsArray(field_type)) {
562 code += indent + " for (int " + index_var + " = ";
563 code += NumToString(field_type.fixed_length);
564 code += "; " + index_var + " > 0; " + index_var + "--) {\n";
565 in_array = true;
566 }
567 if (IsStruct(type)) {
568 GenStructBody(*field_type.struct_def, code_ptr,
569 (nameprefix + (field.name + "_")).c_str(), index + 1,
570 in_array);
571 } else {
572 code += IsArray(field_type) ? " " : "";
573 code += indent + " builder.Put";
574 code += GenMethod(type) + "(";
575 code += SourceCast(type);
576 auto argname = nameprefix + Name(field);
577 code += argname;
578 size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
579 if (array_cnt > 0) {
580 code += "[";
581 for (size_t i = 0; in_array && i < array_cnt; i++) {
582 code += "_idx" + NumToString(i) + "-1";
583 if (i != (array_cnt - 1)) code += ",";
584 }
585 code += "]";
586 }
587 code += ");\n";
588 }
589 if (IsArray(field_type)) { code += indent + " }\n"; }
590 }
591 }
592 }
593 std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
594 const char *num = nullptr) const {
595 std::string key_offset =
596 "Table.__offset(" + NumToString(key_field->value.offset) + ", ";
597 if (num) {
598 key_offset += num;
599 key_offset += ".Value, builder.DataBuffer)";
600 } else {
601 key_offset += "bb.Length";
602 key_offset += " - tableOffset, bb)";
603 }
604 return key_offset;
605 }
606
607 std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
608 std::string key_getter = " ";
609 key_getter += "int tableOffset = Table.";
610 key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
611 key_getter += ", bb);\n ";
612 if (IsString(key_field->value.type)) {
613 key_getter += "int comp = Table.";
614 key_getter += "CompareStrings(";
615 key_getter += GenOffsetGetter(key_field);
616 key_getter += ", byteKey, bb);\n";
617 } else {
618 auto get_val = GenGetterForLookupByKey(key_field, "bb");
619 key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
620 }
621 return key_getter;
622 }
623
624 std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
625 std::string key_getter = "";
626 auto data_buffer = "builder.DataBuffer";
627 if (IsString(key_field->value.type)) {
628 key_getter += "Table.CompareStrings(";
629 key_getter += GenOffsetGetter(key_field, "o1") + ", ";
630 key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
631 } else {
632 auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
633 key_getter += field_getter;
634 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
635 key_getter += ".CompareTo(" + field_getter + ")";
636 }
637 return key_getter;
638 }
639
640 void GenStruct(StructDef &struct_def, std::string *code_ptr,
641 const IDLOptions &opts) const {
642 if (struct_def.generated) return;
643 std::string &code = *code_ptr;
644
645 // Generate a struct accessor class, with methods of the form:
646 // public type name() { return bb.getType(i + offset); }
647 // or for tables of the form:
648 // public type name() {
649 // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
650 // }
651 GenComment(struct_def.doc_comment, code_ptr, &comment_config);
652 if (struct_def.attributes.Lookup("private")) {
653 code += "internal ";
654 } else {
655 code += "public ";
656 }
657 if (struct_def.attributes.Lookup("csharp_partial")) {
658 // generate a partial class for this C# struct/table
659 code += "partial ";
660 }
661 code += "struct " + struct_def.name;
662 code += " : IFlatbufferObject";
663 code += "\n{\n";
664 code += " private ";
665 code += struct_def.fixed ? "Struct" : "Table";
666 code += " __p;\n";
667
668 code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
669
670 if (!struct_def.fixed) {
671 // Generate verson check method.
672 // Force compile time error if not using the same version runtime.
673 code += " public static void ValidateVersion() {";
674 code += " FlatBufferConstants.";
675 code += "FLATBUFFERS_2_0_0(); ";
676 code += "}\n";
677
678 // Generate a special accessor for the table that when used as the root
679 // of a FlatBuffer
680 std::string method_name = "GetRootAs" + struct_def.name;
681 std::string method_signature =
682 " public static " + struct_def.name + " " + method_name;
683
684 // create convenience method that doesn't require an existing object
685 code += method_signature + "(ByteBuffer _bb) ";
686 code += "{ return " + method_name + "(_bb, new " + struct_def.name +
687 "()); }\n";
688
689 // create method that allows object reuse
690 code +=
691 method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
692 code += "return (obj.__assign(_bb.GetInt(_bb.Position";
693 code += ") + _bb.Position";
694 code += ", _bb)); }\n";
695 if (parser_.root_struct_def_ == &struct_def) {
696 if (parser_.file_identifier_.length()) {
697 // Check if a buffer has the identifier.
698 code += " public static ";
699 code += "bool " + struct_def.name;
700 code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
701 code += "Table.__has_identifier(_bb, \"";
702 code += parser_.file_identifier_;
703 code += "\"); }\n";
704 }
705 }
706 }
707 // Generate the __init method that sets the field in a pre-existing
708 // accessor object. This is to allow object reuse.
709 code += " public void __init(int _i, ByteBuffer _bb) ";
710 code += "{ ";
711 code += "__p = new ";
712 code += struct_def.fixed ? "Struct" : "Table";
713 code += "(_i, _bb); ";
714 code += "}\n";
715 code +=
716 " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
717 code += "{ __init(_i, _bb); return this; }\n\n";
718 for (auto it = struct_def.fields.vec.begin();
719 it != struct_def.fields.vec.end(); ++it) {
720 auto &field = **it;
721 if (field.deprecated) continue;
722 GenComment(field.doc_comment, code_ptr, &comment_config, " ");
723 std::string type_name = GenTypeGet(field.value.type);
724 std::string type_name_dest = GenTypeGet(field.value.type);
725 std::string conditional_cast = "";
726 std::string optional = "";
727 if (!struct_def.fixed &&
728 (field.value.type.base_type == BASE_TYPE_STRUCT ||
729 field.value.type.base_type == BASE_TYPE_UNION ||
730 (IsVector(field.value.type) &&
731 (field.value.type.element == BASE_TYPE_STRUCT ||
732 field.value.type.element == BASE_TYPE_UNION)))) {
733 optional = "?";
734 conditional_cast = "(" + type_name_dest + optional + ")";
735 }
736 if (field.IsScalarOptional()) { optional = "?"; }
737 std::string dest_mask = "";
738 std::string dest_cast = DestinationCast(field.value.type);
739 std::string src_cast = SourceCast(field.value.type);
740 std::string field_name_camel = Name(field);
741 if (field_name_camel == struct_def.name) { field_name_camel += "_"; }
742 std::string method_start =
743 " public " + type_name_dest + optional + " " + field_name_camel;
744 std::string obj = "(new " + type_name + "())";
745
746 // Most field accessors need to retrieve and test the field offset first,
747 // this is the prefix code for that:
748 auto offset_prefix =
749 IsArray(field.value.type)
750 ? " { return "
751 : (" { int o = __p.__offset(" + NumToString(field.value.offset) +
752 "); return o != 0 ? ");
753 // Generate the accessors that don't do object reuse.
754 if (field.value.type.base_type == BASE_TYPE_STRUCT) {
755 } else if (IsVector(field.value.type) &&
756 field.value.type.element == BASE_TYPE_STRUCT) {
757 } else if (field.value.type.base_type == BASE_TYPE_UNION ||
758 (IsVector(field.value.type) &&
759 field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
760 method_start += "<TTable>";
761 type_name = type_name_dest;
762 }
763 std::string getter = dest_cast + GenGetter(field.value.type);
764 code += method_start;
765 std::string default_cast = "";
766 // only create default casts for c# scalars or vectors of scalars
767 if ((IsScalar(field.value.type.base_type) ||
768 (IsVector(field.value.type) &&
769 IsScalar(field.value.type.element)))) {
770 // For scalars, default value will be returned by GetDefaultValue().
771 // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
772 // that doesn't need to be casted. However, default values for enum
773 // elements of vectors are integer literals ("0") and are still casted
774 // for clarity.
775 // If the scalar is optional and enum, we still need the cast.
776 if ((field.value.type.enum_def == nullptr ||
777 IsVector(field.value.type)) ||
778 (IsEnum(field.value.type) && field.IsScalarOptional())) {
779 default_cast = "(" + type_name_dest + optional + ")";
780 }
781 }
782 std::string member_suffix = "; ";
783 if (IsScalar(field.value.type.base_type)) {
784 code += " { get";
785 member_suffix += "} ";
786 if (struct_def.fixed) {
787 code += " { return " + getter;
788 code += "(__p.bb_pos + ";
789 code += NumToString(field.value.offset) + ")";
790 code += dest_mask;
791 } else {
792 code += offset_prefix + getter;
793 code += "(o + __p.bb_pos)" + dest_mask;
794 code += " : " + default_cast;
795 code += GenDefaultValue(field);
796 }
797 } else {
798 switch (field.value.type.base_type) {
799 case BASE_TYPE_STRUCT:
800 code += " { get";
801 member_suffix += "} ";
802 if (struct_def.fixed) {
803 code += " { return " + obj + ".__assign(" + "__p.";
804 code += "bb_pos + " + NumToString(field.value.offset) + ", ";
805 code += "__p.bb)";
806 } else {
807 code += offset_prefix + conditional_cast;
808 code += obj + ".__assign(";
809 code += field.value.type.struct_def->fixed
810 ? "o + __p.bb_pos"
811 : "__p.__indirect(o + __p.bb_pos)";
812 code += ", __p.bb) : null";
813 }
814 break;
815 case BASE_TYPE_STRING:
816 code += " { get";
817 member_suffix += "} ";
818 code += offset_prefix + getter + "(o + " + "__p.";
819 code += "bb_pos) : null";
820 break;
821 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
822 case BASE_TYPE_VECTOR: {
823 auto vectortype = field.value.type.VectorType();
824 if (vectortype.base_type == BASE_TYPE_UNION) {
825 conditional_cast = "(TTable?)";
826 getter += "<TTable>";
827 }
828 code += "(";
829 if (vectortype.base_type == BASE_TYPE_STRUCT) {
830 getter = obj + ".__assign";
831 } else if (vectortype.base_type == BASE_TYPE_UNION) {
832 }
833 code += "int j)";
834 const auto body = offset_prefix + conditional_cast + getter + "(";
835 if (vectortype.base_type == BASE_TYPE_UNION) {
836 code += " where TTable : struct, IFlatbufferObject" + body;
837 } else {
838 code += body;
839 }
840 std::string index = "__p.";
841 if (IsArray(field.value.type)) {
842 index += "bb_pos + " + NumToString(field.value.offset) + " + ";
843 } else {
844 index += "__vector(o) + ";
845 }
846 index += "j * " + NumToString(InlineSize(vectortype));
847 if (vectortype.base_type == BASE_TYPE_STRUCT) {
848 code += vectortype.struct_def->fixed
849 ? index
850 : "__p.__indirect(" + index + ")";
851 code += ", __p.bb";
852 } else {
853 code += index;
854 }
855 code += ")" + dest_mask;
856 if (!IsArray(field.value.type)) {
857 code += " : ";
858 code +=
859 field.value.type.element == BASE_TYPE_BOOL
860 ? "false"
861 : (IsScalar(field.value.type.element) ? default_cast + "0"
862 : "null");
863 }
864 if (vectortype.base_type == BASE_TYPE_UNION &&
865 HasUnionStringValue(*vectortype.enum_def)) {
866 code += member_suffix;
867 code += "}\n";
868 code += " public string " + Name(field) + "AsString(int j)";
869 code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
870 code += "(" + index + ") : null";
871 }
872 break;
873 }
874 case BASE_TYPE_UNION:
875 code += "() where TTable : struct, IFlatbufferObject";
876 code += offset_prefix + "(TTable?)" + getter;
877 code += "<TTable>(o + __p.bb_pos) : null";
878 if (HasUnionStringValue(*field.value.type.enum_def)) {
879 code += member_suffix;
880 code += "}\n";
881 code += " public string " + Name(field) + "AsString()";
882 code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
883 code += "(o + __p.bb_pos) : null";
884 }
885 // As<> accesors for Unions
886 // Loop through all the possible union types and generate an As
887 // accessor that casts to the correct type.
888 for (auto uit = field.value.type.enum_def->Vals().begin();
889 uit != field.value.type.enum_def->Vals().end(); ++uit) {
890 auto val = *uit;
891 if (val->union_type.base_type == BASE_TYPE_NONE) { continue; }
892 auto union_field_type_name = GenTypeGet(val->union_type);
893 code += member_suffix + "}\n";
894 if (val->union_type.base_type == BASE_TYPE_STRUCT &&
895 val->union_type.struct_def->attributes.Lookup("private")) {
896 code += " internal ";
897 } else {
898 code += " public ";
899 }
900 code += union_field_type_name + " ";
901 code += field_name_camel + "As" + val->name + "() { return ";
902 code += field_name_camel;
903 if (IsString(val->union_type)) {
904 code += "AsString()";
905 } else {
906 code += "<" + union_field_type_name + ">().Value";
907 }
908 }
909 break;
910 default: FLATBUFFERS_ASSERT(0);
911 }
912 }
913 code += member_suffix;
914 code += "}\n";
915 if (IsVector(field.value.type)) {
916 code += " public int " + Name(field);
917 code += "Length";
918 code += " { get";
919 code += offset_prefix;
920 code += "__p.__vector_len(o) : 0; ";
921 code += "} ";
922 code += "}\n";
923 // See if we should generate a by-key accessor.
924 if (field.value.type.element == BASE_TYPE_STRUCT &&
925 !field.value.type.struct_def->fixed) {
926 auto &sd = *field.value.type.struct_def;
927 auto &fields = sd.fields.vec;
928 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
929 auto &key_field = **kit;
930 if (key_field.key) {
931 auto qualified_name = NamespacedName(sd);
932 code += " public " + qualified_name + "? ";
933 code += Name(field) + "ByKey(";
934 code += GenTypeGet(key_field.value.type) + " key)";
935 code += offset_prefix;
936 code += qualified_name + ".__lookup_by_key(";
937 code += "__p.__vector(o), key, ";
938 code += "__p.bb) : null; ";
939 code += "}\n";
940 break;
941 }
942 }
943 }
944 }
945 // Generate a ByteBuffer accessor for strings & vectors of scalars.
946 if ((IsVector(field.value.type) &&
947 IsScalar(field.value.type.VectorType().base_type)) ||
948 IsString(field.value.type)) {
949 code += "#if ENABLE_SPAN_T\n";
950 code += " public Span<" + GenTypeBasic(field.value.type.VectorType()) +
951 "> Get";
952 code += Name(field);
953 code += "Bytes() { return ";
954 code += "__p.__vector_as_span<" +
955 GenTypeBasic(field.value.type.VectorType()) + ">(";
956 code += NumToString(field.value.offset);
957 code +=
958 ", " + NumToString(SizeOf(field.value.type.VectorType().base_type));
959 code += "); }\n";
960 code += "#else\n";
961 code += " public ArraySegment<byte>? Get";
962 code += Name(field);
963 code += "Bytes() { return ";
964 code += "__p.__vector_as_arraysegment(";
965 code += NumToString(field.value.offset);
966 code += "); }\n";
967 code += "#endif\n";
968
969 // For direct blockcopying the data into a typed array
970 code += " public ";
971 code += GenTypeBasic(field.value.type.VectorType());
972 code += "[] Get";
973 code += Name(field);
974 code += "Array() { ";
975 if (IsEnum(field.value.type.VectorType())) {
976 // Since __vector_as_array does not work for enum types,
977 // fill array using an explicit loop.
978 code += "int o = __p.__offset(";
979 code += NumToString(field.value.offset);
980 code += "); if (o == 0) return null; int p = ";
981 code += "__p.__vector(o); int l = ";
982 code += "__p.__vector_len(o); ";
983 code += GenTypeBasic(field.value.type.VectorType());
984 code += "[] a = new ";
985 code += GenTypeBasic(field.value.type.VectorType());
986 code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
987 code += "(p + i * ";
988 code += NumToString(InlineSize(field.value.type.VectorType()));
989 code += "); } return a;";
990 } else {
991 code += "return ";
992 code += "__p.__vector_as_array<";
993 code += GenTypeBasic(field.value.type.VectorType());
994 code += ">(";
995 code += NumToString(field.value.offset);
996 code += ");";
997 }
998 code += " }\n";
999 }
1000 // generate object accessors if is nested_flatbuffer
1001 if (field.nested_flatbuffer) {
1002 auto nested_type_name = NamespacedName(*field.nested_flatbuffer);
1003 auto nested_method_name =
1004 Name(field) + "As" + field.nested_flatbuffer->name;
1005 auto get_nested_method_name = nested_method_name;
1006 get_nested_method_name = "Get" + nested_method_name;
1007 conditional_cast = "(" + nested_type_name + "?)";
1008 obj = "(new " + nested_type_name + "())";
1009 code += " public " + nested_type_name + "? ";
1010 code += get_nested_method_name + "(";
1011 code += ") { int o = __p.__offset(";
1012 code += NumToString(field.value.offset) + "); ";
1013 code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
1014 code += "__p.";
1015 code += "__indirect(__p.__vector(o)), ";
1016 code += "__p.bb) : null; }\n";
1017 }
1018 // Generate mutators for scalar fields or vectors of scalars.
1019 if (parser_.opts.mutable_buffer) {
1020 auto is_series = (IsSeries(field.value.type));
1021 const auto &underlying_type =
1022 is_series ? field.value.type.VectorType() : field.value.type;
1023 // Boolean parameters have to be explicitly converted to byte
1024 // representation.
1025 auto setter_parameter =
1026 underlying_type.base_type == BASE_TYPE_BOOL
1027 ? "(byte)(" + EscapeKeyword(field.name) + " ? 1 : 0)"
1028 : EscapeKeyword(field.name);
1029 auto mutator_prefix = MakeCamel("mutate", true);
1030 // A vector mutator also needs the index of the vector element it should
1031 // mutate.
1032 auto mutator_params = (is_series ? "(int j, " : "(") +
1033 GenTypeGet(underlying_type) + " " +
1034 EscapeKeyword(field.name) + ") { ";
1035 auto setter_index =
1036 is_series
1037 ? "__p." +
1038 (IsArray(field.value.type)
1039 ? "bb_pos + " + NumToString(field.value.offset)
1040 : "__vector(o)") +
1041 +" + j * " + NumToString(InlineSize(underlying_type))
1042 : (struct_def.fixed
1043 ? "__p.bb_pos + " + NumToString(field.value.offset)
1044 : "o + __p.bb_pos");
1045 if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
1046 code += " public ";
1047 code += struct_def.fixed ? "void " : "bool ";
1048 code += mutator_prefix + Name(field);
1049 code += mutator_params;
1050 if (struct_def.fixed) {
1051 code += GenSetter(underlying_type) + "(" + setter_index + ", ";
1052 code += src_cast + setter_parameter + "); }\n";
1053 } else {
1054 code += "int o = __p.__offset(";
1055 code += NumToString(field.value.offset) + ");";
1056 code += " if (o != 0) { " + GenSetter(underlying_type);
1057 code += "(" + setter_index + ", " + src_cast + setter_parameter +
1058 "); return true; } else { return false; } }\n";
1059 }
1060 }
1061 }
1062 if (parser_.opts.java_primitive_has_method &&
1063 IsScalar(field.value.type.base_type) && !struct_def.fixed) {
1064 auto vt_offset_constant = " public static final int VT_" +
1065 MakeScreamingCamel(field.name) + " = " +
1066 NumToString(field.value.offset) + ";";
1067
1068 code += vt_offset_constant;
1069 code += "\n";
1070 }
1071 }
1072 code += "\n";
1073 auto struct_has_create = false;
1074 std::set<flatbuffers::FieldDef *> field_has_create_set;
1075 flatbuffers::FieldDef *key_field = nullptr;
1076 if (struct_def.fixed) {
1077 struct_has_create = true;
1078 // create a struct constructor function
1079 code += " public static " + GenOffsetType(struct_def) + " ";
1080 code += "Create";
1081 code += struct_def.name + "(FlatBufferBuilder builder";
1082 GenStructArgs(struct_def, code_ptr, "");
1083 code += ") {\n";
1084 GenStructBody(struct_def, code_ptr, "");
1085 code += " return ";
1086 code += GenOffsetConstruct(struct_def, "builder.Offset");
1087 code += ";\n }\n";
1088 } else {
1089 // Generate a method that creates a table in one go. This is only possible
1090 // when the table has no struct fields, since those have to be created
1091 // inline, and there's no way to do so in Java.
1092 bool has_no_struct_fields = true;
1093 int num_fields = 0;
1094 for (auto it = struct_def.fields.vec.begin();
1095 it != struct_def.fields.vec.end(); ++it) {
1096 auto &field = **it;
1097 if (field.deprecated) continue;
1098 if (IsStruct(field.value.type)) {
1099 has_no_struct_fields = false;
1100 } else {
1101 num_fields++;
1102 }
1103 }
1104 // JVM specifications restrict default constructor params to be < 255.
1105 // Longs and doubles take up 2 units, so we set the limit to be < 127.
1106 if ((has_no_struct_fields || opts.generate_object_based_api) &&
1107 num_fields && num_fields < 127) {
1108 struct_has_create = true;
1109 // Generate a table constructor of the form:
1110 // public static int createName(FlatBufferBuilder builder, args...)
1111 code += " public static " + GenOffsetType(struct_def) + " ";
1112 code += "Create" + struct_def.name;
1113 code += "(FlatBufferBuilder builder";
1114 for (auto it = struct_def.fields.vec.begin();
1115 it != struct_def.fields.vec.end(); ++it) {
1116 auto &field = **it;
1117 if (field.deprecated) continue;
1118 code += ",\n ";
1119 if (IsStruct(field.value.type) && opts.generate_object_based_api) {
1120 code += WrapInNameSpace(
1121 field.value.type.struct_def->defined_namespace,
1122 GenTypeName_ObjectAPI(field.value.type.struct_def->name, opts));
1123 code += " ";
1124 code += EscapeKeyword(field.name);
1125 code += " = null";
1126 } else {
1127 code += GenTypeBasic(field.value.type);
1128 if (field.IsScalarOptional()) { code += "?"; }
1129 code += " ";
1130 code += EscapeKeyword(field.name);
1131 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1132
1133 code += " = ";
1134 code += GenDefaultValueBasic(field);
1135 }
1136 }
1137 code += ") {\n builder.";
1138 code += "StartTable(";
1139 code += NumToString(struct_def.fields.vec.size()) + ");\n";
1140 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1141 size; size /= 2) {
1142 for (auto it = struct_def.fields.vec.rbegin();
1143 it != struct_def.fields.vec.rend(); ++it) {
1144 auto &field = **it;
1145 if (!field.deprecated &&
1146 (!struct_def.sortbysize ||
1147 size == SizeOf(field.value.type.base_type))) {
1148 code += " " + struct_def.name + ".";
1149 code += "Add";
1150 code += Name(field) + "(builder, ";
1151 if (IsStruct(field.value.type) &&
1152 opts.generate_object_based_api) {
1153 code += GenTypePointer(field.value.type) + ".Pack(builder, " +
1154 EscapeKeyword(field.name) + ")";
1155 } else {
1156 code += EscapeKeyword(field.name);
1157 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1158 }
1159
1160 code += ");\n";
1161 }
1162 }
1163 }
1164 code += " return " + struct_def.name + ".";
1165 code += "End" + struct_def.name;
1166 code += "(builder);\n }\n\n";
1167 }
1168 // Generate a set of static methods that allow table construction,
1169 // of the form:
1170 // public static void addName(FlatBufferBuilder builder, short name)
1171 // { builder.addShort(id, name, default); }
1172 // Unlike the Create function, these always work.
1173 code += " public static void Start";
1174 code += struct_def.name;
1175 code += "(FlatBufferBuilder builder) { builder.";
1176 code += "StartTable(";
1177 code += NumToString(struct_def.fields.vec.size()) + "); }\n";
1178 for (auto it = struct_def.fields.vec.begin();
1179 it != struct_def.fields.vec.end(); ++it) {
1180 auto &field = **it;
1181 if (field.deprecated) continue;
1182 if (field.key) key_field = &field;
1183 code += " public static void Add";
1184 code += Name(field);
1185 code += "(FlatBufferBuilder builder, ";
1186 code += GenTypeBasic(field.value.type);
1187 auto argname = MakeCamel(field.name, false);
1188 if (!IsScalar(field.value.type.base_type)) argname += "Offset";
1189 if (field.IsScalarOptional()) { code += "?"; }
1190 code += " " + EscapeKeyword(argname) + ") { builder.Add";
1191 code += GenMethod(field.value.type) + "(";
1192 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1193 code += SourceCastBasic(field.value.type, field.IsScalarOptional());
1194 code += EscapeKeyword(argname);
1195 if (!IsScalar(field.value.type.base_type) &&
1196 field.value.type.base_type != BASE_TYPE_UNION) {
1197 code += ".Value";
1198 }
1199 if (!field.IsScalarOptional()) {
1200 // When the scalar is optional, use the builder method that doesn't
1201 // supply a default value. Otherwise, we to continue to use the
1202 // default value method.
1203 code += ", ";
1204 code += GenDefaultValue(field, false);
1205 }
1206 code += "); }\n";
1207 if (IsVector(field.value.type)) {
1208 auto vector_type = field.value.type.VectorType();
1209 auto alignment = InlineAlignment(vector_type);
1210 auto elem_size = InlineSize(vector_type);
1211 if (!IsStruct(vector_type)) {
1212 field_has_create_set.insert(&field);
1213 code += " public static VectorOffset ";
1214 code += "Create";
1215 code += Name(field);
1216 code += "Vector(FlatBufferBuilder builder, ";
1217 code += GenTypeBasic(vector_type) + "[] data) ";
1218 code += "{ builder.StartVector(";
1219 code += NumToString(elem_size);
1220 code += ", data.Length, ";
1221 code += NumToString(alignment);
1222 code += "); for (int i = data.";
1223 code += "Length - 1; i >= 0; i--) builder.";
1224 code += "Add";
1225 code += GenMethod(vector_type);
1226 code += "(";
1227 // At the moment there is no support of the type Vector with
1228 // optional enum, e.g. if we have enum type SomeEnum there is no way
1229 // to define `SomeEmum?[] enums` in FlatBuffer schema, so isOptional
1230 // = false
1231 code += SourceCastBasic(vector_type, false);
1232 code += "data[i]";
1233 if (vector_type.base_type == BASE_TYPE_STRUCT ||
1234 IsString(vector_type))
1235 code += ".Value";
1236 code += "); return ";
1237 code += "builder.EndVector(); }\n";
1238
1239 code += " public static VectorOffset ";
1240 code += "Create";
1241 code += Name(field);
1242 code += "VectorBlock(FlatBufferBuilder builder, ";
1243 code += GenTypeBasic(vector_type) + "[] data) ";
1244 code += "{ builder.StartVector(";
1245 code += NumToString(elem_size);
1246 code += ", data.Length, ";
1247 code += NumToString(alignment);
1248 code += "); builder.Add(data); return builder.EndVector(); }\n";
1249 }
1250 // Generate a method to start a vector, data to be added manually
1251 // after.
1252 code += " public static void Start";
1253 code += Name(field);
1254 code += "Vector(FlatBufferBuilder builder, int numElems) ";
1255 code += "{ builder.StartVector(";
1256 code += NumToString(elem_size);
1257 code += ", numElems, " + NumToString(alignment);
1258 code += "); }\n";
1259 }
1260 }
1261 code += " public static " + GenOffsetType(struct_def) + " ";
1262 code += "End" + struct_def.name;
1263 code += "(FlatBufferBuilder builder) {\n int o = builder.";
1264 code += "EndTable();\n";
1265 for (auto it = struct_def.fields.vec.begin();
1266 it != struct_def.fields.vec.end(); ++it) {
1267 auto &field = **it;
1268 if (!field.deprecated && field.IsRequired()) {
1269 code += " builder.Required(o, ";
1270 code += NumToString(field.value.offset);
1271 code += "); // " + field.name + "\n";
1272 }
1273 }
1274 code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n";
1275 if (parser_.root_struct_def_ == &struct_def) {
1276 std::string size_prefix[] = { "", "SizePrefixed" };
1277 for (int i = 0; i < 2; ++i) {
1278 code += " public static void ";
1279 code += "Finish" + size_prefix[i] + struct_def.name;
1280 code +=
1281 "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1282 code += " offset) {";
1283 code += " builder.Finish" + size_prefix[i] + "(offset";
1284 code += ".Value";
1285
1286 if (parser_.file_identifier_.length())
1287 code += ", \"" + parser_.file_identifier_ + "\"";
1288 code += "); }\n";
1289 }
1290 }
1291 }
1292 // Only generate key compare function for table,
1293 // because `key_field` is not set for struct
1294 if (struct_def.has_key && !struct_def.fixed) {
1295 FLATBUFFERS_ASSERT(key_field);
1296 code += "\n public static VectorOffset ";
1297 code += "CreateSortedVectorOf" + struct_def.name;
1298 code += "(FlatBufferBuilder builder, ";
1299 code += "Offset<" + struct_def.name + ">";
1300 code += "[] offsets) {\n";
1301 code += " Array.Sort(offsets, (Offset<" + struct_def.name +
1302 "> o1, Offset<" + struct_def.name + "> o2) => " +
1303 GenKeyGetter(key_field);
1304 code += ");\n";
1305 code += " return builder.CreateVectorOfTables(offsets);\n }\n";
1306
1307 code += "\n public static " + struct_def.name + "?";
1308 code += " __lookup_by_key(";
1309 code += "int vectorLocation, ";
1310 code += GenTypeGet(key_field->value.type);
1311 code += " key, ByteBuffer bb) {\n";
1312 if (IsString(key_field->value.type)) {
1313 code += " byte[] byteKey = ";
1314 code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
1315 }
1316 code += " int span = ";
1317 code += "bb.GetInt(vectorLocation - 4);\n";
1318 code += " int start = 0;\n";
1319 code += " while (span != 0) {\n";
1320 code += " int middle = span / 2;\n";
1321 code += GenLookupKeyGetter(key_field);
1322 code += " if (comp > 0) {\n";
1323 code += " span = middle;\n";
1324 code += " } else if (comp < 0) {\n";
1325 code += " middle++;\n";
1326 code += " start += middle;\n";
1327 code += " span -= middle;\n";
1328 code += " } else {\n";
1329 code += " return ";
1330 code += "new " + struct_def.name + "()";
1331 code += ".__assign(tableOffset, bb);\n";
1332 code += " }\n }\n";
1333 code += " return null;\n";
1334 code += " }\n";
1335 }
1336
1337 if (opts.generate_object_based_api) {
1338 GenPackUnPack_ObjectAPI(struct_def, code_ptr, opts, struct_has_create,
1339 field_has_create_set);
1340 }
1341 code += "}\n\n";
1342
1343 if (opts.generate_object_based_api) {
1344 GenStruct_ObjectAPI(struct_def, code_ptr, opts);
1345 }
1346 }
1347
1348 void GenVectorAccessObject(StructDef &struct_def,
1349 std::string *code_ptr) const {
1350 auto &code = *code_ptr;
1351 // Generate a vector of structs accessor class.
1352 code += "\n";
1353 code += " ";
1354 if (!struct_def.attributes.Lookup("private")) code += "public ";
1355 code += "static struct Vector : BaseVector\n{\n";
1356
1357 // Generate the __assign method that sets the field in a pre-existing
1358 // accessor object. This is to allow object reuse.
1359 std::string method_indent = " ";
1360 code += method_indent + "public Vector ";
1361 code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1362 code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1363
1364 auto type_name = struct_def.name;
1365 auto method_start = method_indent + "public " + type_name + " Get";
1366 // Generate the accessors that don't do object reuse.
1367 code += method_start + "(int j) { return Get";
1368 code += "(new " + type_name + "(), j); }\n";
1369 code += method_start + "(" + type_name + " obj, int j) { ";
1370 code += " return obj.__assign(";
1371 code += struct_def.fixed ? "__p.__element(j)"
1372 : "__p.__indirect(__p.__element(j), bb)";
1373 code += ", __p.bb); }\n";
1374 // See if we should generate a by-key accessor.
1375 if (!struct_def.fixed) {
1376 auto &fields = struct_def.fields.vec;
1377 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1378 auto &key_field = **kit;
1379 if (key_field.key) {
1380 auto nullable_annotation =
1381 parser_.opts.gen_nullable ? "@Nullable " : "";
1382 code += method_indent + nullable_annotation;
1383 code += "public " + type_name + "? ";
1384 code += "GetByKey(";
1385 code += GenTypeGet(key_field.value.type) + " key) { ";
1386 code += " return __lookup_by_key(null, ";
1387 code += "__p.__vector(), key, ";
1388 code += "__p.bb); ";
1389 code += "}\n";
1390 code += method_indent + nullable_annotation;
1391 code += "public " + type_name + "?" + " ";
1392 code += "GetByKey(";
1393 code += type_name + "? obj, ";
1394 code += GenTypeGet(key_field.value.type) + " key) { ";
1395 code += " return __lookup_by_key(obj, ";
1396 code += "__p.__vector(), key, ";
1397 code += "__p.bb); ";
1398 code += "}\n";
1399 break;
1400 }
1401 }
1402 }
1403 code += " }\n";
1404 }
1405
1406 void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
1407 const IDLOptions &opts) const {
1408 auto &code = *code_ptr;
1409 if (enum_def.generated) return;
1410 if (!enum_def.is_union) return;
1411 if (enum_def.attributes.Lookup("private")) {
1412 code += "internal ";
1413 } else {
1414 code += "public ";
1415 }
1416 auto union_name = enum_def.name + "Union";
1417 code += "class " + union_name + " {\n";
1418 // Type
1419 code += " public " + enum_def.name + " Type { get; set; }\n";
1420 // Value
1421 code += " public object Value { get; set; }\n";
1422 code += "\n";
1423 // Constructor
1424 code += " public " + union_name + "() {\n";
1425 code += " this.Type = " + enum_def.name + "." +
1426 enum_def.Vals()[0]->name + ";\n";
1427 code += " this.Value = null;\n";
1428 code += " }\n\n";
1429 // As<T>
1430 code += " public T As<T>() where T : class { return this.Value as T; }\n";
1431 // As, From
1432 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1433 auto &ev = **it;
1434 if (ev.union_type.base_type == BASE_TYPE_NONE) continue;
1435 auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1436 std::string accessibility =
1437 (ev.union_type.base_type == BASE_TYPE_STRUCT &&
1438 ev.union_type.struct_def->attributes.Lookup("private"))
1439 ? "internal"
1440 : "public";
1441 // As
1442 code += " " + accessibility + " " + type_name + " As" + ev.name +
1443 "() { return this.As<" + type_name + ">(); }\n";
1444 // From
1445 auto lower_ev_name = ev.name;
1446 std::transform(lower_ev_name.begin(), lower_ev_name.end(),
1447 lower_ev_name.begin(), CharToLower);
1448 code += " " + accessibility + " static " + union_name + " From" +
1449 ev.name + "(" + type_name + " _" + lower_ev_name +
1450 ") { return new " + union_name + "{ Type = " + Name(enum_def) +
1451 "." + Name(ev) + ", Value = _" + lower_ev_name + " }; }\n";
1452 }
1453 code += "\n";
1454 // Pack()
1455 code += " public static int Pack(FlatBuffers.FlatBufferBuilder builder, " +
1456 union_name + " _o) {\n";
1457 code += " switch (_o.Type) {\n";
1458 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1459 auto &ev = **it;
1460 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1461 code += " default: return 0;\n";
1462 } else {
1463 code += " case " + Name(enum_def) + "." + Name(ev) + ": return ";
1464 if (IsString(ev.union_type)) {
1465 code += "builder.CreateString(_o.As" + ev.name + "()).Value;\n";
1466 } else {
1467 code += GenTypeGet(ev.union_type) + ".Pack(builder, _o.As" + ev.name +
1468 "()).Value;\n";
1469 }
1470 }
1471 }
1472 code += " }\n";
1473 code += " }\n";
1474 code += "}\n\n";
1475 // JsonConverter
1476 if (opts.cs_gen_json_serializer) {
1477 if (enum_def.attributes.Lookup("private")) {
1478 code += "internal ";
1479 } else {
1480 code += "public ";
1481 }
1482 code += "class " + union_name +
1483 "_JsonConverter : Newtonsoft.Json.JsonConverter {\n";
1484 code += " public override bool CanConvert(System.Type objectType) {\n";
1485 code += " return objectType == typeof(" + union_name +
1486 ") || objectType == typeof(System.Collections.Generic.List<" +
1487 union_name + ">);\n";
1488 code += " }\n";
1489 code +=
1490 " public override void WriteJson(Newtonsoft.Json.JsonWriter writer, "
1491 "object value, "
1492 "Newtonsoft.Json.JsonSerializer serializer) {\n";
1493 code += " var _olist = value as System.Collections.Generic.List<" +
1494 union_name + ">;\n";
1495 code += " if (_olist != null) {\n";
1496 code += " writer.WriteStartArray();\n";
1497 code +=
1498 " foreach (var _o in _olist) { this.WriteJson(writer, _o, "
1499 "serializer); }\n";
1500 code += " writer.WriteEndArray();\n";
1501 code += " } else {\n";
1502 code += " this.WriteJson(writer, value as " + union_name +
1503 ", serializer);\n";
1504 code += " }\n";
1505 code += " }\n";
1506 code += " public void WriteJson(Newtonsoft.Json.JsonWriter writer, " +
1507 union_name +
1508 " _o, "
1509 "Newtonsoft.Json.JsonSerializer serializer) {\n";
1510 code += " if (_o == null) return;\n";
1511 code += " serializer.Serialize(writer, _o.Value);\n";
1512 code += " }\n";
1513 code +=
1514 " public override object ReadJson(Newtonsoft.Json.JsonReader "
1515 "reader, "
1516 "System.Type objectType, "
1517 "object existingValue, Newtonsoft.Json.JsonSerializer serializer) "
1518 "{\n";
1519 code +=
1520 " var _olist = existingValue as System.Collections.Generic.List<" +
1521 union_name + ">;\n";
1522 code += " if (_olist != null) {\n";
1523 code += " for (var _j = 0; _j < _olist.Count; ++_j) {\n";
1524 code += " reader.Read();\n";
1525 code +=
1526 " _olist[_j] = this.ReadJson(reader, _olist[_j], "
1527 "serializer);\n";
1528 code += " }\n";
1529 code += " reader.Read();\n";
1530 code += " return _olist;\n";
1531 code += " } else {\n";
1532 code += " return this.ReadJson(reader, existingValue as " +
1533 union_name + ", serializer);\n";
1534 code += " }\n";
1535 code += " }\n";
1536 code += " public " + union_name +
1537 " ReadJson(Newtonsoft.Json.JsonReader reader, " + union_name +
1538 " _o, Newtonsoft.Json.JsonSerializer serializer) {\n";
1539 code += " if (_o == null) return null;\n";
1540 code += " switch (_o.Type) {\n";
1541 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
1542 ++it) {
1543 auto &ev = **it;
1544 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1545 code += " default: break;\n";
1546 } else {
1547 auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1548 code += " case " + Name(enum_def) + "." + Name(ev) +
1549 ": _o.Value = serializer.Deserialize<" + type_name +
1550 ">(reader); break;\n";
1551 }
1552 }
1553 code += " }\n";
1554 code += " return _o;\n";
1555 code += " }\n";
1556 code += "}\n\n";
1557 }
1558 }
1559
1560 std::string GenTypeName_ObjectAPI(const std::string &name,
1561 const IDLOptions &opts) const {
1562 return opts.object_prefix + name + opts.object_suffix;
1563 }
1564
1565 void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
1566 const std::string &camel_name,
1567 bool is_vector) const {
1568 auto &code = *code_ptr;
1569 std::string varialbe_name = "_o." + camel_name;
1570 std::string type_suffix = "";
1571 std::string func_suffix = "()";
1572 std::string indent = " ";
1573 if (is_vector) {
1574 varialbe_name = "_o_" + camel_name;
1575 type_suffix = "(_j)";
1576 func_suffix = "(_j)";
1577 indent = " ";
1578 }
1579 if (is_vector) {
1580 code += indent + "var " + varialbe_name + " = new ";
1581 } else {
1582 code += indent + varialbe_name + " = new ";
1583 }
1584 code += NamespacedName(enum_def) + "Union();\n";
1585 code += indent + varialbe_name + ".Type = this." + camel_name + "Type" +
1586 type_suffix + ";\n";
1587 code +=
1588 indent + "switch (this." + camel_name + "Type" + type_suffix + ") {\n";
1589 for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end();
1590 ++eit) {
1591 auto &ev = **eit;
1592 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1593 code += indent + " default: break;\n";
1594 } else {
1595 code += indent + " case " + NamespacedName(enum_def) + "." + ev.name +
1596 ":\n";
1597 code += indent + " " + varialbe_name + ".Value = this." + camel_name;
1598 if (IsString(ev.union_type)) {
1599 code += "AsString" + func_suffix + ";\n";
1600 } else {
1601 code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix;
1602 code += ".HasValue ? this." + camel_name;
1603 code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix +
1604 ".Value.UnPack() : null;\n";
1605 }
1606 code += indent + " break;\n";
1607 }
1608 }
1609 code += indent + "}\n";
1610 if (is_vector) {
1611 code += indent + "_o." + camel_name + ".Add(" + varialbe_name + ");\n";
1612 }
1613 }
1614
1615 void GenPackUnPack_ObjectAPI(
1616 StructDef &struct_def, std::string *code_ptr, const IDLOptions &opts,
1617 bool struct_has_create,
1618 const std::set<FieldDef *> &field_has_create) const {
1619 auto &code = *code_ptr;
1620 auto struct_name = GenTypeName_ObjectAPI(struct_def.name, opts);
1621 // UnPack()
1622 code += " public " + struct_name + " UnPack() {\n";
1623 code += " var _o = new " + struct_name + "();\n";
1624 code += " this.UnPackTo(_o);\n";
1625 code += " return _o;\n";
1626 code += " }\n";
1627 // UnPackTo()
1628 code += " public void UnPackTo(" + struct_name + " _o) {\n";
1629 for (auto it = struct_def.fields.vec.begin();
1630 it != struct_def.fields.vec.end(); ++it) {
1631 auto &field = **it;
1632 if (field.deprecated) continue;
1633 auto camel_name = Name(field);
1634 auto start = " _o." + camel_name + " = ";
1635 switch (field.value.type.base_type) {
1636 case BASE_TYPE_STRUCT: {
1637 auto fixed = struct_def.fixed && field.value.type.struct_def->fixed;
1638 if (fixed) {
1639 code += start + "this." + camel_name + ".UnPack();\n";
1640 } else {
1641 code += start + "this." + camel_name + ".HasValue ? this." +
1642 camel_name + ".Value.UnPack() : null;\n";
1643 }
1644 break;
1645 }
1646 case BASE_TYPE_ARRAY: {
1647 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
1648 auto length_str = NumToString(field.value.type.fixed_length);
1649 auto unpack_method = field.value.type.struct_def == nullptr ? ""
1650 : field.value.type.struct_def->fixed
1651 ? ".UnPack()"
1652 : "?.UnPack()";
1653 code += start + "new " + type_name.substr(0, type_name.length() - 1) +
1654 length_str + "];\n";
1655 code += " for (var _j = 0; _j < " + length_str + "; ++_j) { _o." +
1656 camel_name + "[_j] = this." + camel_name + "(_j)" +
1657 unpack_method + "; }\n";
1658 break;
1659 }
1660 case BASE_TYPE_VECTOR:
1661 if (field.value.type.element == BASE_TYPE_UNION) {
1662 code += start + "new " +
1663 GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1664 code += " for (var _j = 0; _j < this." + camel_name +
1665 "Length; ++_j) {\n";
1666 GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1667 camel_name, true);
1668 code += " }\n";
1669 } else if (field.value.type.element != BASE_TYPE_UTYPE) {
1670 auto fixed = field.value.type.struct_def == nullptr;
1671 code += start + "new " +
1672 GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1673 code += " for (var _j = 0; _j < this." + camel_name +
1674 "Length; ++_j) {";
1675 code += "_o." + camel_name + ".Add(";
1676 if (fixed) {
1677 code += "this." + camel_name + "(_j)";
1678 } else {
1679 code += "this." + camel_name + "(_j).HasValue ? this." +
1680 camel_name + "(_j).Value.UnPack() : null";
1681 }
1682 code += ");}\n";
1683 }
1684 break;
1685 case BASE_TYPE_UTYPE: break;
1686 case BASE_TYPE_UNION: {
1687 GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1688 camel_name, false);
1689 break;
1690 }
1691 default: {
1692 code += start + "this." + camel_name + ";\n";
1693 break;
1694 }
1695 }
1696 }
1697 code += " }\n";
1698 // Pack()
1699 code += " public static " + GenOffsetType(struct_def) +
1700 " Pack(FlatBufferBuilder builder, " + struct_name + " _o) {\n";
1701 code += " if (_o == null) return default(" + GenOffsetType(struct_def) +
1702 ");\n";
1703 for (auto it = struct_def.fields.vec.begin();
1704 it != struct_def.fields.vec.end(); ++it) {
1705 auto &field = **it;
1706 if (field.deprecated) continue;
1707 auto camel_name = Name(field);
1708 // pre
1709 switch (field.value.type.base_type) {
1710 case BASE_TYPE_STRUCT: {
1711 if (!field.value.type.struct_def->fixed) {
1712 code += " var _" + field.name + " = _o." + camel_name +
1713 " == null ? default(" +
1714 GenOffsetType(*field.value.type.struct_def) +
1715 ") : " + GenTypeGet(field.value.type) +
1716 ".Pack(builder, _o." + camel_name + ");\n";
1717 } else if (struct_def.fixed && struct_has_create) {
1718 std::vector<FieldArrayLength> array_lengths;
1719 FieldArrayLength tmp_array_length = {
1720 field.name,
1721 field.value.type.fixed_length,
1722 };
1723 array_lengths.push_back(tmp_array_length);
1724 GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1725 array_lengths);
1726 }
1727 break;
1728 }
1729 case BASE_TYPE_STRING: {
1730 std::string create_string =
1731 field.shared ? "CreateSharedString" : "CreateString";
1732 code += " var _" + field.name + " = _o." + camel_name +
1733 " == null ? default(StringOffset) : "
1734 "builder." +
1735 create_string + "(_o." + camel_name + ");\n";
1736 break;
1737 }
1738 case BASE_TYPE_VECTOR: {
1739 if (field_has_create.find(&field) != field_has_create.end()) {
1740 auto property_name = camel_name;
1741 auto gen_for_loop = true;
1742 std::string array_name = "__" + field.name;
1743 std::string array_type = "";
1744 std::string to_array = "";
1745 switch (field.value.type.element) {
1746 case BASE_TYPE_STRING: {
1747 std::string create_string =
1748 field.shared ? "CreateSharedString" : "CreateString";
1749 array_type = "StringOffset";
1750 to_array += "builder." + create_string + "(_o." +
1751 property_name + "[_j])";
1752 break;
1753 }
1754 case BASE_TYPE_STRUCT:
1755 array_type = "Offset<" + GenTypeGet(field.value.type) + ">";
1756 to_array = GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1757 property_name + "[_j])";
1758 break;
1759 case BASE_TYPE_UTYPE:
1760 property_name = camel_name.substr(0, camel_name.size() - 4);
1761 array_type = NamespacedName(*field.value.type.enum_def);
1762 to_array = "_o." + property_name + "[_j].Type";
1763 break;
1764 case BASE_TYPE_UNION:
1765 array_type = "int";
1766 to_array = NamespacedName(*field.value.type.enum_def) +
1767 "Union.Pack(builder, _o." + property_name + "[_j])";
1768 break;
1769 default: gen_for_loop = false; break;
1770 }
1771 code += " var _" + field.name + " = default(VectorOffset);\n";
1772 code += " if (_o." + property_name + " != null) {\n";
1773 if (gen_for_loop) {
1774 code += " var " + array_name + " = new " + array_type +
1775 "[_o." + property_name + ".Count];\n";
1776 code += " for (var _j = 0; _j < " + array_name +
1777 ".Length; ++_j) { ";
1778 code += array_name + "[_j] = " + to_array + "; }\n";
1779 } else {
1780 code += " var " + array_name + " = _o." + property_name +
1781 ".ToArray();\n";
1782 }
1783 code += " _" + field.name + " = Create" + camel_name +
1784 "Vector(builder, " + array_name + ");\n";
1785 code += " }\n";
1786 } else {
1787 auto pack_method =
1788 field.value.type.struct_def == nullptr
1789 ? "builder.Add" + GenMethod(field.value.type.VectorType()) +
1790 "(_o." + camel_name + "[_j]);"
1791 : GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1792 camel_name + "[_j]);";
1793 code += " var _" + field.name + " = default(VectorOffset);\n";
1794 code += " if (_o." + camel_name + " != null) {\n";
1795 code += " Start" + camel_name + "Vector(builder, _o." +
1796 camel_name + ".Count);\n";
1797 code += " for (var _j = _o." + camel_name +
1798 ".Count - 1; _j >= 0; --_j) { " + pack_method + " }\n";
1799 code += " _" + field.name + " = builder.EndVector();\n";
1800 code += " }\n";
1801 }
1802 break;
1803 }
1804 case BASE_TYPE_ARRAY: {
1805 if (field.value.type.struct_def != nullptr) {
1806 std::vector<FieldArrayLength> array_lengths;
1807 FieldArrayLength tmp_array_length = {
1808 field.name,
1809 field.value.type.fixed_length,
1810 };
1811 array_lengths.push_back(tmp_array_length);
1812 GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1813 array_lengths);
1814 } else {
1815 code += " var _" + field.name + " = _o." + camel_name + ";\n";
1816 }
1817 break;
1818 }
1819 case BASE_TYPE_UNION: {
1820 code += " var _" + field.name + "_type = _o." + camel_name +
1821 " == null ? " + NamespacedName(*field.value.type.enum_def) +
1822 ".NONE : " + "_o." + camel_name + ".Type;\n";
1823 code +=
1824 " var _" + field.name + " = _o." + camel_name +
1825 " == null ? 0 : " + GenTypeGet_ObjectAPI(field.value.type, opts) +
1826 ".Pack(builder, _o." + camel_name + ");\n";
1827 break;
1828 }
1829 default: break;
1830 }
1831 }
1832 if (struct_has_create) {
1833 // Create
1834 code += " return Create" + struct_def.name + "(\n";
1835 code += " builder";
1836 for (auto it = struct_def.fields.vec.begin();
1837 it != struct_def.fields.vec.end(); ++it) {
1838 auto &field = **it;
1839 if (field.deprecated) continue;
1840 auto camel_name = Name(field);
1841 switch (field.value.type.base_type) {
1842 case BASE_TYPE_STRUCT: {
1843 if (struct_def.fixed) {
1844 GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1845 code_ptr,
1846 " _" + field.name + "_");
1847 } else {
1848 code += ",\n";
1849 if (field.value.type.struct_def->fixed) {
1850 if (opts.generate_object_based_api)
1851 code += " _o." + camel_name;
1852 else
1853 code += " " + GenTypeGet(field.value.type) +
1854 ".Pack(builder, _o." + camel_name + ")";
1855 } else {
1856 code += " _" + field.name;
1857 }
1858 }
1859 break;
1860 }
1861 case BASE_TYPE_ARRAY: {
1862 if (field.value.type.struct_def != nullptr) {
1863 GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1864 code_ptr,
1865 " _" + field.name + "_");
1866 } else {
1867 code += ",\n";
1868 code += " _" + field.name;
1869 }
1870 break;
1871 }
1872 case BASE_TYPE_UNION: FLATBUFFERS_FALLTHROUGH(); // fall thru
1873 case BASE_TYPE_UTYPE: FLATBUFFERS_FALLTHROUGH(); // fall thru
1874 case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH(); // fall thru
1875 case BASE_TYPE_VECTOR: {
1876 code += ",\n";
1877 code += " _" + field.name;
1878 break;
1879 }
1880 default: // scalar
1881 code += ",\n";
1882 code += " _o." + camel_name;
1883 break;
1884 }
1885 }
1886 code += ");\n";
1887 } else {
1888 // Start, End
1889 code += " Start" + struct_def.name + "(builder);\n";
1890 for (auto it = struct_def.fields.vec.begin();
1891 it != struct_def.fields.vec.end(); ++it) {
1892 auto &field = **it;
1893 if (field.deprecated) continue;
1894 auto camel_name = Name(field);
1895 switch (field.value.type.base_type) {
1896 case BASE_TYPE_STRUCT: {
1897 if (field.value.type.struct_def->fixed) {
1898 code += " Add" + camel_name + "(builder, " +
1899 GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1900 camel_name + "));\n";
1901 } else {
1902 code +=
1903 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1904 }
1905 break;
1906 }
1907 case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH(); // fall thru
1908 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
1909 case BASE_TYPE_VECTOR: {
1910 code +=
1911 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1912 break;
1913 }
1914 case BASE_TYPE_UTYPE: break;
1915 case BASE_TYPE_UNION: {
1916 code += " Add" + camel_name + "Type(builder, _" + field.name +
1917 "_type);\n";
1918 code +=
1919 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1920 break;
1921 }
1922 // scalar
1923 default: {
1924 code +=
1925 " Add" + camel_name + "(builder, _o." + camel_name + ");\n";
1926 break;
1927 }
1928 }
1929 }
1930 code += " return End" + struct_def.name + "(builder);\n";
1931 }
1932 code += " }\n";
1933 }
1934
1935 void GenStructPackDecl_ObjectAPI(
1936 const StructDef &struct_def, std::string *code_ptr,
1937 std::vector<FieldArrayLength> &array_lengths) const {
1938 auto &code = *code_ptr;
1939 for (auto it = struct_def.fields.vec.begin();
1940 it != struct_def.fields.vec.end(); ++it) {
1941 auto &field = **it;
1942 auto is_array = IsArray(field.value.type);
1943 const auto &field_type =
1944 is_array ? field.value.type.VectorType() : field.value.type;
1945 FieldArrayLength tmp_array_length = {
1946 field.name,
1947 field_type.fixed_length,
1948 };
1949 array_lengths.push_back(tmp_array_length);
1950 if (field_type.struct_def != nullptr) {
1951 GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr,
1952 array_lengths);
1953 } else {
1954 std::vector<FieldArrayLength> array_only_lengths;
1955 for (size_t i = 0; i < array_lengths.size(); ++i) {
1956 if (array_lengths[i].length > 0) {
1957 array_only_lengths.push_back(array_lengths[i]);
1958 }
1959 }
1960 std::string name;
1961 for (size_t i = 0; i < array_lengths.size(); ++i) {
1962 name += "_" + array_lengths[i].name;
1963 }
1964 code += " var " + name + " = ";
1965 if (array_only_lengths.size() > 0) {
1966 code += "new " + GenTypeBasic(field_type) + "[";
1967 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1968 if (i != 0) { code += ","; }
1969 code += NumToString(array_only_lengths[i].length);
1970 }
1971 code += "];\n";
1972 code += " ";
1973 // initialize array
1974 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1975 auto idx = "idx" + NumToString(i);
1976 code += "for (var " + idx + " = 0; " + idx + " < " +
1977 NumToString(array_only_lengths[i].length) + "; ++" + idx +
1978 ") {";
1979 }
1980 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1981 auto idx = "idx" + NumToString(i);
1982 if (i == 0) {
1983 code += name + "[" + idx;
1984 } else {
1985 code += "," + idx;
1986 }
1987 }
1988 code += "] = _o";
1989 for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) {
1990 code += "." + MakeCamel(array_lengths[i].name, true);
1991 if (array_lengths[i].length <= 0) continue;
1992 code += "[idx" + NumToString(j++) + "]";
1993 }
1994 code += ";";
1995 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1996 code += "}";
1997 }
1998 } else {
1999 code += "_o";
2000 for (size_t i = 0; i < array_lengths.size(); ++i) {
2001 code += "." + MakeCamel(array_lengths[i].name, true);
2002 }
2003 code += ";";
2004 }
2005 code += "\n";
2006 }
2007 array_lengths.pop_back();
2008 }
2009 }
2010
2011 void GenStructPackCall_ObjectAPI(const StructDef &struct_def,
2012 std::string *code_ptr,
2013 std::string prefix) const {
2014 auto &code = *code_ptr;
2015 for (auto it = struct_def.fields.vec.begin();
2016 it != struct_def.fields.vec.end(); ++it) {
2017 auto &field = **it;
2018 const auto &field_type = field.value.type;
2019 if (field_type.struct_def != nullptr) {
2020 GenStructPackCall_ObjectAPI(*field_type.struct_def, code_ptr,
2021 prefix + field.name + "_");
2022 } else {
2023 code += ",\n";
2024 code += prefix + field.name;
2025 }
2026 }
2027 }
2028
2029 std::string GenTypeGet_ObjectAPI(flatbuffers::Type type,
2030 const IDLOptions &opts) const {
2031 auto type_name = GenTypeGet(type);
2032 // Replace to ObjectBaseAPI Type Name
2033 switch (type.base_type) {
2034 case BASE_TYPE_STRUCT: FLATBUFFERS_FALLTHROUGH(); // fall thru
2035 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
2036 case BASE_TYPE_VECTOR: {
2037 if (type.struct_def != nullptr) {
2038 auto type_name_length = type.struct_def->name.length();
2039 auto new_type_name =
2040 GenTypeName_ObjectAPI(type.struct_def->name, opts);
2041 type_name.replace(type_name.length() - type_name_length,
2042 type_name_length, new_type_name);
2043 } else if (type.element == BASE_TYPE_UNION) {
2044 type_name = NamespacedName(*type.enum_def) + "Union";
2045 }
2046 break;
2047 }
2048
2049 case BASE_TYPE_UNION: {
2050 type_name = NamespacedName(*type.enum_def) + "Union";
2051 break;
2052 }
2053 default: break;
2054 }
2055
2056 switch (type.base_type) {
2057 case BASE_TYPE_ARRAY: {
2058 type_name = type_name + "[]";
2059 break;
2060 }
2061 case BASE_TYPE_VECTOR: {
2062 type_name = "List<" + type_name + ">";
2063 break;
2064 }
2065 default: break;
2066 }
2067 return type_name;
2068 }
2069
2070 void GenStruct_ObjectAPI(StructDef &struct_def, std::string *code_ptr,
2071 const IDLOptions &opts) const {
2072 auto &code = *code_ptr;
2073 if (struct_def.attributes.Lookup("private")) {
2074 code += "internal ";
2075 } else {
2076 code += "public ";
2077 }
2078 if (struct_def.attributes.Lookup("csharp_partial")) {
2079 // generate a partial class for this C# struct/table
2080 code += "partial ";
2081 }
2082 auto class_name = GenTypeName_ObjectAPI(struct_def.name, opts);
2083 code += "class " + class_name;
2084 code += "\n{\n";
2085 // Generate Properties
2086 for (auto it = struct_def.fields.vec.begin();
2087 it != struct_def.fields.vec.end(); ++it) {
2088 auto &field = **it;
2089 if (field.deprecated) continue;
2090 if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2091 if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2092 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2093 if (field.IsScalarOptional()) type_name += "?";
2094 auto camel_name = Name(field);
2095 if (opts.cs_gen_json_serializer) {
2096 if (IsUnion(field.value.type)) {
2097 auto utype_name = NamespacedName(*field.value.type.enum_def);
2098 code +=
2099 " [Newtonsoft.Json.JsonProperty(\"" + field.name + "_type\")]\n";
2100 if (IsVector(field.value.type)) {
2101 code += " private " + utype_name + "[] " + camel_name + "Type {\n";
2102 code += " get {\n";
2103 code += " if (this." + camel_name + " == null) return null;\n";
2104 code += " var _o = new " + utype_name + "[this." + camel_name +
2105 ".Count];\n";
2106 code +=
2107 " for (var _j = 0; _j < _o.Length; ++_j) { _o[_j] = "
2108 "this." +
2109 camel_name + "[_j].Type; }\n";
2110 code += " return _o;\n";
2111 code += " }\n";
2112 code += " set {\n";
2113 code += " this." + camel_name + " = new List<" + utype_name +
2114 "Union>();\n";
2115 code += " for (var _j = 0; _j < value.Length; ++_j) {\n";
2116 code += " var _o = new " + utype_name + "Union();\n";
2117 code += " _o.Type = value[_j];\n";
2118 code += " this." + camel_name + ".Add(_o);\n";
2119 code += " }\n";
2120 code += " }\n";
2121 code += " }\n";
2122 } else {
2123 code += " private " + utype_name + " " + camel_name + "Type {\n";
2124 code += " get {\n";
2125 code += " return this." + camel_name + " != null ? this." +
2126 camel_name + ".Type : " + utype_name + ".NONE;\n";
2127 code += " }\n";
2128 code += " set {\n";
2129 code += " this." + camel_name + " = new " + utype_name +
2130 "Union();\n";
2131 code += " this." + camel_name + ".Type = value;\n";
2132 code += " }\n";
2133 code += " }\n";
2134 }
2135 }
2136 code += " [Newtonsoft.Json.JsonProperty(\"" + field.name + "\")]\n";
2137 if (IsUnion(field.value.type)) {
2138 auto union_name =
2139 (IsVector(field.value.type))
2140 ? GenTypeGet_ObjectAPI(field.value.type.VectorType(), opts)
2141 : type_name;
2142 code += " [Newtonsoft.Json.JsonConverter(typeof(" + union_name +
2143 "_JsonConverter))]\n";
2144 }
2145 if (field.attributes.Lookup("hash")) {
2146 code += " [Newtonsoft.Json.JsonIgnore()]\n";
2147 }
2148 }
2149 code += " public " + type_name + " " + camel_name + " { get; set; }\n";
2150 }
2151 // Generate Constructor
2152 code += "\n";
2153 code += " public " + class_name + "() {\n";
2154 for (auto it = struct_def.fields.vec.begin();
2155 it != struct_def.fields.vec.end(); ++it) {
2156 auto &field = **it;
2157 if (field.deprecated) continue;
2158 if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2159 if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2160 code += " this." + Name(field) + " = ";
2161 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2162 if (IsScalar(field.value.type.base_type)) {
2163 code += GenDefaultValue(field) + ";\n";
2164 } else {
2165 switch (field.value.type.base_type) {
2166 case BASE_TYPE_STRUCT: {
2167 if (IsStruct(field.value.type)) {
2168 code += "new " + type_name + "();\n";
2169 } else {
2170 code += "null;\n";
2171 }
2172 break;
2173 }
2174 case BASE_TYPE_ARRAY: {
2175 code += "new " + type_name.substr(0, type_name.length() - 1) +
2176 NumToString(field.value.type.fixed_length) + "];\n";
2177 break;
2178 }
2179 default: {
2180 code += "null;\n";
2181 break;
2182 }
2183 }
2184 }
2185 }
2186 code += " }\n";
2187 // Generate Serialization
2188 if (opts.cs_gen_json_serializer &&
2189 parser_.root_struct_def_ == &struct_def) {
2190 code += "\n";
2191 code += " public static " + class_name +
2192 " DeserializeFromJson(string jsonText) {\n";
2193 code += " return Newtonsoft.Json.JsonConvert.DeserializeObject<" +
2194 class_name + ">(jsonText);\n";
2195 code += " }\n";
2196 code += " public string SerializeToJson() {\n";
2197 code +=
2198 " return Newtonsoft.Json.JsonConvert.SerializeObject(this, "
2199 "Newtonsoft.Json.Formatting.Indented);\n";
2200 code += " }\n";
2201 }
2202 if (parser_.root_struct_def_ == &struct_def) {
2203 code += " public static " + class_name +
2204 " DeserializeFromBinary(byte[] fbBuffer) {\n";
2205 code += " return " + struct_def.name + ".GetRootAs" + struct_def.name +
2206 "(new ByteBuffer(fbBuffer)).UnPack();\n";
2207 code += " }\n";
2208 code += " public byte[] SerializeToBinary() {\n";
2209 code += " var fbb = new FlatBufferBuilder(0x10000);\n";
2210 code += " " + struct_def.name + ".Finish" + struct_def.name +
2211 "Buffer(fbb, " + struct_def.name + ".Pack(fbb, this));\n";
2212 code += " return fbb.DataBuffer.ToSizedArray();\n";
2213 code += " }\n";
2214 }
2215 code += "}\n\n";
2216 }
2217
2218 // This tracks the current namespace used to determine if a type need to be
2219 // prefixed by its namespace
2220 const Namespace *cur_name_space_;
2221};
2222} // namespace csharp
2223
2224bool GenerateCSharp(const Parser &parser, const std::string &path,
2225 const std::string &file_name) {
2226 csharp::CSharpGenerator generator(parser, path, file_name);
2227 return generator.generate();
2228}
2229
2230} // namespace flatbuffers
2231