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