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