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
26 namespace flatbuffers {
27
28 static TypedFloatConstantGenerator CSharpFloatGen("Double.", "Single.", "NaN",
29 "PositiveInfinity",
30 "NegativeInfinity");
31 static CommentConfig comment_config = {
32 nullptr,
33 "///",
34 nullptr,
35 };
36
37 namespace csharp {
38 class CSharpGenerator : public BaseGenerator {
39 struct FieldArrayLength {
40 std::string name;
41 int length;
42 };
43
44 public:
CSharpGenerator(const Parser & parser,const std::string & path,const std::string & file_name)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
generate()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
EscapeKeyword(const std::string & name) const189 std::string EscapeKeyword(const std::string &name) const {
190 return keywords_.find(name) == keywords_.end() ? name : "@" + name;
191 }
192
Name(const FieldDef & field) const193 std::string Name(const FieldDef &field) const {
194 std::string name = ConvertCase(field.name, Case::kUpperCamel);
195 return EscapeKeyword(name);
196 }
197
Name(const Definition & def) const198 std::string Name(const Definition &def) const {
199 return EscapeKeyword(def.name);
200 }
201
NamespacedName(const Definition & def) const202 std::string NamespacedName(const Definition &def) const {
203 return WrapInNameSpace(def.defined_namespace, Name(def));
204 }
205
Name(const EnumVal & ev) const206 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.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes,const IDLOptions & options) const210 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
CurrentNameSpace() const240 const Namespace *CurrentNameSpace() const { return cur_name_space_; }
241
GenTypeBasic(const Type & type,bool enableLangOverrides) const242 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
GenTypeBasic(const Type & type) const262 inline std::string GenTypeBasic(const Type &type) const {
263 return GenTypeBasic(type, true);
264 }
265
GenTypePointer(const Type & type) const266 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
GenTypeGet(const Type & type) const276 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
GenOffsetType(const StructDef & struct_def) const283 std::string GenOffsetType(const StructDef &struct_def) const {
284 return "Offset<" + NamespacedName(struct_def) + ">";
285 }
286
GenOffsetConstruct(const StructDef & struct_def,const std::string & variable_name) const287 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
DestinationCast(const Type & type) const294 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.
SourceCast(const Type & type,const bool isOptional=false) const309 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
SourceCastBasic(const Type & type,const bool isOptional) const320 std::string SourceCastBasic(const Type &type, const bool isOptional) const {
321 return IsScalar(type.base_type) ? SourceCast(type, isOptional) : "";
322 }
323
GenEnumDefaultValue(const FieldDef & field) const324 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
GenDefaultValue(const FieldDef & field,bool enableLangOverrides) const333 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
GenDefaultValue(const FieldDef & field) const361 std::string GenDefaultValue(const FieldDef &field) const {
362 return GenDefaultValue(field, true);
363 }
364
GenDefaultValueBasic(const FieldDef & field,bool enableLangOverrides) const365 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
GenDefaultValueBasic(const FieldDef & field) const384 std::string GenDefaultValueBasic(const FieldDef &field) const {
385 return GenDefaultValueBasic(field, true);
386 }
387
GenEnum(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const388 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
HasUnionStringValue(const EnumDef & enum_def) const433 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.
GenGetter(const Type & type) const443 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 += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel);
456 }
457 return getter;
458 }
459 }
460 }
461
GetObjectConstructor(flatbuffers::StructDef & struct_def,const std::string & data_buffer,const std::string & offset) const462 std::string GetObjectConstructor(flatbuffers::StructDef &struct_def,
463 const std::string &data_buffer,
464 const std::string &offset) const {
465 // Use the generated type directly, to properly handle default values that
466 // might not be written to the buffer.
467 return "new " + Name(struct_def) + "().__assign(" + offset + ", " +
468 data_buffer + ")";
469 }
470
471 // Returns the function name that is able to read a value of the given type.
GenGetterForLookupByKey(flatbuffers::StructDef & struct_def,flatbuffers::FieldDef * key_field,const std::string & data_buffer,const std::string & offset) const472 std::string GenGetterForLookupByKey(flatbuffers::StructDef &struct_def,
473 flatbuffers::FieldDef *key_field,
474 const std::string &data_buffer,
475 const std::string &offset) const {
476 // Use the generated type directly, to properly handle default values that
477 // might not be written to the buffer.
478 return GetObjectConstructor(struct_def, data_buffer, offset) + "." +
479 Name(*key_field);
480 }
481
482 // Direct mutation is only allowed for scalar fields.
483 // Hence a setter method will only be generated for such fields.
GenSetter(const Type & type) const484 std::string GenSetter(const Type &type) const {
485 if (IsScalar(type.base_type)) {
486 std::string setter = "__p.bb.Put";
487 if (GenTypeBasic(type, false) != "byte" &&
488 type.base_type != BASE_TYPE_BOOL) {
489 setter += ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel);
490 }
491 return setter;
492 } else {
493 return "";
494 }
495 }
496
497 // Returns the method name for use with add/put calls.
GenMethod(const Type & type) const498 std::string GenMethod(const Type &type) const {
499 return IsScalar(type.base_type)
500 ? ConvertCase(GenTypeBasic(type, false), Case::kUpperCamel)
501 : (IsStruct(type) ? "Struct" : "Offset");
502 }
503
504 // Recursively generate arguments for a constructor, to deal with nested
505 // structs.
GenStructArgs(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t array_count=0) const506 void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
507 const char *nameprefix, size_t array_count = 0) const {
508 std::string &code = *code_ptr;
509 for (auto it = struct_def.fields.vec.begin();
510 it != struct_def.fields.vec.end(); ++it) {
511 auto &field = **it;
512 const auto &field_type = field.value.type;
513 const auto array_field = IsArray(field_type);
514 const auto &type = array_field ? field_type.VectorType() : field_type;
515 const auto array_cnt = array_field ? (array_count + 1) : array_count;
516 if (IsStruct(type)) {
517 // Generate arguments for a struct inside a struct. To ensure names
518 // don't clash, and to make it obvious these arguments are constructing
519 // a nested struct, prefix the name with the field name.
520 GenStructArgs(*field_type.struct_def, code_ptr,
521 (nameprefix + (EscapeKeyword(field.name) + "_")).c_str(),
522 array_cnt);
523 } else {
524 code += ", ";
525 code += GenTypeBasic(type);
526 if (field.IsScalarOptional()) { code += "?"; }
527 if (array_cnt > 0) {
528 code += "[";
529 for (size_t i = 1; i < array_cnt; i++) code += ",";
530 code += "]";
531 }
532 code += " ";
533 code += nameprefix;
534 code += Name(field);
535 }
536 }
537 }
538
539 // Recusively generate struct construction statements of the form:
540 // builder.putType(name);
541 // and insert manual padding.
GenStructBody(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t index=0,bool in_array=false) const542 void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
543 const char *nameprefix, size_t index = 0,
544 bool in_array = false) const {
545 std::string &code = *code_ptr;
546 std::string indent((index + 1) * 2, ' ');
547 code += indent + " builder.Prep(";
548 code += NumToString(struct_def.minalign) + ", ";
549 code += NumToString(struct_def.bytesize) + ");\n";
550 for (auto it = struct_def.fields.vec.rbegin();
551 it != struct_def.fields.vec.rend(); ++it) {
552 auto &field = **it;
553 const auto &field_type = field.value.type;
554 if (field.padding) {
555 code += indent + " builder.Pad(";
556 code += NumToString(field.padding) + ");\n";
557 }
558 if (IsStruct(field_type)) {
559 GenStructBody(*field_type.struct_def, code_ptr,
560 (nameprefix + (field.name + "_")).c_str(), index,
561 in_array);
562 } else {
563 const auto &type =
564 IsArray(field_type) ? field_type.VectorType() : field_type;
565 const auto index_var = "_idx" + NumToString(index);
566 if (IsArray(field_type)) {
567 code += indent + " for (int " + index_var + " = ";
568 code += NumToString(field_type.fixed_length);
569 code += "; " + index_var + " > 0; " + index_var + "--) {\n";
570 in_array = true;
571 }
572 if (IsStruct(type)) {
573 GenStructBody(*field_type.struct_def, code_ptr,
574 (nameprefix + (field.name + "_")).c_str(), index + 1,
575 in_array);
576 } else {
577 code += IsArray(field_type) ? " " : "";
578 code += indent + " builder.Put";
579 code += GenMethod(type) + "(";
580 code += SourceCast(type);
581 auto argname = nameprefix + Name(field);
582 code += argname;
583 size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
584 if (array_cnt > 0) {
585 code += "[";
586 for (size_t i = 0; in_array && i < array_cnt; i++) {
587 code += "_idx" + NumToString(i) + "-1";
588 if (i != (array_cnt - 1)) code += ",";
589 }
590 code += "]";
591 }
592 code += ");\n";
593 }
594 if (IsArray(field_type)) { code += indent + " }\n"; }
595 }
596 }
597 }
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const598 std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
599 const char *num = nullptr) const {
600 std::string key_offset =
601 "Table.__offset(" + NumToString(key_field->value.offset) + ", ";
602 if (num) {
603 key_offset += num;
604 key_offset += ".Value, builder.DataBuffer)";
605 } else {
606 key_offset += "bb.Length";
607 key_offset += " - tableOffset, bb)";
608 }
609 return key_offset;
610 }
611
GenKeyGetter(flatbuffers::StructDef & struct_def,flatbuffers::FieldDef * key_field) const612 std::string GenKeyGetter(flatbuffers::StructDef &struct_def,
613 flatbuffers::FieldDef *key_field) const {
614 // Get the getter for the key of the struct.
615 return GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
616 "builder.DataBuffer.Length - o1.Value") +
617 ".CompareTo(" +
618 GenGetterForLookupByKey(struct_def, key_field, "builder.DataBuffer",
619 "builder.DataBuffer.Length - o2.Value") +
620 ")";
621 }
622
GenStruct(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const623 void GenStruct(StructDef &struct_def, std::string *code_ptr,
624 const IDLOptions &opts) const {
625 if (struct_def.generated) return;
626 std::string &code = *code_ptr;
627
628 // Generate a struct accessor class, with methods of the form:
629 // public type name() { return bb.getType(i + offset); }
630 // or for tables of the form:
631 // public type name() {
632 // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
633 // }
634 GenComment(struct_def.doc_comment, code_ptr, &comment_config);
635 if (struct_def.attributes.Lookup("private")) {
636 code += "internal ";
637 } else {
638 code += "public ";
639 }
640 if (struct_def.attributes.Lookup("csharp_partial")) {
641 // generate a partial class for this C# struct/table
642 code += "partial ";
643 }
644 code += "struct " + struct_def.name;
645 code += " : IFlatbufferObject";
646 code += "\n{\n";
647 code += " private ";
648 code += struct_def.fixed ? "Struct" : "Table";
649 code += " __p;\n";
650
651 code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
652
653 if (!struct_def.fixed) {
654 // Generate verson check method.
655 // Force compile time error if not using the same version runtime.
656 code += " public static void ValidateVersion() {";
657 code += " FlatBufferConstants.";
658 code += "FLATBUFFERS_2_0_0(); ";
659 code += "}\n";
660
661 // Generate a special accessor for the table that when used as the root
662 // of a FlatBuffer
663 std::string method_name = "GetRootAs" + struct_def.name;
664 std::string method_signature =
665 " public static " + struct_def.name + " " + method_name;
666
667 // create convenience method that doesn't require an existing object
668 code += method_signature + "(ByteBuffer _bb) ";
669 code += "{ return " + method_name + "(_bb, new " + struct_def.name +
670 "()); }\n";
671
672 // create method that allows object reuse
673 code +=
674 method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
675 code += "return (obj.__assign(_bb.GetInt(_bb.Position";
676 code += ") + _bb.Position";
677 code += ", _bb)); }\n";
678 if (parser_.root_struct_def_ == &struct_def) {
679 if (parser_.file_identifier_.length()) {
680 // Check if a buffer has the identifier.
681 code += " public static ";
682 code += "bool " + struct_def.name;
683 code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
684 code += "Table.__has_identifier(_bb, \"";
685 code += parser_.file_identifier_;
686 code += "\"); }\n";
687 }
688 }
689 }
690 // Generate the __init method that sets the field in a pre-existing
691 // accessor object. This is to allow object reuse.
692 code += " public void __init(int _i, ByteBuffer _bb) ";
693 code += "{ ";
694 code += "__p = new ";
695 code += struct_def.fixed ? "Struct" : "Table";
696 code += "(_i, _bb); ";
697 code += "}\n";
698 code +=
699 " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
700 code += "{ __init(_i, _bb); return this; }\n\n";
701 for (auto it = struct_def.fields.vec.begin();
702 it != struct_def.fields.vec.end(); ++it) {
703 auto &field = **it;
704 if (field.deprecated) continue;
705 GenComment(field.doc_comment, code_ptr, &comment_config, " ");
706 std::string type_name = GenTypeGet(field.value.type);
707 std::string type_name_dest = GenTypeGet(field.value.type);
708 std::string conditional_cast = "";
709 std::string optional = "";
710 if (!struct_def.fixed &&
711 (field.value.type.base_type == BASE_TYPE_STRUCT ||
712 field.value.type.base_type == BASE_TYPE_UNION ||
713 (IsVector(field.value.type) &&
714 (field.value.type.element == BASE_TYPE_STRUCT ||
715 field.value.type.element == BASE_TYPE_UNION)))) {
716 optional = "?";
717 conditional_cast = "(" + type_name_dest + optional + ")";
718 }
719 if (field.IsScalarOptional()) { optional = "?"; }
720 std::string dest_mask = "";
721 std::string dest_cast = DestinationCast(field.value.type);
722 std::string src_cast = SourceCast(field.value.type);
723 std::string field_name_camel = Name(field);
724 if (field_name_camel == struct_def.name) { field_name_camel += "_"; }
725 std::string method_start =
726 " public " + type_name_dest + optional + " " + field_name_camel;
727 std::string obj = "(new " + type_name + "())";
728
729 // Most field accessors need to retrieve and test the field offset first,
730 // this is the prefix code for that:
731 auto offset_prefix =
732 IsArray(field.value.type)
733 ? " { return "
734 : (" { int o = __p.__offset(" + NumToString(field.value.offset) +
735 "); return o != 0 ? ");
736 // Generate the accessors that don't do object reuse.
737 if (field.value.type.base_type == BASE_TYPE_STRUCT) {
738 } else if (IsVector(field.value.type) &&
739 field.value.type.element == BASE_TYPE_STRUCT) {
740 } else if (field.value.type.base_type == BASE_TYPE_UNION ||
741 (IsVector(field.value.type) &&
742 field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
743 method_start += "<TTable>";
744 type_name = type_name_dest;
745 }
746 std::string getter = dest_cast + GenGetter(field.value.type);
747 code += method_start;
748 std::string default_cast = "";
749 // only create default casts for c# scalars or vectors of scalars
750 if ((IsScalar(field.value.type.base_type) ||
751 (IsVector(field.value.type) &&
752 IsScalar(field.value.type.element)))) {
753 // For scalars, default value will be returned by GetDefaultValue().
754 // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
755 // that doesn't need to be casted. However, default values for enum
756 // elements of vectors are integer literals ("0") and are still casted
757 // for clarity.
758 // If the scalar is optional and enum, we still need the cast.
759 if ((field.value.type.enum_def == nullptr ||
760 IsVector(field.value.type)) ||
761 (IsEnum(field.value.type) && field.IsScalarOptional())) {
762 default_cast = "(" + type_name_dest + optional + ")";
763 }
764 }
765 std::string member_suffix = "; ";
766 if (IsScalar(field.value.type.base_type)) {
767 code += " { get";
768 member_suffix += "} ";
769 if (struct_def.fixed) {
770 code += " { return " + getter;
771 code += "(__p.bb_pos + ";
772 code += NumToString(field.value.offset) + ")";
773 code += dest_mask;
774 } else {
775 code += offset_prefix + getter;
776 code += "(o + __p.bb_pos)" + dest_mask;
777 code += " : " + default_cast;
778 code += GenDefaultValue(field);
779 }
780 } else {
781 switch (field.value.type.base_type) {
782 case BASE_TYPE_STRUCT:
783 code += " { get";
784 member_suffix += "} ";
785 if (struct_def.fixed) {
786 code += " { return " + obj + ".__assign(" + "__p.";
787 code += "bb_pos + " + NumToString(field.value.offset) + ", ";
788 code += "__p.bb)";
789 } else {
790 code += offset_prefix + conditional_cast;
791 code += obj + ".__assign(";
792 code += field.value.type.struct_def->fixed
793 ? "o + __p.bb_pos"
794 : "__p.__indirect(o + __p.bb_pos)";
795 code += ", __p.bb) : null";
796 }
797 break;
798 case BASE_TYPE_STRING:
799 code += " { get";
800 member_suffix += "} ";
801 code += offset_prefix + getter + "(o + " + "__p.";
802 code += "bb_pos) : null";
803 break;
804 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
805 case BASE_TYPE_VECTOR: {
806 auto vectortype = field.value.type.VectorType();
807 if (vectortype.base_type == BASE_TYPE_UNION) {
808 conditional_cast = "(TTable?)";
809 getter += "<TTable>";
810 }
811 code += "(";
812 if (vectortype.base_type == BASE_TYPE_STRUCT) {
813 getter = obj + ".__assign";
814 } else if (vectortype.base_type == BASE_TYPE_UNION) {
815 }
816 code += "int j)";
817 const auto body = offset_prefix + conditional_cast + getter + "(";
818 if (vectortype.base_type == BASE_TYPE_UNION) {
819 code += " where TTable : struct, IFlatbufferObject" + body;
820 } else {
821 code += body;
822 }
823 std::string index = "__p.";
824 if (IsArray(field.value.type)) {
825 index += "bb_pos + " + NumToString(field.value.offset) + " + ";
826 } else {
827 index += "__vector(o) + ";
828 }
829 index += "j * " + NumToString(InlineSize(vectortype));
830 if (vectortype.base_type == BASE_TYPE_STRUCT) {
831 code += vectortype.struct_def->fixed
832 ? index
833 : "__p.__indirect(" + index + ")";
834 code += ", __p.bb";
835 } else {
836 code += index;
837 }
838 code += ")" + dest_mask;
839 if (!IsArray(field.value.type)) {
840 code += " : ";
841 code +=
842 field.value.type.element == BASE_TYPE_BOOL
843 ? "false"
844 : (IsScalar(field.value.type.element) ? default_cast + "0"
845 : "null");
846 }
847 if (vectortype.base_type == BASE_TYPE_UNION &&
848 HasUnionStringValue(*vectortype.enum_def)) {
849 code += member_suffix;
850 code += "}\n";
851 code += " public string " + Name(field) + "AsString(int j)";
852 code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
853 code += "(" + index + ") : null";
854 }
855 break;
856 }
857 case BASE_TYPE_UNION:
858 code += "() where TTable : struct, IFlatbufferObject";
859 code += offset_prefix + "(TTable?)" + getter;
860 code += "<TTable>(o + __p.bb_pos) : null";
861 if (HasUnionStringValue(*field.value.type.enum_def)) {
862 code += member_suffix;
863 code += "}\n";
864 code += " public string " + Name(field) + "AsString()";
865 code += offset_prefix + GenGetter(Type(BASE_TYPE_STRING));
866 code += "(o + __p.bb_pos) : null";
867 }
868 // As<> accesors for Unions
869 // Loop through all the possible union types and generate an As
870 // accessor that casts to the correct type.
871 for (auto uit = field.value.type.enum_def->Vals().begin();
872 uit != field.value.type.enum_def->Vals().end(); ++uit) {
873 auto val = *uit;
874 if (val->union_type.base_type == BASE_TYPE_NONE) { continue; }
875 auto union_field_type_name = GenTypeGet(val->union_type);
876 code += member_suffix + "}\n";
877 if (val->union_type.base_type == BASE_TYPE_STRUCT &&
878 val->union_type.struct_def->attributes.Lookup("private")) {
879 code += " internal ";
880 } else {
881 code += " public ";
882 }
883 code += union_field_type_name + " ";
884 code += field_name_camel + "As" + val->name + "() { return ";
885 code += field_name_camel;
886 if (IsString(val->union_type)) {
887 code += "AsString()";
888 } else {
889 code += "<" + union_field_type_name + ">().Value";
890 }
891 }
892 break;
893 default: FLATBUFFERS_ASSERT(0);
894 }
895 }
896 code += member_suffix;
897 code += "}\n";
898 if (IsVector(field.value.type)) {
899 auto camel_name = Name(field);
900 if (camel_name == struct_def.name) { camel_name += "_"; }
901 code += " public int " + camel_name;
902 code += "Length";
903 code += " { get";
904 code += offset_prefix;
905 code += "__p.__vector_len(o) : 0; ";
906 code += "} ";
907 code += "}\n";
908 // See if we should generate a by-key accessor.
909 if (field.value.type.element == BASE_TYPE_STRUCT &&
910 !field.value.type.struct_def->fixed) {
911 auto &sd = *field.value.type.struct_def;
912 auto &fields = sd.fields.vec;
913 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
914 auto &key_field = **kit;
915 if (key_field.key) {
916 auto qualified_name = NamespacedName(sd);
917 code += " public " + qualified_name + "? ";
918 code += Name(field) + "ByKey(";
919 code += GenTypeGet(key_field.value.type) + " key)";
920 code += offset_prefix;
921 code += qualified_name + ".__lookup_by_key(";
922 code += "__p.__vector(o), key, ";
923 code += "__p.bb) : null; ";
924 code += "}\n";
925 break;
926 }
927 }
928 }
929 }
930 // Generate a ByteBuffer accessor for strings & vectors of scalars.
931 if ((IsVector(field.value.type) &&
932 IsScalar(field.value.type.VectorType().base_type)) ||
933 IsString(field.value.type)) {
934 code += "#if ENABLE_SPAN_T\n";
935 code += " public Span<" + GenTypeBasic(field.value.type.VectorType()) +
936 "> Get";
937 code += Name(field);
938 code += "Bytes() { return ";
939 code += "__p.__vector_as_span<" +
940 GenTypeBasic(field.value.type.VectorType()) + ">(";
941 code += NumToString(field.value.offset);
942 code +=
943 ", " + NumToString(SizeOf(field.value.type.VectorType().base_type));
944 code += "); }\n";
945 code += "#else\n";
946 code += " public ArraySegment<byte>? Get";
947 code += Name(field);
948 code += "Bytes() { return ";
949 code += "__p.__vector_as_arraysegment(";
950 code += NumToString(field.value.offset);
951 code += "); }\n";
952 code += "#endif\n";
953
954 // For direct blockcopying the data into a typed array
955 code += " public ";
956 code += GenTypeBasic(field.value.type.VectorType());
957 code += "[] Get";
958 code += Name(field);
959 code += "Array() { ";
960 if (IsEnum(field.value.type.VectorType())) {
961 // Since __vector_as_array does not work for enum types,
962 // fill array using an explicit loop.
963 code += "int o = __p.__offset(";
964 code += NumToString(field.value.offset);
965 code += "); if (o == 0) return null; int p = ";
966 code += "__p.__vector(o); int l = ";
967 code += "__p.__vector_len(o); ";
968 code += GenTypeBasic(field.value.type.VectorType());
969 code += "[] a = new ";
970 code += GenTypeBasic(field.value.type.VectorType());
971 code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
972 code += "(p + i * ";
973 code += NumToString(InlineSize(field.value.type.VectorType()));
974 code += "); } return a;";
975 } else {
976 code += "return ";
977 code += "__p.__vector_as_array<";
978 code += GenTypeBasic(field.value.type.VectorType());
979 code += ">(";
980 code += NumToString(field.value.offset);
981 code += ");";
982 }
983 code += " }\n";
984 }
985 // generate object accessors if is nested_flatbuffer
986 if (field.nested_flatbuffer) {
987 auto nested_type_name = NamespacedName(*field.nested_flatbuffer);
988 auto nested_method_name =
989 Name(field) + "As" + field.nested_flatbuffer->name;
990 auto get_nested_method_name = nested_method_name;
991 get_nested_method_name = "Get" + nested_method_name;
992 conditional_cast = "(" + nested_type_name + "?)";
993 obj = "(new " + nested_type_name + "())";
994 code += " public " + nested_type_name + "? ";
995 code += get_nested_method_name + "(";
996 code += ") { int o = __p.__offset(";
997 code += NumToString(field.value.offset) + "); ";
998 code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
999 code += "__p.";
1000 code += "__indirect(__p.__vector(o)), ";
1001 code += "__p.bb) : null; }\n";
1002 }
1003 // Generate mutators for scalar fields or vectors of scalars.
1004 if (parser_.opts.mutable_buffer) {
1005 auto is_series = (IsSeries(field.value.type));
1006 const auto &underlying_type =
1007 is_series ? field.value.type.VectorType() : field.value.type;
1008 // Boolean parameters have to be explicitly converted to byte
1009 // representation.
1010 auto setter_parameter =
1011 underlying_type.base_type == BASE_TYPE_BOOL
1012 ? "(byte)(" + EscapeKeyword(field.name) + " ? 1 : 0)"
1013 : EscapeKeyword(field.name);
1014 auto mutator_prefix = "Mutate";
1015 // A vector mutator also needs the index of the vector element it should
1016 // mutate.
1017 auto mutator_params = (is_series ? "(int j, " : "(") +
1018 GenTypeGet(underlying_type) + " " +
1019 EscapeKeyword(field.name) + ") { ";
1020 auto setter_index =
1021 is_series
1022 ? "__p." +
1023 (IsArray(field.value.type)
1024 ? "bb_pos + " + NumToString(field.value.offset)
1025 : "__vector(o)") +
1026 +" + j * " + NumToString(InlineSize(underlying_type))
1027 : (struct_def.fixed
1028 ? "__p.bb_pos + " + NumToString(field.value.offset)
1029 : "o + __p.bb_pos");
1030 if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
1031 code += " public ";
1032 code += struct_def.fixed ? "void " : "bool ";
1033 code += mutator_prefix + Name(field);
1034 code += mutator_params;
1035 if (struct_def.fixed) {
1036 code += GenSetter(underlying_type) + "(" + setter_index + ", ";
1037 code += src_cast + setter_parameter + "); }\n";
1038 } else {
1039 code += "int o = __p.__offset(";
1040 code += NumToString(field.value.offset) + ");";
1041 code += " if (o != 0) { " + GenSetter(underlying_type);
1042 code += "(" + setter_index + ", " + src_cast + setter_parameter +
1043 "); return true; } else { return false; } }\n";
1044 }
1045 }
1046 }
1047 if (parser_.opts.java_primitive_has_method &&
1048 IsScalar(field.value.type.base_type) && !struct_def.fixed) {
1049 auto vt_offset_constant =
1050 " public static final int VT_" +
1051 ConvertCase(field.name, Case::kScreamingSnake) + " = " +
1052 NumToString(field.value.offset) + ";";
1053
1054 code += vt_offset_constant;
1055 code += "\n";
1056 }
1057 }
1058 code += "\n";
1059 auto struct_has_create = false;
1060 std::set<flatbuffers::FieldDef *> field_has_create_set;
1061 flatbuffers::FieldDef *key_field = nullptr;
1062 if (struct_def.fixed) {
1063 struct_has_create = true;
1064 // create a struct constructor function
1065 code += " public static " + GenOffsetType(struct_def) + " ";
1066 code += "Create";
1067 code += struct_def.name + "(FlatBufferBuilder builder";
1068 GenStructArgs(struct_def, code_ptr, "");
1069 code += ") {\n";
1070 GenStructBody(struct_def, code_ptr, "");
1071 code += " return ";
1072 code += GenOffsetConstruct(struct_def, "builder.Offset");
1073 code += ";\n }\n";
1074 } else {
1075 // Generate a method that creates a table in one go. This is only possible
1076 // when the table has no struct fields, since those have to be created
1077 // inline, and there's no way to do so in Java.
1078 bool has_no_struct_fields = true;
1079 int num_fields = 0;
1080 for (auto it = struct_def.fields.vec.begin();
1081 it != struct_def.fields.vec.end(); ++it) {
1082 auto &field = **it;
1083 if (field.deprecated) continue;
1084 if (IsStruct(field.value.type)) {
1085 has_no_struct_fields = false;
1086 } else {
1087 num_fields++;
1088 }
1089 }
1090 // JVM specifications restrict default constructor params to be < 255.
1091 // Longs and doubles take up 2 units, so we set the limit to be < 127.
1092 if ((has_no_struct_fields || opts.generate_object_based_api) &&
1093 num_fields && num_fields < 127) {
1094 struct_has_create = true;
1095 // Generate a table constructor of the form:
1096 // public static int createName(FlatBufferBuilder builder, args...)
1097 code += " public static " + GenOffsetType(struct_def) + " ";
1098 code += "Create" + struct_def.name;
1099 code += "(FlatBufferBuilder builder";
1100 for (auto it = struct_def.fields.vec.begin();
1101 it != struct_def.fields.vec.end(); ++it) {
1102 auto &field = **it;
1103 if (field.deprecated) continue;
1104 code += ",\n ";
1105 if (IsStruct(field.value.type) && opts.generate_object_based_api) {
1106 code += WrapInNameSpace(
1107 field.value.type.struct_def->defined_namespace,
1108 GenTypeName_ObjectAPI(field.value.type.struct_def->name, opts));
1109 code += " ";
1110 code += EscapeKeyword(field.name);
1111 code += " = null";
1112 } else {
1113 code += GenTypeBasic(field.value.type);
1114 if (field.IsScalarOptional()) { code += "?"; }
1115 code += " ";
1116 code += EscapeKeyword(field.name);
1117 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1118
1119 code += " = ";
1120 code += GenDefaultValueBasic(field);
1121 }
1122 }
1123 code += ") {\n builder.";
1124 code += "StartTable(";
1125 code += NumToString(struct_def.fields.vec.size()) + ");\n";
1126 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1127 size; size /= 2) {
1128 for (auto it = struct_def.fields.vec.rbegin();
1129 it != struct_def.fields.vec.rend(); ++it) {
1130 auto &field = **it;
1131 if (!field.deprecated &&
1132 (!struct_def.sortbysize ||
1133 size == SizeOf(field.value.type.base_type))) {
1134 code += " " + struct_def.name + ".";
1135 code += "Add";
1136 code += Name(field) + "(builder, ";
1137 if (IsStruct(field.value.type) &&
1138 opts.generate_object_based_api) {
1139 code += GenTypePointer(field.value.type) + ".Pack(builder, " +
1140 EscapeKeyword(field.name) + ")";
1141 } else {
1142 code += EscapeKeyword(field.name);
1143 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1144 }
1145
1146 code += ");\n";
1147 }
1148 }
1149 }
1150 code += " return " + struct_def.name + ".";
1151 code += "End" + struct_def.name;
1152 code += "(builder);\n }\n\n";
1153 }
1154 // Generate a set of static methods that allow table construction,
1155 // of the form:
1156 // public static void addName(FlatBufferBuilder builder, short name)
1157 // { builder.addShort(id, name, default); }
1158 // Unlike the Create function, these always work.
1159 code += " public static void Start";
1160 code += struct_def.name;
1161 code += "(FlatBufferBuilder builder) { builder.";
1162 code += "StartTable(";
1163 code += NumToString(struct_def.fields.vec.size()) + "); }\n";
1164 for (auto it = struct_def.fields.vec.begin();
1165 it != struct_def.fields.vec.end(); ++it) {
1166 auto &field = **it;
1167 if (field.deprecated) continue;
1168 if (field.key) key_field = &field;
1169 code += " public static void Add";
1170 code += Name(field);
1171 code += "(FlatBufferBuilder builder, ";
1172 code += GenTypeBasic(field.value.type);
1173 auto argname = ConvertCase(field.name, Case::kLowerCamel);
1174 if (!IsScalar(field.value.type.base_type)) argname += "Offset";
1175 if (field.IsScalarOptional()) { code += "?"; }
1176 code += " " + EscapeKeyword(argname) + ") { builder.Add";
1177 code += GenMethod(field.value.type) + "(";
1178 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1179 code += SourceCastBasic(field.value.type, field.IsScalarOptional());
1180 code += EscapeKeyword(argname);
1181 if (!IsScalar(field.value.type.base_type) &&
1182 field.value.type.base_type != BASE_TYPE_UNION) {
1183 code += ".Value";
1184 }
1185 if (!field.IsScalarOptional()) {
1186 // When the scalar is optional, use the builder method that doesn't
1187 // supply a default value. Otherwise, we to continue to use the
1188 // default value method.
1189 code += ", ";
1190 code += GenDefaultValue(field, false);
1191 }
1192 code += "); }\n";
1193 if (IsVector(field.value.type)) {
1194 auto vector_type = field.value.type.VectorType();
1195 auto alignment = InlineAlignment(vector_type);
1196 auto elem_size = InlineSize(vector_type);
1197 if (!IsStruct(vector_type)) {
1198 field_has_create_set.insert(&field);
1199 code += " public static VectorOffset ";
1200 code += "Create";
1201 code += Name(field);
1202 code += "Vector(FlatBufferBuilder builder, ";
1203 code += GenTypeBasic(vector_type) + "[] data) ";
1204 code += "{ builder.StartVector(";
1205 code += NumToString(elem_size);
1206 code += ", data.Length, ";
1207 code += NumToString(alignment);
1208 code += "); for (int i = data.";
1209 code += "Length - 1; i >= 0; i--) builder.";
1210 code += "Add";
1211 code += GenMethod(vector_type);
1212 code += "(";
1213 // At the moment there is no support of the type Vector with
1214 // optional enum, e.g. if we have enum type SomeEnum there is no way
1215 // to define `SomeEmum?[] enums` in FlatBuffer schema, so isOptional
1216 // = false
1217 code += SourceCastBasic(vector_type, false);
1218 code += "data[i]";
1219 if (vector_type.base_type == BASE_TYPE_STRUCT ||
1220 IsString(vector_type))
1221 code += ".Value";
1222 code += "); return ";
1223 code += "builder.EndVector(); }\n";
1224
1225 // add Create...VectorBlock() overloads for T[], ArraySegment<T> and
1226 // IntPtr
1227 code += " public static VectorOffset ";
1228 code += "Create";
1229 code += Name(field);
1230 code += "VectorBlock(FlatBufferBuilder builder, ";
1231 code += GenTypeBasic(vector_type) + "[] data) ";
1232 code += "{ builder.StartVector(";
1233 code += NumToString(elem_size);
1234 code += ", data.Length, ";
1235 code += NumToString(alignment);
1236 code += "); builder.Add(data); return builder.EndVector(); }\n";
1237
1238 code += " public static VectorOffset ";
1239 code += "Create";
1240 code += Name(field);
1241 code += "VectorBlock(FlatBufferBuilder builder, ";
1242 code += "ArraySegment<" + GenTypeBasic(vector_type) + "> data) ";
1243 code += "{ builder.StartVector(";
1244 code += NumToString(elem_size);
1245 code += ", data.Count, ";
1246 code += NumToString(alignment);
1247 code += "); builder.Add(data); return builder.EndVector(); }\n";
1248
1249 code += " public static VectorOffset ";
1250 code += "Create";
1251 code += Name(field);
1252 code += "VectorBlock(FlatBufferBuilder builder, ";
1253 code += "IntPtr dataPtr, int sizeInBytes) ";
1254 code += "{ builder.StartVector(1, sizeInBytes, 1); ";
1255 code += "builder.Add<" + GenTypeBasic(vector_type) +
1256 ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n";
1257 }
1258 // Generate a method to start a vector, data to be added manually
1259 // after.
1260 code += " public static void Start";
1261 code += Name(field);
1262 code += "Vector(FlatBufferBuilder builder, int numElems) ";
1263 code += "{ builder.StartVector(";
1264 code += NumToString(elem_size);
1265 code += ", numElems, " + NumToString(alignment);
1266 code += "); }\n";
1267 }
1268 }
1269 code += " public static " + GenOffsetType(struct_def) + " ";
1270 code += "End" + struct_def.name;
1271 code += "(FlatBufferBuilder builder) {\n int o = builder.";
1272 code += "EndTable();\n";
1273 for (auto it = struct_def.fields.vec.begin();
1274 it != struct_def.fields.vec.end(); ++it) {
1275 auto &field = **it;
1276 if (!field.deprecated && field.IsRequired()) {
1277 code += " builder.Required(o, ";
1278 code += NumToString(field.value.offset);
1279 code += "); // " + field.name + "\n";
1280 }
1281 }
1282 code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n";
1283 if (parser_.root_struct_def_ == &struct_def) {
1284 std::string size_prefix[] = { "", "SizePrefixed" };
1285 for (int i = 0; i < 2; ++i) {
1286 code += " public static void ";
1287 code += "Finish" + size_prefix[i] + struct_def.name;
1288 code +=
1289 "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1290 code += " offset) {";
1291 code += " builder.Finish" + size_prefix[i] + "(offset";
1292 code += ".Value";
1293
1294 if (parser_.file_identifier_.length())
1295 code += ", \"" + parser_.file_identifier_ + "\"";
1296 code += "); }\n";
1297 }
1298 }
1299 }
1300 // Only generate key compare function for table,
1301 // because `key_field` is not set for struct
1302 if (struct_def.has_key && !struct_def.fixed) {
1303 FLATBUFFERS_ASSERT(key_field);
1304 code += "\n public static VectorOffset ";
1305 code += "CreateSortedVectorOf" + struct_def.name;
1306 code += "(FlatBufferBuilder builder, ";
1307 code += "Offset<" + struct_def.name + ">";
1308 code += "[] offsets) {\n";
1309 code += " Array.Sort(offsets,\n";
1310 code += " (Offset<" + struct_def.name +
1311 "> o1, Offset<" + struct_def.name + "> o2) =>\n";
1312 code += " "+ GenKeyGetter(struct_def, key_field);
1313 code += ");\n";
1314 code += " return builder.CreateVectorOfTables(offsets);\n }\n";
1315
1316 code += "\n public static " + struct_def.name + "?";
1317 code += " __lookup_by_key(";
1318 code += "int vectorLocation, ";
1319 code += GenTypeGet(key_field->value.type);
1320 code += " key, ByteBuffer bb) {\n";
1321 code +=
1322 " " + struct_def.name + " obj_ = new " + struct_def.name + "();\n";
1323 code += " int span = ";
1324 code += "bb.GetInt(vectorLocation - 4);\n";
1325 code += " int start = 0;\n";
1326 code += " while (span != 0) {\n";
1327 code += " int middle = span / 2;\n";
1328 code +=
1329 " int tableOffset = Table.__indirect(vectorLocation + 4 * "
1330 "(start + middle), bb);\n";
1331
1332 code += " obj_.__assign(tableOffset, bb);\n";
1333 code +=
1334 " int comp = obj_." + Name(*key_field) + ".CompareTo(key);\n";
1335 code += " if (comp > 0) {\n";
1336 code += " span = middle;\n";
1337 code += " } else if (comp < 0) {\n";
1338 code += " middle++;\n";
1339 code += " start += middle;\n";
1340 code += " span -= middle;\n";
1341 code += " } else {\n";
1342 code += " return obj_;\n";
1343 code += " }\n }\n";
1344 code += " return null;\n";
1345 code += " }\n";
1346 }
1347
1348 if (opts.generate_object_based_api) {
1349 GenPackUnPack_ObjectAPI(struct_def, code_ptr, opts, struct_has_create,
1350 field_has_create_set);
1351 }
1352 code += "}\n\n";
1353
1354 if (opts.generate_object_based_api) {
1355 GenStruct_ObjectAPI(struct_def, code_ptr, opts);
1356 }
1357 }
1358
GenVectorAccessObject(StructDef & struct_def,std::string * code_ptr) const1359 void GenVectorAccessObject(StructDef &struct_def,
1360 std::string *code_ptr) const {
1361 auto &code = *code_ptr;
1362 // Generate a vector of structs accessor class.
1363 code += "\n";
1364 code += " ";
1365 if (!struct_def.attributes.Lookup("private")) code += "public ";
1366 code += "static struct Vector : BaseVector\n{\n";
1367
1368 // Generate the __assign method that sets the field in a pre-existing
1369 // accessor object. This is to allow object reuse.
1370 std::string method_indent = " ";
1371 code += method_indent + "public Vector ";
1372 code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1373 code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1374
1375 auto type_name = struct_def.name;
1376 auto method_start = method_indent + "public " + type_name + " Get";
1377 // Generate the accessors that don't do object reuse.
1378 code += method_start + "(int j) { return Get";
1379 code += "(new " + type_name + "(), j); }\n";
1380 code += method_start + "(" + type_name + " obj, int j) { ";
1381 code += " return obj.__assign(";
1382 code += struct_def.fixed ? "__p.__element(j)"
1383 : "__p.__indirect(__p.__element(j), bb)";
1384 code += ", __p.bb); }\n";
1385 // See if we should generate a by-key accessor.
1386 if (!struct_def.fixed) {
1387 auto &fields = struct_def.fields.vec;
1388 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1389 auto &key_field = **kit;
1390 if (key_field.key) {
1391 auto nullable_annotation =
1392 parser_.opts.gen_nullable ? "@Nullable " : "";
1393 code += method_indent + nullable_annotation;
1394 code += "public " + type_name + "? ";
1395 code += "GetByKey(";
1396 code += GenTypeGet(key_field.value.type) + " key) { ";
1397 code += " return __lookup_by_key(null, ";
1398 code += "__p.__vector(), key, ";
1399 code += "__p.bb); ";
1400 code += "}\n";
1401 code += method_indent + nullable_annotation;
1402 code += "public " + type_name + "?" + " ";
1403 code += "GetByKey(";
1404 code += type_name + "? obj, ";
1405 code += GenTypeGet(key_field.value.type) + " key) { ";
1406 code += " return __lookup_by_key(obj, ";
1407 code += "__p.__vector(), key, ";
1408 code += "__p.bb); ";
1409 code += "}\n";
1410 break;
1411 }
1412 }
1413 }
1414 code += " }\n";
1415 }
1416
GenEnum_ObjectAPI(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const1417 void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
1418 const IDLOptions &opts) const {
1419 auto &code = *code_ptr;
1420 if (enum_def.generated) return;
1421 if (!enum_def.is_union) return;
1422 if (enum_def.attributes.Lookup("private")) {
1423 code += "internal ";
1424 } else {
1425 code += "public ";
1426 }
1427 auto union_name = enum_def.name + "Union";
1428 code += "class " + union_name + " {\n";
1429 // Type
1430 code += " public " + enum_def.name + " Type { get; set; }\n";
1431 // Value
1432 code += " public object Value { get; set; }\n";
1433 code += "\n";
1434 // Constructor
1435 code += " public " + union_name + "() {\n";
1436 code += " this.Type = " + enum_def.name + "." +
1437 enum_def.Vals()[0]->name + ";\n";
1438 code += " this.Value = null;\n";
1439 code += " }\n\n";
1440 // As<T>
1441 code += " public T As<T>() where T : class { return this.Value as T; }\n";
1442 // As, From
1443 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1444 auto &ev = **it;
1445 if (ev.union_type.base_type == BASE_TYPE_NONE) continue;
1446 auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1447 std::string accessibility =
1448 (ev.union_type.base_type == BASE_TYPE_STRUCT &&
1449 ev.union_type.struct_def->attributes.Lookup("private"))
1450 ? "internal"
1451 : "public";
1452 // As
1453 code += " " + accessibility + " " + type_name + " As" + ev.name +
1454 "() { return this.As<" + type_name + ">(); }\n";
1455 // From
1456 auto lower_ev_name = ev.name;
1457 std::transform(lower_ev_name.begin(), lower_ev_name.end(),
1458 lower_ev_name.begin(), CharToLower);
1459 code += " " + accessibility + " static " + union_name + " From" +
1460 ev.name + "(" + type_name + " _" + lower_ev_name +
1461 ") { return new " + union_name + "{ Type = " + Name(enum_def) +
1462 "." + Name(ev) + ", Value = _" + lower_ev_name + " }; }\n";
1463 }
1464 code += "\n";
1465 // Pack()
1466 code += " public static int Pack(FlatBuffers.FlatBufferBuilder builder, " +
1467 union_name + " _o) {\n";
1468 code += " switch (_o.Type) {\n";
1469 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1470 auto &ev = **it;
1471 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1472 code += " default: return 0;\n";
1473 } else {
1474 code += " case " + Name(enum_def) + "." + Name(ev) + ": return ";
1475 if (IsString(ev.union_type)) {
1476 code += "builder.CreateString(_o.As" + ev.name + "()).Value;\n";
1477 } else {
1478 code += GenTypeGet(ev.union_type) + ".Pack(builder, _o.As" + ev.name +
1479 "()).Value;\n";
1480 }
1481 }
1482 }
1483 code += " }\n";
1484 code += " }\n";
1485 code += "}\n\n";
1486 // JsonConverter
1487 if (opts.cs_gen_json_serializer) {
1488 if (enum_def.attributes.Lookup("private")) {
1489 code += "internal ";
1490 } else {
1491 code += "public ";
1492 }
1493 code += "class " + union_name +
1494 "_JsonConverter : Newtonsoft.Json.JsonConverter {\n";
1495 code += " public override bool CanConvert(System.Type objectType) {\n";
1496 code += " return objectType == typeof(" + union_name +
1497 ") || objectType == typeof(System.Collections.Generic.List<" +
1498 union_name + ">);\n";
1499 code += " }\n";
1500 code +=
1501 " public override void WriteJson(Newtonsoft.Json.JsonWriter writer, "
1502 "object value, "
1503 "Newtonsoft.Json.JsonSerializer serializer) {\n";
1504 code += " var _olist = value as System.Collections.Generic.List<" +
1505 union_name + ">;\n";
1506 code += " if (_olist != null) {\n";
1507 code += " writer.WriteStartArray();\n";
1508 code +=
1509 " foreach (var _o in _olist) { this.WriteJson(writer, _o, "
1510 "serializer); }\n";
1511 code += " writer.WriteEndArray();\n";
1512 code += " } else {\n";
1513 code += " this.WriteJson(writer, value as " + union_name +
1514 ", serializer);\n";
1515 code += " }\n";
1516 code += " }\n";
1517 code += " public void WriteJson(Newtonsoft.Json.JsonWriter writer, " +
1518 union_name +
1519 " _o, "
1520 "Newtonsoft.Json.JsonSerializer serializer) {\n";
1521 code += " if (_o == null) return;\n";
1522 code += " serializer.Serialize(writer, _o.Value);\n";
1523 code += " }\n";
1524 code +=
1525 " public override object ReadJson(Newtonsoft.Json.JsonReader "
1526 "reader, "
1527 "System.Type objectType, "
1528 "object existingValue, Newtonsoft.Json.JsonSerializer serializer) "
1529 "{\n";
1530 code +=
1531 " var _olist = existingValue as System.Collections.Generic.List<" +
1532 union_name + ">;\n";
1533 code += " if (_olist != null) {\n";
1534 code += " for (var _j = 0; _j < _olist.Count; ++_j) {\n";
1535 code += " reader.Read();\n";
1536 code +=
1537 " _olist[_j] = this.ReadJson(reader, _olist[_j], "
1538 "serializer);\n";
1539 code += " }\n";
1540 code += " reader.Read();\n";
1541 code += " return _olist;\n";
1542 code += " } else {\n";
1543 code += " return this.ReadJson(reader, existingValue as " +
1544 union_name + ", serializer);\n";
1545 code += " }\n";
1546 code += " }\n";
1547 code += " public " + union_name +
1548 " ReadJson(Newtonsoft.Json.JsonReader reader, " + union_name +
1549 " _o, Newtonsoft.Json.JsonSerializer serializer) {\n";
1550 code += " if (_o == null) return null;\n";
1551 code += " switch (_o.Type) {\n";
1552 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
1553 ++it) {
1554 auto &ev = **it;
1555 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1556 code += " default: break;\n";
1557 } else {
1558 auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1559 code += " case " + Name(enum_def) + "." + Name(ev) +
1560 ": _o.Value = serializer.Deserialize<" + type_name +
1561 ">(reader); break;\n";
1562 }
1563 }
1564 code += " }\n";
1565 code += " return _o;\n";
1566 code += " }\n";
1567 code += "}\n\n";
1568 }
1569 }
1570
GenTypeName_ObjectAPI(const std::string & name,const IDLOptions & opts) const1571 std::string GenTypeName_ObjectAPI(const std::string &name,
1572 const IDLOptions &opts) const {
1573 return opts.object_prefix + name + opts.object_suffix;
1574 }
1575
GenUnionUnPack_ObjectAPI(const EnumDef & enum_def,std::string * code_ptr,const std::string & camel_name,const std::string & camel_name_short,bool is_vector) const1576 void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
1577 const std::string &camel_name,
1578 const std::string &camel_name_short,
1579 bool is_vector) const {
1580 auto &code = *code_ptr;
1581 std::string varialbe_name = "_o." + camel_name;
1582 std::string type_suffix = "";
1583 std::string func_suffix = "()";
1584 std::string indent = " ";
1585 if (is_vector) {
1586 varialbe_name = "_o_" + camel_name;
1587 type_suffix = "(_j)";
1588 func_suffix = "(_j)";
1589 indent = " ";
1590 }
1591 if (is_vector) {
1592 code += indent + "var " + varialbe_name + " = new ";
1593 } else {
1594 code += indent + varialbe_name + " = new ";
1595 }
1596 code += NamespacedName(enum_def) + "Union();\n";
1597 code += indent + varialbe_name + ".Type = this." + camel_name_short +
1598 "Type" + type_suffix + ";\n";
1599 code += indent + "switch (this." + camel_name_short + "Type" + type_suffix +
1600 ") {\n";
1601 for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end();
1602 ++eit) {
1603 auto &ev = **eit;
1604 if (ev.union_type.base_type == BASE_TYPE_NONE) {
1605 code += indent + " default: break;\n";
1606 } else {
1607 code += indent + " case " + NamespacedName(enum_def) + "." + ev.name +
1608 ":\n";
1609 code += indent + " " + varialbe_name + ".Value = this." + camel_name;
1610 if (IsString(ev.union_type)) {
1611 code += "AsString" + func_suffix + ";\n";
1612 } else {
1613 code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix;
1614 code += ".HasValue ? this." + camel_name;
1615 code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix +
1616 ".Value.UnPack() : null;\n";
1617 }
1618 code += indent + " break;\n";
1619 }
1620 }
1621 code += indent + "}\n";
1622 if (is_vector) {
1623 code += indent + "_o." + camel_name + ".Add(" + varialbe_name + ");\n";
1624 }
1625 }
1626
GenPackUnPack_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts,bool struct_has_create,const std::set<FieldDef * > & field_has_create) const1627 void GenPackUnPack_ObjectAPI(
1628 StructDef &struct_def, std::string *code_ptr, const IDLOptions &opts,
1629 bool struct_has_create,
1630 const std::set<FieldDef *> &field_has_create) const {
1631 auto &code = *code_ptr;
1632 auto struct_name = GenTypeName_ObjectAPI(struct_def.name, opts);
1633 // UnPack()
1634 code += " public " + struct_name + " UnPack() {\n";
1635 code += " var _o = new " + struct_name + "();\n";
1636 code += " this.UnPackTo(_o);\n";
1637 code += " return _o;\n";
1638 code += " }\n";
1639 // UnPackTo()
1640 code += " public void UnPackTo(" + struct_name + " _o) {\n";
1641 for (auto it = struct_def.fields.vec.begin();
1642 it != struct_def.fields.vec.end(); ++it) {
1643 auto &field = **it;
1644 if (field.deprecated) continue;
1645 auto camel_name = Name(field);
1646 if (camel_name == struct_def.name) { camel_name += "_"; }
1647 auto camel_name_short = Name(field);
1648 auto start = " _o." + camel_name + " = ";
1649 switch (field.value.type.base_type) {
1650 case BASE_TYPE_STRUCT: {
1651 auto fixed = struct_def.fixed && field.value.type.struct_def->fixed;
1652 if (fixed) {
1653 code += start + "this." + camel_name + ".UnPack();\n";
1654 } else {
1655 code += start + "this." + camel_name + ".HasValue ? this." +
1656 camel_name + ".Value.UnPack() : null;\n";
1657 }
1658 break;
1659 }
1660 case BASE_TYPE_ARRAY: {
1661 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
1662 auto length_str = NumToString(field.value.type.fixed_length);
1663 auto unpack_method = field.value.type.struct_def == nullptr ? ""
1664 : field.value.type.struct_def->fixed
1665 ? ".UnPack()"
1666 : "?.UnPack()";
1667 code += start + "new " + type_name.substr(0, type_name.length() - 1) +
1668 length_str + "];\n";
1669 code += " for (var _j = 0; _j < " + length_str + "; ++_j) { _o." +
1670 camel_name + "[_j] = this." + camel_name + "(_j)" +
1671 unpack_method + "; }\n";
1672 break;
1673 }
1674 case BASE_TYPE_VECTOR:
1675 if (field.value.type.element == BASE_TYPE_UNION) {
1676 code += start + "new " +
1677 GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1678 code += " for (var _j = 0; _j < this." + camel_name +
1679 "Length; ++_j) {\n";
1680 GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1681 camel_name, camel_name_short, true);
1682 code += " }\n";
1683 } else if (field.value.type.element != BASE_TYPE_UTYPE) {
1684 auto fixed = field.value.type.struct_def == nullptr;
1685 code += start + "new " +
1686 GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1687 code += " for (var _j = 0; _j < this." + camel_name +
1688 "Length; ++_j) {";
1689 code += "_o." + camel_name + ".Add(";
1690 if (fixed) {
1691 code += "this." + camel_name + "(_j)";
1692 } else {
1693 code += "this." + camel_name + "(_j).HasValue ? this." +
1694 camel_name + "(_j).Value.UnPack() : null";
1695 }
1696 code += ");}\n";
1697 }
1698 break;
1699 case BASE_TYPE_UTYPE: break;
1700 case BASE_TYPE_UNION: {
1701 GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1702 camel_name, camel_name_short, false);
1703 break;
1704 }
1705 default: {
1706 code += start + "this." + camel_name + ";\n";
1707 break;
1708 }
1709 }
1710 }
1711 code += " }\n";
1712 // Pack()
1713 code += " public static " + GenOffsetType(struct_def) +
1714 " Pack(FlatBufferBuilder builder, " + struct_name + " _o) {\n";
1715 code += " if (_o == null) return default(" + GenOffsetType(struct_def) +
1716 ");\n";
1717 for (auto it = struct_def.fields.vec.begin();
1718 it != struct_def.fields.vec.end(); ++it) {
1719 auto &field = **it;
1720 if (field.deprecated) continue;
1721 auto camel_name = Name(field);
1722 if (camel_name == struct_def.name) { camel_name += "_"; }
1723 auto camel_name_short = Name(field);
1724 // pre
1725 switch (field.value.type.base_type) {
1726 case BASE_TYPE_STRUCT: {
1727 if (!field.value.type.struct_def->fixed) {
1728 code += " var _" + field.name + " = _o." + camel_name +
1729 " == null ? default(" +
1730 GenOffsetType(*field.value.type.struct_def) +
1731 ") : " + GenTypeGet(field.value.type) +
1732 ".Pack(builder, _o." + camel_name + ");\n";
1733 } else if (struct_def.fixed && struct_has_create) {
1734 std::vector<FieldArrayLength> array_lengths;
1735 FieldArrayLength tmp_array_length = {
1736 field.name,
1737 field.value.type.fixed_length,
1738 };
1739 array_lengths.push_back(tmp_array_length);
1740 GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1741 array_lengths);
1742 }
1743 break;
1744 }
1745 case BASE_TYPE_STRING: {
1746 std::string create_string =
1747 field.shared ? "CreateSharedString" : "CreateString";
1748 code += " var _" + field.name + " = _o." + camel_name +
1749 " == null ? default(StringOffset) : "
1750 "builder." +
1751 create_string + "(_o." + camel_name + ");\n";
1752 break;
1753 }
1754 case BASE_TYPE_VECTOR: {
1755 if (field_has_create.find(&field) != field_has_create.end()) {
1756 auto property_name = camel_name;
1757 auto gen_for_loop = true;
1758 std::string array_name = "__" + field.name;
1759 std::string array_type = "";
1760 std::string to_array = "";
1761 switch (field.value.type.element) {
1762 case BASE_TYPE_STRING: {
1763 std::string create_string =
1764 field.shared ? "CreateSharedString" : "CreateString";
1765 array_type = "StringOffset";
1766 to_array += "builder." + create_string + "(_o." +
1767 property_name + "[_j])";
1768 break;
1769 }
1770 case BASE_TYPE_STRUCT:
1771 array_type = "Offset<" + GenTypeGet(field.value.type) + ">";
1772 to_array = GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1773 property_name + "[_j])";
1774 break;
1775 case BASE_TYPE_UTYPE:
1776 property_name = camel_name.substr(0, camel_name.size() - 4);
1777 array_type = NamespacedName(*field.value.type.enum_def);
1778 to_array = "_o." + property_name + "[_j].Type";
1779 break;
1780 case BASE_TYPE_UNION:
1781 array_type = "int";
1782 to_array = NamespacedName(*field.value.type.enum_def) +
1783 "Union.Pack(builder, _o." + property_name + "[_j])";
1784 break;
1785 default: gen_for_loop = false; break;
1786 }
1787 code += " var _" + field.name + " = default(VectorOffset);\n";
1788 code += " if (_o." + property_name + " != null) {\n";
1789 if (gen_for_loop) {
1790 code += " var " + array_name + " = new " + array_type +
1791 "[_o." + property_name + ".Count];\n";
1792 code += " for (var _j = 0; _j < " + array_name +
1793 ".Length; ++_j) { ";
1794 code += array_name + "[_j] = " + to_array + "; }\n";
1795 } else {
1796 code += " var " + array_name + " = _o." + property_name +
1797 ".ToArray();\n";
1798 }
1799 code += " _" + field.name + " = Create" + camel_name_short +
1800 "Vector(builder, " + array_name + ");\n";
1801 code += " }\n";
1802 } else {
1803 auto pack_method =
1804 field.value.type.struct_def == nullptr
1805 ? "builder.Add" + GenMethod(field.value.type.VectorType()) +
1806 "(_o." + camel_name + "[_j]);"
1807 : GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1808 camel_name + "[_j]);";
1809 code += " var _" + field.name + " = default(VectorOffset);\n";
1810 code += " if (_o." + camel_name + " != null) {\n";
1811 code += " Start" + camel_name_short + "Vector(builder, _o." +
1812 camel_name + ".Count);\n";
1813 code += " for (var _j = _o." + camel_name +
1814 ".Count - 1; _j >= 0; --_j) { " + pack_method + " }\n";
1815 code += " _" + field.name + " = builder.EndVector();\n";
1816 code += " }\n";
1817 }
1818 break;
1819 }
1820 case BASE_TYPE_ARRAY: {
1821 if (field.value.type.struct_def != nullptr) {
1822 std::vector<FieldArrayLength> array_lengths;
1823 FieldArrayLength tmp_array_length = {
1824 field.name,
1825 field.value.type.fixed_length,
1826 };
1827 array_lengths.push_back(tmp_array_length);
1828 GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1829 array_lengths);
1830 } else {
1831 code += " var _" + field.name + " = _o." + camel_name + ";\n";
1832 }
1833 break;
1834 }
1835 case BASE_TYPE_UNION: {
1836 code += " var _" + field.name + "_type = _o." + camel_name +
1837 " == null ? " + NamespacedName(*field.value.type.enum_def) +
1838 ".NONE : " + "_o." + camel_name + ".Type;\n";
1839 code +=
1840 " var _" + field.name + " = _o." + camel_name +
1841 " == null ? 0 : " + GenTypeGet_ObjectAPI(field.value.type, opts) +
1842 ".Pack(builder, _o." + camel_name + ");\n";
1843 break;
1844 }
1845 default: break;
1846 }
1847 }
1848 if (struct_has_create) {
1849 // Create
1850 code += " return Create" + struct_def.name + "(\n";
1851 code += " builder";
1852 for (auto it = struct_def.fields.vec.begin();
1853 it != struct_def.fields.vec.end(); ++it) {
1854 auto &field = **it;
1855 if (field.deprecated) continue;
1856 auto camel_name = Name(field);
1857 if (camel_name == struct_def.name) { camel_name += "_"; }
1858 switch (field.value.type.base_type) {
1859 case BASE_TYPE_STRUCT: {
1860 if (struct_def.fixed) {
1861 GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1862 code_ptr,
1863 " _" + field.name + "_");
1864 } else {
1865 code += ",\n";
1866 if (field.value.type.struct_def->fixed) {
1867 if (opts.generate_object_based_api)
1868 code += " _o." + camel_name;
1869 else
1870 code += " " + GenTypeGet(field.value.type) +
1871 ".Pack(builder, _o." + camel_name + ")";
1872 } else {
1873 code += " _" + field.name;
1874 }
1875 }
1876 break;
1877 }
1878 case BASE_TYPE_ARRAY: {
1879 if (field.value.type.struct_def != nullptr) {
1880 GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1881 code_ptr,
1882 " _" + field.name + "_");
1883 } else {
1884 code += ",\n";
1885 code += " _" + field.name;
1886 }
1887 break;
1888 }
1889 case BASE_TYPE_UNION: FLATBUFFERS_FALLTHROUGH(); // fall thru
1890 case BASE_TYPE_UTYPE: FLATBUFFERS_FALLTHROUGH(); // fall thru
1891 case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH(); // fall thru
1892 case BASE_TYPE_VECTOR: {
1893 code += ",\n";
1894 code += " _" + field.name;
1895 break;
1896 }
1897 default: // scalar
1898 code += ",\n";
1899 code += " _o." + camel_name;
1900 break;
1901 }
1902 }
1903 code += ");\n";
1904 } else {
1905 // Start, End
1906 code += " Start" + struct_def.name + "(builder);\n";
1907 for (auto it = struct_def.fields.vec.begin();
1908 it != struct_def.fields.vec.end(); ++it) {
1909 auto &field = **it;
1910 if (field.deprecated) continue;
1911 auto camel_name = Name(field);
1912 switch (field.value.type.base_type) {
1913 case BASE_TYPE_STRUCT: {
1914 if (field.value.type.struct_def->fixed) {
1915 code += " Add" + camel_name + "(builder, " +
1916 GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1917 camel_name + "));\n";
1918 } else {
1919 code +=
1920 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1921 }
1922 break;
1923 }
1924 case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH(); // fall thru
1925 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
1926 case BASE_TYPE_VECTOR: {
1927 code +=
1928 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1929 break;
1930 }
1931 case BASE_TYPE_UTYPE: break;
1932 case BASE_TYPE_UNION: {
1933 code += " Add" + camel_name + "Type(builder, _" + field.name +
1934 "_type);\n";
1935 code +=
1936 " Add" + camel_name + "(builder, _" + field.name + ");\n";
1937 break;
1938 }
1939 // scalar
1940 default: {
1941 code +=
1942 " Add" + camel_name + "(builder, _o." + camel_name + ");\n";
1943 break;
1944 }
1945 }
1946 }
1947 code += " return End" + struct_def.name + "(builder);\n";
1948 }
1949 code += " }\n";
1950 }
1951
GenStructPackDecl_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::vector<FieldArrayLength> & array_lengths) const1952 void GenStructPackDecl_ObjectAPI(
1953 const StructDef &struct_def, std::string *code_ptr,
1954 std::vector<FieldArrayLength> &array_lengths) const {
1955 auto &code = *code_ptr;
1956 for (auto it = struct_def.fields.vec.begin();
1957 it != struct_def.fields.vec.end(); ++it) {
1958 auto &field = **it;
1959 auto is_array = IsArray(field.value.type);
1960 const auto &field_type =
1961 is_array ? field.value.type.VectorType() : field.value.type;
1962 FieldArrayLength tmp_array_length = {
1963 field.name,
1964 field_type.fixed_length,
1965 };
1966 array_lengths.push_back(tmp_array_length);
1967 if (field_type.struct_def != nullptr) {
1968 GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr,
1969 array_lengths);
1970 } else {
1971 std::vector<FieldArrayLength> array_only_lengths;
1972 for (size_t i = 0; i < array_lengths.size(); ++i) {
1973 if (array_lengths[i].length > 0) {
1974 array_only_lengths.push_back(array_lengths[i]);
1975 }
1976 }
1977 std::string name;
1978 for (size_t i = 0; i < array_lengths.size(); ++i) {
1979 name += "_" + array_lengths[i].name;
1980 }
1981 code += " var " + name + " = ";
1982 if (array_only_lengths.size() > 0) {
1983 code += "new " + GenTypeBasic(field_type) + "[";
1984 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1985 if (i != 0) { code += ","; }
1986 code += NumToString(array_only_lengths[i].length);
1987 }
1988 code += "];\n";
1989 code += " ";
1990 // initialize array
1991 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1992 auto idx = "idx" + NumToString(i);
1993 code += "for (var " + idx + " = 0; " + idx + " < " +
1994 NumToString(array_only_lengths[i].length) + "; ++" + idx +
1995 ") {";
1996 }
1997 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1998 auto idx = "idx" + NumToString(i);
1999 if (i == 0) {
2000 code += name + "[" + idx;
2001 } else {
2002 code += "," + idx;
2003 }
2004 }
2005 code += "] = _o";
2006 for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) {
2007 code += "." + ConvertCase(array_lengths[i].name, Case::kUpperCamel);
2008 if (array_lengths[i].length <= 0) continue;
2009 code += "[idx" + NumToString(j++) + "]";
2010 }
2011 code += ";";
2012 for (size_t i = 0; i < array_only_lengths.size(); ++i) {
2013 code += "}";
2014 }
2015 } else {
2016 code += "_o";
2017 for (size_t i = 0; i < array_lengths.size(); ++i) {
2018 code += "." + ConvertCase(array_lengths[i].name, Case::kUpperCamel);
2019 }
2020 code += ";";
2021 }
2022 code += "\n";
2023 }
2024 array_lengths.pop_back();
2025 }
2026 }
2027
GenStructPackCall_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::string prefix) const2028 void GenStructPackCall_ObjectAPI(const StructDef &struct_def,
2029 std::string *code_ptr,
2030 std::string prefix) const {
2031 auto &code = *code_ptr;
2032 for (auto it = struct_def.fields.vec.begin();
2033 it != struct_def.fields.vec.end(); ++it) {
2034 auto &field = **it;
2035 const auto &field_type = field.value.type;
2036 if (field_type.struct_def != nullptr) {
2037 GenStructPackCall_ObjectAPI(*field_type.struct_def, code_ptr,
2038 prefix + field.name + "_");
2039 } else {
2040 code += ",\n";
2041 code += prefix + field.name;
2042 }
2043 }
2044 }
2045
GenTypeGet_ObjectAPI(flatbuffers::Type type,const IDLOptions & opts) const2046 std::string GenTypeGet_ObjectAPI(flatbuffers::Type type,
2047 const IDLOptions &opts) const {
2048 auto type_name = GenTypeGet(type);
2049 // Replace to ObjectBaseAPI Type Name
2050 switch (type.base_type) {
2051 case BASE_TYPE_STRUCT: FLATBUFFERS_FALLTHROUGH(); // fall thru
2052 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
2053 case BASE_TYPE_VECTOR: {
2054 if (type.struct_def != nullptr) {
2055 auto type_name_length = type.struct_def->name.length();
2056 auto new_type_name =
2057 GenTypeName_ObjectAPI(type.struct_def->name, opts);
2058 type_name.replace(type_name.length() - type_name_length,
2059 type_name_length, new_type_name);
2060 } else if (type.element == BASE_TYPE_UNION) {
2061 type_name = NamespacedName(*type.enum_def) + "Union";
2062 }
2063 break;
2064 }
2065
2066 case BASE_TYPE_UNION: {
2067 type_name = NamespacedName(*type.enum_def) + "Union";
2068 break;
2069 }
2070 default: break;
2071 }
2072
2073 switch (type.base_type) {
2074 case BASE_TYPE_ARRAY: {
2075 type_name = type_name + "[]";
2076 break;
2077 }
2078 case BASE_TYPE_VECTOR: {
2079 type_name = "List<" + type_name + ">";
2080 break;
2081 }
2082 default: break;
2083 }
2084 return type_name;
2085 }
2086
GenStruct_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const2087 void GenStruct_ObjectAPI(StructDef &struct_def, std::string *code_ptr,
2088 const IDLOptions &opts) const {
2089 auto &code = *code_ptr;
2090 if (struct_def.attributes.Lookup("private")) {
2091 code += "internal ";
2092 } else {
2093 code += "public ";
2094 }
2095 if (struct_def.attributes.Lookup("csharp_partial")) {
2096 // generate a partial class for this C# struct/table
2097 code += "partial ";
2098 }
2099 auto class_name = GenTypeName_ObjectAPI(struct_def.name, opts);
2100 code += "class " + class_name;
2101 code += "\n{\n";
2102 // Generate Properties
2103 for (auto it = struct_def.fields.vec.begin();
2104 it != struct_def.fields.vec.end(); ++it) {
2105 auto &field = **it;
2106 if (field.deprecated) continue;
2107 if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2108 if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2109 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2110 if (field.IsScalarOptional()) type_name += "?";
2111 auto camel_name = Name(field);
2112 if (camel_name == struct_def.name) { camel_name += "_"; }
2113 if (opts.cs_gen_json_serializer) {
2114 if (IsUnion(field.value.type)) {
2115 auto utype_name = NamespacedName(*field.value.type.enum_def);
2116 code +=
2117 " [Newtonsoft.Json.JsonProperty(\"" + field.name + "_type\")]\n";
2118 if (IsVector(field.value.type)) {
2119 code += " private " + utype_name + "[] " + camel_name + "Type {\n";
2120 code += " get {\n";
2121 code += " if (this." + camel_name + " == null) return null;\n";
2122 code += " var _o = new " + utype_name + "[this." + camel_name +
2123 ".Count];\n";
2124 code +=
2125 " for (var _j = 0; _j < _o.Length; ++_j) { _o[_j] = "
2126 "this." +
2127 camel_name + "[_j].Type; }\n";
2128 code += " return _o;\n";
2129 code += " }\n";
2130 code += " set {\n";
2131 code += " this." + camel_name + " = new List<" + utype_name +
2132 "Union>();\n";
2133 code += " for (var _j = 0; _j < value.Length; ++_j) {\n";
2134 code += " var _o = new " + utype_name + "Union();\n";
2135 code += " _o.Type = value[_j];\n";
2136 code += " this." + camel_name + ".Add(_o);\n";
2137 code += " }\n";
2138 code += " }\n";
2139 code += " }\n";
2140 } else {
2141 code += " private " + utype_name + " " + camel_name + "Type {\n";
2142 code += " get {\n";
2143 code += " return this." + camel_name + " != null ? this." +
2144 camel_name + ".Type : " + utype_name + ".NONE;\n";
2145 code += " }\n";
2146 code += " set {\n";
2147 code += " this." + camel_name + " = new " + utype_name +
2148 "Union();\n";
2149 code += " this." + camel_name + ".Type = value;\n";
2150 code += " }\n";
2151 code += " }\n";
2152 }
2153 }
2154 code += " [Newtonsoft.Json.JsonProperty(\"" + field.name + "\")]\n";
2155 if (IsUnion(field.value.type)) {
2156 auto union_name =
2157 (IsVector(field.value.type))
2158 ? GenTypeGet_ObjectAPI(field.value.type.VectorType(), opts)
2159 : type_name;
2160 code += " [Newtonsoft.Json.JsonConverter(typeof(" + union_name +
2161 "_JsonConverter))]\n";
2162 }
2163 if (field.attributes.Lookup("hash")) {
2164 code += " [Newtonsoft.Json.JsonIgnore()]\n";
2165 }
2166 }
2167 code += " public " + type_name + " " + camel_name + " { get; set; }\n";
2168 }
2169 // Generate Constructor
2170 code += "\n";
2171 code += " public " + class_name + "() {\n";
2172 for (auto it = struct_def.fields.vec.begin();
2173 it != struct_def.fields.vec.end(); ++it) {
2174 auto &field = **it;
2175 if (field.deprecated) continue;
2176 if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2177 if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2178 auto camel_name = Name(field);
2179 if (camel_name == struct_def.name) { camel_name += "_"; }
2180 code += " this." + camel_name + " = ";
2181 auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2182 if (IsScalar(field.value.type.base_type)) {
2183 code += GenDefaultValue(field) + ";\n";
2184 } else {
2185 switch (field.value.type.base_type) {
2186 case BASE_TYPE_STRUCT: {
2187 if (IsStruct(field.value.type)) {
2188 code += "new " + type_name + "();\n";
2189 } else {
2190 code += "null;\n";
2191 }
2192 break;
2193 }
2194 case BASE_TYPE_ARRAY: {
2195 code += "new " + type_name.substr(0, type_name.length() - 1) +
2196 NumToString(field.value.type.fixed_length) + "];\n";
2197 break;
2198 }
2199 default: {
2200 code += "null;\n";
2201 break;
2202 }
2203 }
2204 }
2205 }
2206 code += " }\n";
2207 // Generate Serialization
2208 if (opts.cs_gen_json_serializer &&
2209 parser_.root_struct_def_ == &struct_def) {
2210 code += "\n";
2211 code += " public static " + class_name +
2212 " DeserializeFromJson(string jsonText) {\n";
2213 code += " return Newtonsoft.Json.JsonConvert.DeserializeObject<" +
2214 class_name + ">(jsonText);\n";
2215 code += " }\n";
2216 code += " public string SerializeToJson() {\n";
2217 code +=
2218 " return Newtonsoft.Json.JsonConvert.SerializeObject(this, "
2219 "Newtonsoft.Json.Formatting.Indented);\n";
2220 code += " }\n";
2221 }
2222 if (parser_.root_struct_def_ == &struct_def) {
2223 code += " public static " + class_name +
2224 " DeserializeFromBinary(byte[] fbBuffer) {\n";
2225 code += " return " + struct_def.name + ".GetRootAs" + struct_def.name +
2226 "(new ByteBuffer(fbBuffer)).UnPack();\n";
2227 code += " }\n";
2228 code += " public byte[] SerializeToBinary() {\n";
2229 code += " var fbb = new FlatBufferBuilder(0x10000);\n";
2230 code += " " + struct_def.name + ".Finish" + struct_def.name +
2231 "Buffer(fbb, " + struct_def.name + ".Pack(fbb, this));\n";
2232 code += " return fbb.DataBuffer.ToSizedArray();\n";
2233 code += " }\n";
2234 }
2235 code += "}\n\n";
2236 }
2237
2238 // This tracks the current namespace used to determine if a type need to be
2239 // prefixed by its namespace
2240 const Namespace *cur_name_space_;
2241 };
2242 } // namespace csharp
2243
GenerateCSharp(const Parser & parser,const std::string & path,const std::string & file_name)2244 bool GenerateCSharp(const Parser &parser, const std::string &path,
2245 const std::string &file_name) {
2246 csharp::CSharpGenerator generator(parser, path, file_name);
2247 return generator.generate();
2248 }
2249
2250 } // namespace flatbuffers
2251