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:
CSharpGenerator(const Parser & parser,const std::string & path,const std::string & file_name)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 
generate()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.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes) const98   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 
CurrentNameSpace() const124   const Namespace *CurrentNameSpace() const { return cur_name_space_; }
125 
GenTypeBasic(const Type & type,bool enableLangOverrides) const126   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 
GenTypeBasic(const Type & type) const146   inline std::string GenTypeBasic(const Type &type) const {
147     return GenTypeBasic(type, true);
148   }
149 
GenTypePointer(const Type & type) const150   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 
GenTypeGet(const Type & type) const160   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 
GenOffsetType(const StructDef & struct_def) const167   std::string GenOffsetType(const StructDef &struct_def) const {
168     return "Offset<" + WrapInNameSpace(struct_def) + ">";
169   }
170 
GenOffsetConstruct(const StructDef & struct_def,const std::string & variable_name) const171   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
DestinationCast(const Type & type) const178   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.
SourceCast(const Type & type) const193   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 
SourceCastBasic(const Type & type) const202   std::string SourceCastBasic(const Type &type) const {
203     return IsScalar(type.base_type) ? SourceCast(type) : "";
204   }
205 
GenEnumDefaultValue(const FieldDef & field) const206   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 
GenDefaultValue(const FieldDef & field,bool enableLangOverrides) const215   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 
GenDefaultValue(const FieldDef & field) const243   std::string GenDefaultValue(const FieldDef &field) const {
244     return GenDefaultValue(field, true);
245   }
246 
GenDefaultValueBasic(const FieldDef & field,bool enableLangOverrides) const247   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 
GenDefaultValueBasic(const FieldDef & field) const266   std::string GenDefaultValueBasic(const FieldDef &field) const {
267     return GenDefaultValueBasic(field, true);
268   }
269 
GenEnum(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const270   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 
HasUnionStringValue(const EnumDef & enum_def) const315   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.
GenGetter(const Type & type) const325   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.
GenGetterForLookupByKey(flatbuffers::FieldDef * key_field,const std::string & data_buffer,const char * num=nullptr) const345   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.
GenSetter(const Type & type) const362   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.
GenMethod(const Type & type) const376   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.
GenStructArgs(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t array_count=0) const383   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.
GenStructBody(const StructDef & struct_def,std::string * code_ptr,const char * nameprefix,size_t index=0,bool in_array=false) const418   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   }
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const474   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 
GenLookupKeyGetter(flatbuffers::FieldDef * key_field) const488   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 
GenKeyGetter(flatbuffers::FieldDef * key_field) const505   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 
GenStruct(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const521   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_2_0_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 += "); }\n";
849         code += "#endif\n";
850 
851         // For direct blockcopying the data into a typed array
852         code += "  public ";
853         code += GenTypeBasic(field.value.type.VectorType());
854         code += "[] Get";
855         code += MakeCamel(field.name, true);
856         code += "Array() { ";
857         if (IsEnum(field.value.type.VectorType())) {
858           // Since __vector_as_array does not work for enum types,
859           // fill array using an explicit loop.
860           code += "int o = __p.__offset(";
861           code += NumToString(field.value.offset);
862           code += "); if (o == 0) return null; int p = ";
863           code += "__p.__vector(o); int l = ";
864           code += "__p.__vector_len(o); ";
865           code += GenTypeBasic(field.value.type.VectorType());
866           code += "[] a = new ";
867           code += GenTypeBasic(field.value.type.VectorType());
868           code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
869           code += "(p + i * ";
870           code += NumToString(InlineSize(field.value.type.VectorType()));
871           code += "); } return a;";
872         } else {
873           code += "return ";
874           code += "__p.__vector_as_array<";
875           code += GenTypeBasic(field.value.type.VectorType());
876           code += ">(";
877           code += NumToString(field.value.offset);
878           code += ");";
879         }
880         code += " }\n";
881       }
882       // generate object accessors if is nested_flatbuffer
883       if (field.nested_flatbuffer) {
884         auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
885         auto nested_method_name =
886             MakeCamel(field.name, true) + "As" + field.nested_flatbuffer->name;
887         auto get_nested_method_name = nested_method_name;
888         get_nested_method_name = "Get" + nested_method_name;
889         conditional_cast = "(" + nested_type_name + "?)";
890         obj = "(new " + nested_type_name + "())";
891         code += "  public " + nested_type_name + "? ";
892         code += get_nested_method_name + "(";
893         code += ") { int o = __p.__offset(";
894         code += NumToString(field.value.offset) + "); ";
895         code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
896         code += "__p.";
897         code += "__indirect(__p.__vector(o)), ";
898         code += "__p.bb) : null; }\n";
899       }
900       // Generate mutators for scalar fields or vectors of scalars.
901       if (parser_.opts.mutable_buffer) {
902         auto is_series = (IsSeries(field.value.type));
903         const auto &underlying_type =
904             is_series ? field.value.type.VectorType() : field.value.type;
905         // Boolean parameters have to be explicitly converted to byte
906         // representation.
907         auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
908                                     ? "(byte)(" + field.name + " ? 1 : 0)"
909                                     : field.name;
910         auto mutator_prefix = MakeCamel("mutate", true);
911         // A vector mutator also needs the index of the vector element it should
912         // mutate.
913         auto mutator_params = (is_series ? "(int j, " : "(") +
914                               GenTypeGet(underlying_type) + " " + field.name +
915                               ") { ";
916         auto setter_index =
917             is_series
918                 ? "__p." +
919                       (IsArray(field.value.type)
920                            ? "bb_pos + " + NumToString(field.value.offset)
921                            : "__vector(o)") +
922                       +" + j * " + NumToString(InlineSize(underlying_type))
923                 : (struct_def.fixed
924                        ? "__p.bb_pos + " + NumToString(field.value.offset)
925                        : "o + __p.bb_pos");
926         if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
927           code += "  public ";
928           code += struct_def.fixed ? "void " : "bool ";
929           code += mutator_prefix + MakeCamel(field.name, true);
930           code += mutator_params;
931           if (struct_def.fixed) {
932             code += GenSetter(underlying_type) + "(" + setter_index + ", ";
933             code += src_cast + setter_parameter + "); }\n";
934           } else {
935             code += "int o = __p.__offset(";
936             code += NumToString(field.value.offset) + ");";
937             code += " if (o != 0) { " + GenSetter(underlying_type);
938             code += "(" + setter_index + ", " + src_cast + setter_parameter +
939                     "); return true; } else { return false; } }\n";
940           }
941         }
942       }
943       if (parser_.opts.java_primitive_has_method &&
944           IsScalar(field.value.type.base_type) && !struct_def.fixed) {
945         auto vt_offset_constant = "  public static final int VT_" +
946                                   MakeScreamingCamel(field.name) + " = " +
947                                   NumToString(field.value.offset) + ";";
948 
949         code += vt_offset_constant;
950         code += "\n";
951       }
952     }
953     code += "\n";
954     auto struct_has_create = false;
955     std::set<flatbuffers::FieldDef *> field_has_create_set;
956     flatbuffers::FieldDef *key_field = nullptr;
957     if (struct_def.fixed) {
958       struct_has_create = true;
959       // create a struct constructor function
960       code += "  public static " + GenOffsetType(struct_def) + " ";
961       code += "Create";
962       code += struct_def.name + "(FlatBufferBuilder builder";
963       GenStructArgs(struct_def, code_ptr, "");
964       code += ") {\n";
965       GenStructBody(struct_def, code_ptr, "");
966       code += "    return ";
967       code += GenOffsetConstruct(struct_def, "builder.Offset");
968       code += ";\n  }\n";
969     } else {
970       // Generate a method that creates a table in one go. This is only possible
971       // when the table has no struct fields, since those have to be created
972       // inline, and there's no way to do so in Java.
973       bool has_no_struct_fields = true;
974       int num_fields = 0;
975       for (auto it = struct_def.fields.vec.begin();
976            it != struct_def.fields.vec.end(); ++it) {
977         auto &field = **it;
978         if (field.deprecated) continue;
979         if (IsStruct(field.value.type)) {
980           has_no_struct_fields = false;
981         } else {
982           num_fields++;
983         }
984       }
985       // JVM specifications restrict default constructor params to be < 255.
986       // Longs and doubles take up 2 units, so we set the limit to be < 127.
987       if ((has_no_struct_fields || opts.generate_object_based_api) &&
988           num_fields && num_fields < 127) {
989         struct_has_create = true;
990         // Generate a table constructor of the form:
991         // public static int createName(FlatBufferBuilder builder, args...)
992         code += "  public static " + GenOffsetType(struct_def) + " ";
993         code += "Create" + struct_def.name;
994         code += "(FlatBufferBuilder builder";
995         for (auto it = struct_def.fields.vec.begin();
996              it != struct_def.fields.vec.end(); ++it) {
997           auto &field = **it;
998           if (field.deprecated) continue;
999           code += ",\n      ";
1000           if (IsStruct(field.value.type) && opts.generate_object_based_api) {
1001             code += WrapInNameSpace(
1002                 field.value.type.struct_def->defined_namespace,
1003                 GenTypeName_ObjectAPI(field.value.type.struct_def->name, opts));
1004             code += " ";
1005             code += field.name;
1006             code += " = null";
1007           } else {
1008             code += GenTypeBasic(field.value.type);
1009             if (field.IsScalarOptional()) { code += "?"; }
1010             code += " ";
1011             code += field.name;
1012             if (!IsScalar(field.value.type.base_type)) code += "Offset";
1013 
1014             code += " = ";
1015             code += GenDefaultValueBasic(field);
1016           }
1017         }
1018         code += ") {\n    builder.";
1019         code += "StartTable(";
1020         code += NumToString(struct_def.fields.vec.size()) + ");\n";
1021         for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
1022              size; size /= 2) {
1023           for (auto it = struct_def.fields.vec.rbegin();
1024                it != struct_def.fields.vec.rend(); ++it) {
1025             auto &field = **it;
1026             if (!field.deprecated &&
1027                 (!struct_def.sortbysize ||
1028                  size == SizeOf(field.value.type.base_type))) {
1029               code += "    " + struct_def.name + ".";
1030               code += "Add";
1031               code += MakeCamel(field.name) + "(builder, ";
1032               if (IsStruct(field.value.type) &&
1033                   opts.generate_object_based_api) {
1034                 code += GenTypePointer(field.value.type) + ".Pack(builder, " +
1035                         field.name + ")";
1036               } else {
1037                 code += field.name;
1038                 if (!IsScalar(field.value.type.base_type)) code += "Offset";
1039               }
1040 
1041               code += ");\n";
1042             }
1043           }
1044         }
1045         code += "    return " + struct_def.name + ".";
1046         code += "End" + struct_def.name;
1047         code += "(builder);\n  }\n\n";
1048       }
1049       // Generate a set of static methods that allow table construction,
1050       // of the form:
1051       // public static void addName(FlatBufferBuilder builder, short name)
1052       // { builder.addShort(id, name, default); }
1053       // Unlike the Create function, these always work.
1054       code += "  public static void Start";
1055       code += struct_def.name;
1056       code += "(FlatBufferBuilder builder) { builder.";
1057       code += "StartTable(";
1058       code += NumToString(struct_def.fields.vec.size()) + "); }\n";
1059       for (auto it = struct_def.fields.vec.begin();
1060            it != struct_def.fields.vec.end(); ++it) {
1061         auto &field = **it;
1062         if (field.deprecated) continue;
1063         if (field.key) key_field = &field;
1064         code += "  public static void Add";
1065         code += MakeCamel(field.name);
1066         code += "(FlatBufferBuilder builder, ";
1067         code += GenTypeBasic(field.value.type);
1068         auto argname = MakeCamel(field.name, false);
1069         if (!IsScalar(field.value.type.base_type)) argname += "Offset";
1070         if (field.IsScalarOptional()) { code += "?"; }
1071         code += " " + argname + ") { builder.Add";
1072         code += GenMethod(field.value.type) + "(";
1073         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1074         code += SourceCastBasic(field.value.type);
1075         code += argname;
1076         if (!IsScalar(field.value.type.base_type) &&
1077             field.value.type.base_type != BASE_TYPE_UNION) {
1078           code += ".Value";
1079         }
1080         if (!field.IsScalarOptional()) {
1081           // When the scalar is optional, use the builder method that doesn't
1082           // supply a default value. Otherwise, we to continue to use the
1083           // default value method.
1084           code += ", ";
1085           code += GenDefaultValue(field, false);
1086         }
1087         code += "); }\n";
1088         if (IsVector(field.value.type)) {
1089           auto vector_type = field.value.type.VectorType();
1090           auto alignment = InlineAlignment(vector_type);
1091           auto elem_size = InlineSize(vector_type);
1092           if (!IsStruct(vector_type)) {
1093             field_has_create_set.insert(&field);
1094             code += "  public static VectorOffset ";
1095             code += "Create";
1096             code += MakeCamel(field.name);
1097             code += "Vector(FlatBufferBuilder builder, ";
1098             code += GenTypeBasic(vector_type) + "[] data) ";
1099             code += "{ builder.StartVector(";
1100             code += NumToString(elem_size);
1101             code += ", data.Length, ";
1102             code += NumToString(alignment);
1103             code += "); for (int i = data.";
1104             code += "Length - 1; i >= 0; i--) builder.";
1105             code += "Add";
1106             code += GenMethod(vector_type);
1107             code += "(";
1108             code += SourceCastBasic(vector_type);
1109             code += "data[i]";
1110             if (vector_type.base_type == BASE_TYPE_STRUCT ||
1111                 IsString(vector_type))
1112               code += ".Value";
1113             code += "); return ";
1114             code += "builder.EndVector(); }\n";
1115 
1116             code += "  public static VectorOffset ";
1117             code += "Create";
1118             code += MakeCamel(field.name);
1119             code += "VectorBlock(FlatBufferBuilder builder, ";
1120             code += GenTypeBasic(vector_type) + "[] data) ";
1121             code += "{ builder.StartVector(";
1122             code += NumToString(elem_size);
1123             code += ", data.Length, ";
1124             code += NumToString(alignment);
1125             code += "); builder.Add(data); return builder.EndVector(); }\n";
1126           }
1127           // Generate a method to start a vector, data to be added manually
1128           // after.
1129           code += "  public static void Start";
1130           code += MakeCamel(field.name);
1131           code += "Vector(FlatBufferBuilder builder, int numElems) ";
1132           code += "{ builder.StartVector(";
1133           code += NumToString(elem_size);
1134           code += ", numElems, " + NumToString(alignment);
1135           code += "); }\n";
1136         }
1137       }
1138       code += "  public static " + GenOffsetType(struct_def) + " ";
1139       code += "End" + struct_def.name;
1140       code += "(FlatBufferBuilder builder) {\n    int o = builder.";
1141       code += "EndTable();\n";
1142       for (auto it = struct_def.fields.vec.begin();
1143            it != struct_def.fields.vec.end(); ++it) {
1144         auto &field = **it;
1145         if (!field.deprecated && field.IsRequired()) {
1146           code += "    builder.Required(o, ";
1147           code += NumToString(field.value.offset);
1148           code += ");  // " + field.name + "\n";
1149         }
1150       }
1151       code += "    return " + GenOffsetConstruct(struct_def, "o") + ";\n  }\n";
1152       if (parser_.root_struct_def_ == &struct_def) {
1153         std::string size_prefix[] = { "", "SizePrefixed" };
1154         for (int i = 0; i < 2; ++i) {
1155           code += "  public static void ";
1156           code += "Finish" + size_prefix[i] + struct_def.name;
1157           code +=
1158               "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1159           code += " offset) {";
1160           code += " builder.Finish" + size_prefix[i] + "(offset";
1161           code += ".Value";
1162 
1163           if (parser_.file_identifier_.length())
1164             code += ", \"" + parser_.file_identifier_ + "\"";
1165           code += "); }\n";
1166         }
1167       }
1168     }
1169     // Only generate key compare function for table,
1170     // because `key_field` is not set for struct
1171     if (struct_def.has_key && !struct_def.fixed) {
1172       FLATBUFFERS_ASSERT(key_field);
1173       code += "\n  public static VectorOffset ";
1174       code += "CreateSortedVectorOf" + struct_def.name;
1175       code += "(FlatBufferBuilder builder, ";
1176       code += "Offset<" + struct_def.name + ">";
1177       code += "[] offsets) {\n";
1178       code += "    Array.Sort(offsets, (Offset<" + struct_def.name +
1179               "> o1, Offset<" + struct_def.name + "> o2) => " +
1180               GenKeyGetter(key_field);
1181       code += ");\n";
1182       code += "    return builder.CreateVectorOfTables(offsets);\n  }\n";
1183 
1184       code += "\n  public static " + struct_def.name + "?";
1185       code += " __lookup_by_key(";
1186       code += "int vectorLocation, ";
1187       code += GenTypeGet(key_field->value.type);
1188       code += " key, ByteBuffer bb) {\n";
1189       if (IsString(key_field->value.type)) {
1190         code += "    byte[] byteKey = ";
1191         code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
1192       }
1193       code += "    int span = ";
1194       code += "bb.GetInt(vectorLocation - 4);\n";
1195       code += "    int start = 0;\n";
1196       code += "    while (span != 0) {\n";
1197       code += "      int middle = span / 2;\n";
1198       code += GenLookupKeyGetter(key_field);
1199       code += "      if (comp > 0) {\n";
1200       code += "        span = middle;\n";
1201       code += "      } else if (comp < 0) {\n";
1202       code += "        middle++;\n";
1203       code += "        start += middle;\n";
1204       code += "        span -= middle;\n";
1205       code += "      } else {\n";
1206       code += "        return ";
1207       code += "new " + struct_def.name + "()";
1208       code += ".__assign(tableOffset, bb);\n";
1209       code += "      }\n    }\n";
1210       code += "    return null;\n";
1211       code += "  }\n";
1212     }
1213 
1214     if (opts.generate_object_based_api) {
1215       GenPackUnPack_ObjectAPI(struct_def, code_ptr, opts, struct_has_create,
1216                               field_has_create_set);
1217     }
1218     code += "};\n\n";
1219 
1220     if (opts.generate_object_based_api) {
1221       GenStruct_ObjectAPI(struct_def, code_ptr, opts);
1222     }
1223   }
1224 
GenVectorAccessObject(StructDef & struct_def,std::string * code_ptr) const1225   void GenVectorAccessObject(StructDef &struct_def,
1226                              std::string *code_ptr) const {
1227     auto &code = *code_ptr;
1228     // Generate a vector of structs accessor class.
1229     code += "\n";
1230     code += "  ";
1231     if (!struct_def.attributes.Lookup("private")) code += "public ";
1232     code += "static struct Vector : BaseVector\n{\n";
1233 
1234     // Generate the __assign method that sets the field in a pre-existing
1235     // accessor object. This is to allow object reuse.
1236     std::string method_indent = "    ";
1237     code += method_indent + "public Vector ";
1238     code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1239     code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1240 
1241     auto type_name = struct_def.name;
1242     auto method_start = method_indent + "public " + type_name + " Get";
1243     // Generate the accessors that don't do object reuse.
1244     code += method_start + "(int j) { return Get";
1245     code += "(new " + type_name + "(), j); }\n";
1246     code += method_start + "(" + type_name + " obj, int j) { ";
1247     code += " return obj.__assign(";
1248     code += struct_def.fixed ? "__p.__element(j)"
1249                              : "__p.__indirect(__p.__element(j), bb)";
1250     code += ", __p.bb); }\n";
1251     // See if we should generate a by-key accessor.
1252     if (!struct_def.fixed) {
1253       auto &fields = struct_def.fields.vec;
1254       for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1255         auto &key_field = **kit;
1256         if (key_field.key) {
1257           auto nullable_annotation =
1258               parser_.opts.gen_nullable ? "@Nullable " : "";
1259           code += method_indent + nullable_annotation;
1260           code += "public " + type_name + "? ";
1261           code += "GetByKey(";
1262           code += GenTypeGet(key_field.value.type) + " key) { ";
1263           code += " return __lookup_by_key(null, ";
1264           code += "__p.__vector(), key, ";
1265           code += "__p.bb); ";
1266           code += "}\n";
1267           code += method_indent + nullable_annotation;
1268           code += "public " + type_name + "?" + " ";
1269           code += "GetByKey(";
1270           code += type_name + "? obj, ";
1271           code += GenTypeGet(key_field.value.type) + " key) { ";
1272           code += " return __lookup_by_key(obj, ";
1273           code += "__p.__vector(), key, ";
1274           code += "__p.bb); ";
1275           code += "}\n";
1276           break;
1277         }
1278       }
1279     }
1280     code += "  }\n";
1281   }
1282 
GenEnum_ObjectAPI(EnumDef & enum_def,std::string * code_ptr,const IDLOptions & opts) const1283   void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
1284                          const IDLOptions &opts) const {
1285     auto &code = *code_ptr;
1286     if (enum_def.generated) return;
1287     if (!enum_def.is_union) return;
1288     if (enum_def.attributes.Lookup("private")) {
1289       code += "internal ";
1290     } else {
1291       code += "public ";
1292     }
1293     auto union_name = enum_def.name + "Union";
1294     code += "class " + union_name + " {\n";
1295     // Type
1296     code += "  public " + enum_def.name + " Type { get; set; }\n";
1297     // Value
1298     code += "  public object Value { get; set; }\n";
1299     code += "\n";
1300     // Constructor
1301     code += "  public " + union_name + "() {\n";
1302     code += "    this.Type = " + enum_def.name + "." +
1303             enum_def.Vals()[0]->name + ";\n";
1304     code += "    this.Value = null;\n";
1305     code += "  }\n\n";
1306     // As<T>
1307     code += "  public T As<T>() where T : class { return this.Value as T; }\n";
1308     // As
1309     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1310       auto &ev = **it;
1311       if (ev.union_type.base_type == BASE_TYPE_NONE) continue;
1312       auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1313       if (ev.union_type.base_type == BASE_TYPE_STRUCT &&
1314           ev.union_type.struct_def->attributes.Lookup("private")) {
1315         code += "  internal ";
1316       } else {
1317         code += "  public ";
1318       }
1319       code += type_name + " As" + ev.name + "() { return this.As<" + type_name +
1320               ">(); }\n";
1321     }
1322     code += "\n";
1323     // Pack()
1324     code += "  public static int Pack(FlatBuffers.FlatBufferBuilder builder, " +
1325             union_name + " _o) {\n";
1326     code += "    switch (_o.Type) {\n";
1327     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
1328       auto &ev = **it;
1329       if (ev.union_type.base_type == BASE_TYPE_NONE) {
1330         code += "      default: return 0;\n";
1331       } else {
1332         code += "      case " + enum_def.name + "." + ev.name + ": return ";
1333         if (IsString(ev.union_type)) {
1334           code += "builder.CreateString(_o.As" + ev.name + "()).Value;\n";
1335         } else {
1336           code += GenTypeGet(ev.union_type) + ".Pack(builder, _o.As" + ev.name +
1337                   "()).Value;\n";
1338         }
1339       }
1340     }
1341     code += "    }\n";
1342     code += "  }\n";
1343     code += "}\n\n";
1344     // JsonConverter
1345     if (opts.cs_gen_json_serializer) {
1346       if (enum_def.attributes.Lookup("private")) {
1347         code += "internal ";
1348       } else {
1349         code += "public ";
1350       }
1351       code += "class " + union_name +
1352               "_JsonConverter : Newtonsoft.Json.JsonConverter {\n";
1353       code += "  public override bool CanConvert(System.Type objectType) {\n";
1354       code += "    return objectType == typeof(" + union_name +
1355               ") || objectType == typeof(System.Collections.Generic.List<" +
1356               union_name + ">);\n";
1357       code += "  }\n";
1358       code +=
1359           "  public override void WriteJson(Newtonsoft.Json.JsonWriter writer, "
1360           "object value, "
1361           "Newtonsoft.Json.JsonSerializer serializer) {\n";
1362       code += "    var _olist = value as System.Collections.Generic.List<" +
1363               union_name + ">;\n";
1364       code += "    if (_olist != null) {\n";
1365       code += "      writer.WriteStartArray();\n";
1366       code +=
1367           "      foreach (var _o in _olist) { this.WriteJson(writer, _o, "
1368           "serializer); }\n";
1369       code += "      writer.WriteEndArray();\n";
1370       code += "    } else {\n";
1371       code += "      this.WriteJson(writer, value as " + union_name +
1372               ", serializer);\n";
1373       code += "    }\n";
1374       code += "  }\n";
1375       code += "  public void WriteJson(Newtonsoft.Json.JsonWriter writer, " +
1376               union_name +
1377               " _o, "
1378               "Newtonsoft.Json.JsonSerializer serializer) {\n";
1379       code += "    if (_o == null) return;\n";
1380       code += "    serializer.Serialize(writer, _o.Value);\n";
1381       code += "  }\n";
1382       code +=
1383           "  public override object ReadJson(Newtonsoft.Json.JsonReader "
1384           "reader, "
1385           "System.Type objectType, "
1386           "object existingValue, Newtonsoft.Json.JsonSerializer serializer) "
1387           "{\n";
1388       code +=
1389           "    var _olist = existingValue as System.Collections.Generic.List<" +
1390           union_name + ">;\n";
1391       code += "    if (_olist != null) {\n";
1392       code += "      for (var _j = 0; _j < _olist.Count; ++_j) {\n";
1393       code += "        reader.Read();\n";
1394       code +=
1395           "        _olist[_j] = this.ReadJson(reader, _olist[_j], "
1396           "serializer);\n";
1397       code += "      }\n";
1398       code += "      reader.Read();\n";
1399       code += "      return _olist;\n";
1400       code += "    } else {\n";
1401       code += "      return this.ReadJson(reader, existingValue as " +
1402               union_name + ", serializer);\n";
1403       code += "    }\n";
1404       code += "  }\n";
1405       code += "  public " + union_name +
1406               " ReadJson(Newtonsoft.Json.JsonReader reader, " + union_name +
1407               " _o, Newtonsoft.Json.JsonSerializer serializer) {\n";
1408       code += "    if (_o == null) return null;\n";
1409       code += "    switch (_o.Type) {\n";
1410       for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
1411            ++it) {
1412         auto &ev = **it;
1413         if (ev.union_type.base_type == BASE_TYPE_NONE) {
1414           code += "      default: break;\n";
1415         } else {
1416           auto type_name = GenTypeGet_ObjectAPI(ev.union_type, opts);
1417           code += "      case " + enum_def.name + "." + ev.name +
1418                   ": _o.Value = serializer.Deserialize<" + type_name +
1419                   ">(reader); break;\n";
1420         }
1421       }
1422       code += "    }\n";
1423       code += "    return _o;\n";
1424       code += "  }\n";
1425       code += "}\n\n";
1426     }
1427   }
1428 
GenTypeName_ObjectAPI(const std::string & name,const IDLOptions & opts) const1429   std::string GenTypeName_ObjectAPI(const std::string &name,
1430                                     const IDLOptions &opts) const {
1431     return opts.object_prefix + name + opts.object_suffix;
1432   }
1433 
GenUnionUnPack_ObjectAPI(const EnumDef & enum_def,std::string * code_ptr,const std::string & camel_name,bool is_vector) const1434   void GenUnionUnPack_ObjectAPI(const EnumDef &enum_def, std::string *code_ptr,
1435                                 const std::string &camel_name,
1436                                 bool is_vector) const {
1437     auto &code = *code_ptr;
1438     std::string varialbe_name = "_o." + camel_name;
1439     std::string type_suffix = "";
1440     std::string func_suffix = "()";
1441     std::string indent = "    ";
1442     if (is_vector) {
1443       varialbe_name = "_o_" + camel_name;
1444       type_suffix = "(_j)";
1445       func_suffix = "(_j)";
1446       indent = "      ";
1447     }
1448     if (is_vector) {
1449       code += indent + "var " + varialbe_name + " = new ";
1450     } else {
1451       code += indent + varialbe_name + " = new ";
1452     }
1453     code += WrapInNameSpace(enum_def) + "Union();\n";
1454     code += indent + varialbe_name + ".Type = this." + camel_name + "Type" +
1455             type_suffix + ";\n";
1456     code +=
1457         indent + "switch (this." + camel_name + "Type" + type_suffix + ") {\n";
1458     for (auto eit = enum_def.Vals().begin(); eit != enum_def.Vals().end();
1459          ++eit) {
1460       auto &ev = **eit;
1461       if (ev.union_type.base_type == BASE_TYPE_NONE) {
1462         code += indent + "  default: break;\n";
1463       } else {
1464         code += indent + "  case " + WrapInNameSpace(enum_def) + "." + ev.name +
1465                 ":\n";
1466         code += indent + "    " + varialbe_name + ".Value = this." + camel_name;
1467         if (IsString(ev.union_type)) {
1468           code += "AsString" + func_suffix + ";\n";
1469         } else {
1470           code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix;
1471           code += ".HasValue ? this." + camel_name;
1472           code += "<" + GenTypeGet(ev.union_type) + ">" + func_suffix +
1473                   ".Value.UnPack() : null;\n";
1474         }
1475         code += indent + "    break;\n";
1476       }
1477     }
1478     code += indent + "}\n";
1479     if (is_vector) {
1480       code += indent + "_o." + camel_name + ".Add(" + varialbe_name + ");\n";
1481     }
1482   }
1483 
GenPackUnPack_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts,bool struct_has_create,const std::set<FieldDef * > & field_has_create) const1484   void GenPackUnPack_ObjectAPI(
1485       StructDef &struct_def, std::string *code_ptr, const IDLOptions &opts,
1486       bool struct_has_create,
1487       const std::set<FieldDef *> &field_has_create) const {
1488     auto &code = *code_ptr;
1489     auto struct_name = GenTypeName_ObjectAPI(struct_def.name, opts);
1490     // UnPack()
1491     code += "  public " + struct_name + " UnPack() {\n";
1492     code += "    var _o = new " + struct_name + "();\n";
1493     code += "    this.UnPackTo(_o);\n";
1494     code += "    return _o;\n";
1495     code += "  }\n";
1496     // UnPackTo()
1497     code += "  public void UnPackTo(" + struct_name + " _o) {\n";
1498     for (auto it = struct_def.fields.vec.begin();
1499          it != struct_def.fields.vec.end(); ++it) {
1500       auto &field = **it;
1501       if (field.deprecated) continue;
1502       auto camel_name = MakeCamel(field.name);
1503       auto start = "    _o." + camel_name + " = ";
1504       switch (field.value.type.base_type) {
1505         case BASE_TYPE_STRUCT: {
1506           auto fixed = struct_def.fixed && field.value.type.struct_def->fixed;
1507           if (fixed) {
1508             code += start + "this." + camel_name + ".UnPack();\n";
1509           } else {
1510             code += start + "this." + camel_name + ".HasValue ? this." +
1511                     camel_name + ".Value.UnPack() : null;\n";
1512           }
1513           break;
1514         }
1515         case BASE_TYPE_ARRAY: {
1516           auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
1517           auto length_str = NumToString(field.value.type.fixed_length);
1518           auto unpack_method = field.value.type.struct_def == nullptr
1519                                    ? ""
1520                                    : field.value.type.struct_def->fixed
1521                                          ? ".UnPack()"
1522                                          : "?.UnPack()";
1523           code += start + "new " + type_name.substr(0, type_name.length() - 1) +
1524                   length_str + "];\n";
1525           code += "    for (var _j = 0; _j < " + length_str + "; ++_j) { _o." +
1526                   camel_name + "[_j] = this." + camel_name + "(_j)" +
1527                   unpack_method + "; }\n";
1528           break;
1529         }
1530         case BASE_TYPE_VECTOR:
1531           if (field.value.type.element == BASE_TYPE_UNION) {
1532             code += start + "new " +
1533                     GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1534             code += "    for (var _j = 0; _j < this." + camel_name +
1535                     "Length; ++_j) {\n";
1536             GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1537                                      camel_name, true);
1538             code += "    }\n";
1539           } else if (field.value.type.element != BASE_TYPE_UTYPE) {
1540             auto fixed = field.value.type.struct_def == nullptr;
1541             code += start + "new " +
1542                     GenTypeGet_ObjectAPI(field.value.type, opts) + "();\n";
1543             code += "    for (var _j = 0; _j < this." + camel_name +
1544                     "Length; ++_j) {";
1545             code += "_o." + camel_name + ".Add(";
1546             if (fixed) {
1547               code += "this." + camel_name + "(_j)";
1548             } else {
1549               code += "this." + camel_name + "(_j).HasValue ? this." +
1550                       camel_name + "(_j).Value.UnPack() : null";
1551             }
1552             code += ");}\n";
1553           }
1554           break;
1555         case BASE_TYPE_UTYPE: break;
1556         case BASE_TYPE_UNION: {
1557           GenUnionUnPack_ObjectAPI(*field.value.type.enum_def, code_ptr,
1558                                    camel_name, false);
1559           break;
1560         }
1561         default: {
1562           code += start + "this." + camel_name + ";\n";
1563           break;
1564         }
1565       }
1566     }
1567     code += "  }\n";
1568     // Pack()
1569     code += "  public static " + GenOffsetType(struct_def) +
1570             " Pack(FlatBufferBuilder builder, " + struct_name + " _o) {\n";
1571     code += "    if (_o == null) return default(" + GenOffsetType(struct_def) +
1572             ");\n";
1573     for (auto it = struct_def.fields.vec.begin();
1574          it != struct_def.fields.vec.end(); ++it) {
1575       auto &field = **it;
1576       if (field.deprecated) continue;
1577       auto camel_name = MakeCamel(field.name);
1578       // pre
1579       switch (field.value.type.base_type) {
1580         case BASE_TYPE_STRUCT: {
1581           if (!field.value.type.struct_def->fixed) {
1582             code += "    var _" + field.name + " = _o." + camel_name +
1583                     " == null ? default(" +
1584                     GenOffsetType(*field.value.type.struct_def) +
1585                     ") : " + GenTypeGet(field.value.type) +
1586                     ".Pack(builder, _o." + camel_name + ");\n";
1587           } else if (struct_def.fixed && struct_has_create) {
1588             std::vector<FieldArrayLength> array_lengths;
1589             FieldArrayLength tmp_array_length = {
1590               field.name,
1591               field.value.type.fixed_length,
1592             };
1593             array_lengths.push_back(tmp_array_length);
1594             GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1595                                         array_lengths);
1596           }
1597           break;
1598         }
1599         case BASE_TYPE_STRING: {
1600           std::string create_string =
1601               field.shared ? "CreateSharedString" : "CreateString";
1602           code += "    var _" + field.name + " = _o." + camel_name +
1603                   " == null ? default(StringOffset) : "
1604                   "builder." +
1605                   create_string + "(_o." + camel_name + ");\n";
1606           break;
1607         }
1608         case BASE_TYPE_VECTOR: {
1609           if (field_has_create.find(&field) != field_has_create.end()) {
1610             auto property_name = camel_name;
1611             auto gen_for_loop = true;
1612             std::string array_name = "__" + field.name;
1613             std::string array_type = "";
1614             std::string to_array = "";
1615             switch (field.value.type.element) {
1616               case BASE_TYPE_STRING: {
1617                 std::string create_string =
1618                     field.shared ? "CreateSharedString" : "CreateString";
1619                 array_type = "StringOffset";
1620                 to_array += "builder." + create_string + "(_o." +
1621                             property_name + "[_j])";
1622                 break;
1623               }
1624               case BASE_TYPE_STRUCT:
1625                 array_type = "Offset<" + GenTypeGet(field.value.type) + ">";
1626                 to_array = GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1627                            property_name + "[_j])";
1628                 break;
1629               case BASE_TYPE_UTYPE:
1630                 property_name = camel_name.substr(0, camel_name.size() - 4);
1631                 array_type = WrapInNameSpace(*field.value.type.enum_def);
1632                 to_array = "_o." + property_name + "[_j].Type";
1633                 break;
1634               case BASE_TYPE_UNION:
1635                 array_type = "int";
1636                 to_array = WrapInNameSpace(*field.value.type.enum_def) +
1637                            "Union.Pack(builder,  _o." + property_name + "[_j])";
1638                 break;
1639               default: gen_for_loop = false; break;
1640             }
1641             code += "    var _" + field.name + " = default(VectorOffset);\n";
1642             code += "    if (_o." + property_name + " != null) {\n";
1643             if (gen_for_loop) {
1644               code += "      var " + array_name + " = new " + array_type +
1645                       "[_o." + property_name + ".Count];\n";
1646               code += "      for (var _j = 0; _j < " + array_name +
1647                       ".Length; ++_j) { ";
1648               code += array_name + "[_j] = " + to_array + "; }\n";
1649             } else {
1650               code += "      var " + array_name + " = _o." + property_name +
1651                       ".ToArray();\n";
1652             }
1653             code += "      _" + field.name + " = Create" + camel_name +
1654                     "Vector(builder, " + array_name + ");\n";
1655             code += "    }\n";
1656           } else {
1657             auto pack_method =
1658                 field.value.type.struct_def == nullptr
1659                     ? "builder.Add" + GenMethod(field.value.type.VectorType()) +
1660                           "(_o." + camel_name + "[_j]);"
1661                     : GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1662                           camel_name + "[_j]);";
1663             code += "    var _" + field.name + " = default(VectorOffset);\n";
1664             code += "    if (_o." + camel_name + " != null) {\n";
1665             code += "      Start" + camel_name + "Vector(builder, _o." +
1666                     camel_name + ".Count);\n";
1667             code += "      for (var _j = _o." + camel_name +
1668                     ".Count - 1; _j >= 0; --_j) { " + pack_method + " }\n";
1669             code += "      _" + field.name + " = builder.EndVector();\n";
1670             code += "    }\n";
1671           }
1672           break;
1673         }
1674         case BASE_TYPE_ARRAY: {
1675           if (field.value.type.struct_def != nullptr) {
1676             std::vector<FieldArrayLength> array_lengths;
1677             FieldArrayLength tmp_array_length = {
1678               field.name,
1679               field.value.type.fixed_length,
1680             };
1681             array_lengths.push_back(tmp_array_length);
1682             GenStructPackDecl_ObjectAPI(*field.value.type.struct_def, code_ptr,
1683                                         array_lengths);
1684           } else {
1685             code += "    var _" + field.name + " = _o." + camel_name + ";\n";
1686           }
1687           break;
1688         }
1689         case BASE_TYPE_UNION: {
1690           code += "    var _" + field.name + "_type = _o." + camel_name +
1691                   " == null ? " + WrapInNameSpace(*field.value.type.enum_def) +
1692                   ".NONE : " + "_o." + camel_name + ".Type;\n";
1693           code +=
1694               "    var _" + field.name + " = _o." + camel_name +
1695               " == null ? 0 : " + GenTypeGet_ObjectAPI(field.value.type, opts) +
1696               ".Pack(builder, _o." + camel_name + ");\n";
1697           break;
1698         }
1699         default: break;
1700       }
1701     }
1702     if (struct_has_create) {
1703       // Create
1704       code += "    return Create" + struct_def.name + "(\n";
1705       code += "      builder";
1706       for (auto it = struct_def.fields.vec.begin();
1707            it != struct_def.fields.vec.end(); ++it) {
1708         auto &field = **it;
1709         if (field.deprecated) continue;
1710         auto camel_name = MakeCamel(field.name);
1711         switch (field.value.type.base_type) {
1712           case BASE_TYPE_STRUCT: {
1713             if (struct_def.fixed) {
1714               GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1715                                           code_ptr,
1716                                           "      _" + field.name + "_");
1717             } else {
1718               code += ",\n";
1719               if (field.value.type.struct_def->fixed) {
1720                 if (opts.generate_object_based_api)
1721                   code += "      _o." + camel_name;
1722                 else
1723                   code += "      " + GenTypeGet(field.value.type) +
1724                           ".Pack(builder, _o." + camel_name + ")";
1725               } else {
1726                 code += "      _" + field.name;
1727               }
1728             }
1729             break;
1730           }
1731           case BASE_TYPE_ARRAY: {
1732             if (field.value.type.struct_def != nullptr) {
1733               GenStructPackCall_ObjectAPI(*field.value.type.struct_def,
1734                                           code_ptr,
1735                                           "      _" + field.name + "_");
1736             } else {
1737               code += ",\n";
1738               code += "      _" + field.name;
1739             }
1740             break;
1741           }
1742           case BASE_TYPE_UNION: FLATBUFFERS_FALLTHROUGH();   // fall thru
1743           case BASE_TYPE_UTYPE: FLATBUFFERS_FALLTHROUGH();   // fall thru
1744           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
1745           case BASE_TYPE_VECTOR: {
1746             code += ",\n";
1747             code += "      _" + field.name;
1748             break;
1749           }
1750           default:  // scalar
1751             code += ",\n";
1752             code += "      _o." + camel_name;
1753             break;
1754         }
1755       }
1756       code += ");\n";
1757     } else {
1758       // Start, End
1759       code += "    Start" + struct_def.name + "(builder);\n";
1760       for (auto it = struct_def.fields.vec.begin();
1761            it != struct_def.fields.vec.end(); ++it) {
1762         auto &field = **it;
1763         if (field.deprecated) continue;
1764         auto camel_name = MakeCamel(field.name);
1765         switch (field.value.type.base_type) {
1766           case BASE_TYPE_STRUCT: {
1767             if (field.value.type.struct_def->fixed) {
1768               code += "    Add" + camel_name + "(builder, " +
1769                       GenTypeGet(field.value.type) + ".Pack(builder, _o." +
1770                       camel_name + "));\n";
1771             } else {
1772               code +=
1773                   "    Add" + camel_name + "(builder, _" + field.name + ");\n";
1774             }
1775             break;
1776           }
1777           case BASE_TYPE_STRING: FLATBUFFERS_FALLTHROUGH();  // fall thru
1778           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();   // fall thru
1779           case BASE_TYPE_VECTOR: {
1780             code +=
1781                 "    Add" + camel_name + "(builder, _" + field.name + ");\n";
1782             break;
1783           }
1784           case BASE_TYPE_UTYPE: break;
1785           case BASE_TYPE_UNION: {
1786             code += "    Add" + camel_name + "Type(builder, _" + field.name +
1787                     "_type);\n";
1788             code +=
1789                 "    Add" + camel_name + "(builder, _" + field.name + ");\n";
1790             break;
1791           }
1792           // scalar
1793           default: {
1794             code +=
1795                 "    Add" + camel_name + "(builder, _o." + camel_name + ");\n";
1796             break;
1797           }
1798         }
1799       }
1800       code += "    return End" + struct_def.name + "(builder);\n";
1801     }
1802     code += "  }\n";
1803   }
1804 
GenStructPackDecl_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::vector<FieldArrayLength> & array_lengths) const1805   void GenStructPackDecl_ObjectAPI(
1806       const StructDef &struct_def, std::string *code_ptr,
1807       std::vector<FieldArrayLength> &array_lengths) const {
1808     auto &code = *code_ptr;
1809     for (auto it = struct_def.fields.vec.begin();
1810          it != struct_def.fields.vec.end(); ++it) {
1811       auto &field = **it;
1812       auto is_array = IsArray(field.value.type);
1813       const auto &field_type =
1814           is_array ? field.value.type.VectorType() : field.value.type;
1815       FieldArrayLength tmp_array_length = {
1816         field.name,
1817         field_type.fixed_length,
1818       };
1819       array_lengths.push_back(tmp_array_length);
1820       if (field_type.struct_def != nullptr) {
1821         GenStructPackDecl_ObjectAPI(*field_type.struct_def, code_ptr,
1822                                     array_lengths);
1823       } else {
1824         std::vector<FieldArrayLength> array_only_lengths;
1825         for (size_t i = 0; i < array_lengths.size(); ++i) {
1826           if (array_lengths[i].length > 0) {
1827             array_only_lengths.push_back(array_lengths[i]);
1828           }
1829         }
1830         std::string name;
1831         for (size_t i = 0; i < array_lengths.size(); ++i) {
1832           name += "_" + array_lengths[i].name;
1833         }
1834         code += "    var " + name + " = ";
1835         if (array_only_lengths.size() > 0) {
1836           code += "new " + GenTypeBasic(field_type) + "[";
1837           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1838             if (i != 0) { code += ","; }
1839             code += NumToString(array_only_lengths[i].length);
1840           }
1841           code += "];\n";
1842           code += "    ";
1843           // initialize array
1844           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1845             auto idx = "idx" + NumToString(i);
1846             code += "for (var " + idx + " = 0; " + idx + " < " +
1847                     NumToString(array_only_lengths[i].length) + "; ++" + idx +
1848                     ") {";
1849           }
1850           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1851             auto idx = "idx" + NumToString(i);
1852             if (i == 0) {
1853               code += name + "[" + idx;
1854             } else {
1855               code += "," + idx;
1856             }
1857           }
1858           code += "] = _o";
1859           for (size_t i = 0, j = 0; i < array_lengths.size(); ++i) {
1860             code += "." + MakeCamel(array_lengths[i].name);
1861             if (array_lengths[i].length <= 0) continue;
1862             code += "[idx" + NumToString(j++) + "]";
1863           }
1864           code += ";";
1865           for (size_t i = 0; i < array_only_lengths.size(); ++i) {
1866             code += "}";
1867           }
1868         } else {
1869           code += "_o";
1870           for (size_t i = 0; i < array_lengths.size(); ++i) {
1871             code += "." + MakeCamel(array_lengths[i].name);
1872           }
1873           code += ";";
1874         }
1875         code += "\n";
1876       }
1877       array_lengths.pop_back();
1878     }
1879   }
1880 
GenStructPackCall_ObjectAPI(const StructDef & struct_def,std::string * code_ptr,std::string prefix) const1881   void GenStructPackCall_ObjectAPI(const StructDef &struct_def,
1882                                    std::string *code_ptr,
1883                                    std::string prefix) const {
1884     auto &code = *code_ptr;
1885     for (auto it = struct_def.fields.vec.begin();
1886          it != struct_def.fields.vec.end(); ++it) {
1887       auto &field = **it;
1888       const auto &field_type = field.value.type;
1889       if (field_type.struct_def != nullptr) {
1890         GenStructPackCall_ObjectAPI(*field_type.struct_def, code_ptr,
1891                                     prefix + field.name + "_");
1892       } else {
1893         code += ",\n";
1894         code += prefix + field.name;
1895       }
1896     }
1897   }
1898 
GenTypeGet_ObjectAPI(flatbuffers::Type type,const IDLOptions & opts) const1899   std::string GenTypeGet_ObjectAPI(flatbuffers::Type type,
1900                                    const IDLOptions &opts) const {
1901     auto type_name = GenTypeGet(type);
1902     // Replace to ObjectBaseAPI Type Name
1903     switch (type.base_type) {
1904       case BASE_TYPE_STRUCT: FLATBUFFERS_FALLTHROUGH();  // fall thru
1905       case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();   // fall thru
1906       case BASE_TYPE_VECTOR: {
1907         if (type.struct_def != nullptr) {
1908           auto type_name_length = type.struct_def->name.length();
1909           auto new_type_name =
1910               GenTypeName_ObjectAPI(type.struct_def->name, opts);
1911           type_name.replace(type_name.length() - type_name_length,
1912                             type_name_length, new_type_name);
1913         } else if (type.element == BASE_TYPE_UNION) {
1914           type_name = WrapInNameSpace(*type.enum_def) + "Union";
1915         }
1916         break;
1917       }
1918 
1919       case BASE_TYPE_UNION: {
1920         type_name = WrapInNameSpace(*type.enum_def) + "Union";
1921         break;
1922       }
1923       default: break;
1924     }
1925 
1926     switch (type.base_type) {
1927       case BASE_TYPE_ARRAY: {
1928         type_name = type_name + "[]";
1929         break;
1930       }
1931       case BASE_TYPE_VECTOR: {
1932         type_name = "List<" + type_name + ">";
1933         break;
1934       }
1935       default: break;
1936     }
1937     return type_name;
1938   }
1939 
GenStruct_ObjectAPI(StructDef & struct_def,std::string * code_ptr,const IDLOptions & opts) const1940   void GenStruct_ObjectAPI(StructDef &struct_def, std::string *code_ptr,
1941                            const IDLOptions &opts) const {
1942     auto &code = *code_ptr;
1943     if (struct_def.attributes.Lookup("private")) {
1944       code += "internal ";
1945     } else {
1946       code += "public ";
1947     }
1948     if (struct_def.attributes.Lookup("csharp_partial")) {
1949       // generate a partial class for this C# struct/table
1950       code += "partial ";
1951     }
1952     auto class_name = GenTypeName_ObjectAPI(struct_def.name, opts);
1953     code += "class " + class_name;
1954     code += "\n{\n";
1955     // Generate Properties
1956     for (auto it = struct_def.fields.vec.begin();
1957          it != struct_def.fields.vec.end(); ++it) {
1958       auto &field = **it;
1959       if (field.deprecated) continue;
1960       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
1961       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
1962       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
1963       if (field.IsScalarOptional()) type_name += "?";
1964       auto camel_name = MakeCamel(field.name, true);
1965       if (opts.cs_gen_json_serializer) {
1966         if (IsUnion(field.value.type)) {
1967           auto utype_name = WrapInNameSpace(*field.value.type.enum_def);
1968           code +=
1969               "  [Newtonsoft.Json.JsonProperty(\"" + field.name + "_type\")]\n";
1970           if (IsVector(field.value.type)) {
1971             code += "  private " + utype_name + "[] " + camel_name + "Type {\n";
1972             code += "    get {\n";
1973             code += "      if (this." + camel_name + " == null) return null;\n";
1974             code += "      var _o = new " + utype_name + "[this." + camel_name +
1975                     ".Count];\n";
1976             code +=
1977                 "      for (var _j = 0; _j < _o.Length; ++_j) { _o[_j] = "
1978                 "this." +
1979                 camel_name + "[_j].Type; }\n";
1980             code += "      return _o;\n";
1981             code += "    }\n";
1982             code += "    set {\n";
1983             code += "      this." + camel_name + " = new List<" + utype_name +
1984                     "Union>();\n";
1985             code += "      for (var _j = 0; _j < value.Length; ++_j) {\n";
1986             code += "        var _o = new " + utype_name + "Union();\n";
1987             code += "        _o.Type = value[_j];\n";
1988             code += "        this." + camel_name + ".Add(_o);\n";
1989             code += "      }\n";
1990             code += "    }\n";
1991             code += "  }\n";
1992           } else {
1993             code += "  private " + utype_name + " " + camel_name + "Type {\n";
1994             code += "    get {\n";
1995             code += "      return this." + camel_name + " != null ? this." +
1996                     camel_name + ".Type : " + utype_name + ".NONE;\n";
1997             code += "    }\n";
1998             code += "    set {\n";
1999             code += "      this." + camel_name + " = new " + utype_name +
2000                     "Union();\n";
2001             code += "      this." + camel_name + ".Type = value;\n";
2002             code += "    }\n";
2003             code += "  }\n";
2004           }
2005         }
2006         code += "  [Newtonsoft.Json.JsonProperty(\"" + field.name + "\")]\n";
2007         if (IsUnion(field.value.type)) {
2008           auto union_name =
2009               (IsVector(field.value.type))
2010                   ? GenTypeGet_ObjectAPI(field.value.type.VectorType(), opts)
2011                   : type_name;
2012           code += "  [Newtonsoft.Json.JsonConverter(typeof(" + union_name +
2013                   "_JsonConverter))]\n";
2014         }
2015         if (field.attributes.Lookup("hash")) {
2016           code += "  [Newtonsoft.Json.JsonIgnore()]\n";
2017         }
2018       }
2019       code += "  public " + type_name + " " + camel_name + " { get; set; }\n";
2020     }
2021     // Generate Constructor
2022     code += "\n";
2023     code += "  public " + class_name + "() {\n";
2024     for (auto it = struct_def.fields.vec.begin();
2025          it != struct_def.fields.vec.end(); ++it) {
2026       auto &field = **it;
2027       if (field.deprecated) continue;
2028       if (field.value.type.base_type == BASE_TYPE_UTYPE) continue;
2029       if (field.value.type.element == BASE_TYPE_UTYPE) continue;
2030       code += "    this." + MakeCamel(field.name) + " = ";
2031       auto type_name = GenTypeGet_ObjectAPI(field.value.type, opts);
2032       if (IsScalar(field.value.type.base_type)) {
2033         code += GenDefaultValue(field) + ";\n";
2034       } else {
2035         switch (field.value.type.base_type) {
2036           case BASE_TYPE_STRUCT: {
2037             if (IsStruct(field.value.type)) {
2038               code += "new " + type_name + "();\n";
2039             } else {
2040               code += "null;\n";
2041             }
2042             break;
2043           }
2044           case BASE_TYPE_ARRAY: {
2045             code += "new " + type_name.substr(0, type_name.length() - 1) +
2046                     NumToString(field.value.type.fixed_length) + "];\n";
2047             break;
2048           }
2049           default: {
2050             code += "null;\n";
2051             break;
2052           }
2053         }
2054       }
2055     }
2056     code += "  }\n";
2057     // Generate Serialization
2058     if (opts.cs_gen_json_serializer &&
2059         parser_.root_struct_def_ == &struct_def) {
2060       code += "\n";
2061       code += "  public static " + class_name +
2062               " DeserializeFromJson(string jsonText) {\n";
2063       code += "    return Newtonsoft.Json.JsonConvert.DeserializeObject<" +
2064               class_name + ">(jsonText);\n";
2065       code += "  }\n";
2066       code += "  public string SerializeToJson() {\n";
2067       code +=
2068           "    return Newtonsoft.Json.JsonConvert.SerializeObject(this, "
2069           "Newtonsoft.Json.Formatting.Indented);\n";
2070       code += "  }\n";
2071     }
2072     if (parser_.root_struct_def_ == &struct_def) {
2073       code += "  public static " + class_name +
2074               " DeserializeFromBinary(byte[] fbBuffer) {\n";
2075       code += "    return " + struct_def.name + ".GetRootAs" + struct_def.name +
2076               "(new ByteBuffer(fbBuffer)).UnPack();\n";
2077       code += "  }\n";
2078       code += "  public byte[] SerializeToBinary() {\n";
2079       code += "    var fbb = new FlatBufferBuilder(0x10000);\n";
2080       code += "    " + struct_def.name + ".Finish" + struct_def.name +
2081               "Buffer(fbb, " + struct_def.name + ".Pack(fbb, this));\n";
2082       code += "    return fbb.DataBuffer.ToSizedArray();\n";
2083       code += "  }\n";
2084     }
2085     code += "}\n\n";
2086   }
2087 
2088   // This tracks the current namespace used to determine if a type need to be
2089   // prefixed by its namespace
2090   const Namespace *cur_name_space_;
2091 };
2092 }  // namespace csharp
2093 
GenerateCSharp(const Parser & parser,const std::string & path,const std::string & file_name)2094 bool GenerateCSharp(const Parser &parser, const std::string &path,
2095                     const std::string &file_name) {
2096   csharp::CSharpGenerator generator(parser, path, file_name);
2097   return generator.generate();
2098 }
2099 
2100 }  // namespace flatbuffers
2101