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