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