[C++, C#, Java] Separated C# and Java generators into their own classes (#5618)
[platform/upstream/flatbuffers.git] / src / idl_gen_csharp.cpp
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // independent from idl_parser, since this code is not needed for most clients
18
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/flatbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
23
24 #if defined(FLATBUFFERS_CPP98_STL)
25 #  include <cctype>
26 #endif  // defined(FLATBUFFERS_CPP98_STL)
27
28 namespace flatbuffers {
29
30 static TypedFloatConstantGenerator CSharpFloatGen("Double.", "Single.", "NaN",
31                                                   "PositiveInfinity",
32                                                   "NegativeInfinity");
33 static CommentConfig comment_config = {
34   nullptr,
35   "///",
36   nullptr,
37 };
38
39 namespace csharp {
40 class CSharpGenerator : public BaseGenerator {
41  public:
42   CSharpGenerator(const Parser &parser, const std::string &path,
43                   const std::string &file_name)
44       : BaseGenerator(parser, path, file_name, "", "."),
45         cur_name_space_(nullptr) {}
46
47   CSharpGenerator &operator=(const CSharpGenerator &);
48
49   bool generate() {
50     std::string one_file_code;
51     cur_name_space_ = parser_.current_namespace_;
52
53     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
54          ++it) {
55       std::string enumcode;
56       auto &enum_def = **it;
57       if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
58       GenEnum(enum_def, &enumcode);
59       if (parser_.opts.one_file) {
60         one_file_code += enumcode;
61       } else {
62         if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
63                       false))
64           return false;
65       }
66     }
67
68     for (auto it = parser_.structs_.vec.begin();
69          it != parser_.structs_.vec.end(); ++it) {
70       std::string declcode;
71       auto &struct_def = **it;
72       if (!parser_.opts.one_file)
73         cur_name_space_ = struct_def.defined_namespace;
74       GenStruct(struct_def, &declcode);
75       if (parser_.opts.one_file) {
76         one_file_code += declcode;
77       } else {
78         if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
79                       true))
80           return false;
81       }
82     }
83
84     if (parser_.opts.one_file) {
85       return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
86                       true);
87     }
88     return true;
89   }
90
91   // Save out the generated code for a single class while adding
92   // declaration boilerplate.
93   bool SaveType(const std::string &defname, const Namespace &ns,
94                 const std::string &classcode, bool needs_includes) const {
95     if (!classcode.length()) return true;
96
97     std::string code =
98         "// <auto-generated>\n"
99         "//  " +
100         std::string(FlatBuffersGeneratedWarning()) +
101         "\n"
102         "// </auto-generated>\n\n";
103
104     std::string namespace_name = FullNamespace(".", ns);
105     if (!namespace_name.empty()) {
106       code += "namespace " + namespace_name + "\n{\n\n";
107     }
108     if (needs_includes) {
109       code += "using global::System;\nusing global::FlatBuffers;\n\n";
110     }
111     code += classcode;
112     if (!namespace_name.empty()) { code += "\n}\n"; }
113     auto filename = NamespaceDir(ns) + defname + ".cs";
114     return SaveFile(filename.c_str(), code, false);
115   }
116
117   const Namespace *CurrentNameSpace() const { return cur_name_space_; }
118
119   std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
120     // clang-format off
121     static const char * const csharp_typename[] = {
122     #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
123         CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
124         #NTYPE,
125       FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
126     #undef FLATBUFFERS_TD
127     };
128     // clang-format on
129
130     if (enableLangOverrides) {
131       if (IsEnum(type)) return WrapInNameSpace(*type.enum_def);
132       if (type.base_type == BASE_TYPE_STRUCT) {
133         return "Offset<" + WrapInNameSpace(*type.struct_def) + ">";
134       }
135     }
136
137     return csharp_typename[type.base_type];
138   }
139
140   inline std::string GenTypeBasic(const Type &type) const {
141     return GenTypeBasic(type, true);
142   }
143
144   std::string GenTypePointer(const Type &type) const {
145     switch (type.base_type) {
146       case BASE_TYPE_STRING: return "string";
147       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
148       case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
149       case BASE_TYPE_UNION: return "TTable";
150       default: return "Table";
151     }
152   }
153
154   std::string GenTypeGet(const Type &type) const {
155     return IsScalar(type.base_type)
156                ? GenTypeBasic(type)
157                : (IsArray(type) ? GenTypeGet(type.VectorType())
158                                 : GenTypePointer(type));
159   }
160
161   std::string GenOffsetType(const StructDef &struct_def) const {
162     return "Offset<" + WrapInNameSpace(struct_def) + ">";
163   }
164
165   std::string GenOffsetConstruct(const StructDef &struct_def,
166                                  const std::string &variable_name) const {
167     return "new Offset<" + WrapInNameSpace(struct_def) + ">(" + variable_name +
168            ")";
169   }
170
171   // Casts necessary to correctly read serialized data
172   std::string DestinationCast(const Type &type) const {
173     if (IsSeries(type)) {
174       return DestinationCast(type.VectorType());
175     } else {
176       if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")";
177     }
178     return "";
179   }
180
181   // Cast statements for mutator method parameters.
182   // In Java, parameters representing unsigned numbers need to be cast down to
183   // their respective type. For example, a long holding an unsigned int value
184   // would be cast down to int before being put onto the buffer. In C#, one cast
185   // directly cast an Enum to its underlying type, which is essential before
186   // putting it onto the buffer.
187   std::string SourceCast(const Type &type) const {
188     if (IsSeries(type)) {
189       return SourceCast(type.VectorType());
190     } else {
191       if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")";
192     }
193     return "";
194   }
195
196   std::string SourceCastBasic(const Type &type) const {
197     return IsScalar(type.base_type) ? SourceCast(type) : "";
198   }
199
200   std::string GenEnumDefaultValue(const FieldDef &field) const {
201     auto &value = field.value;
202     FLATBUFFERS_ASSERT(value.type.enum_def);
203     auto &enum_def = *value.type.enum_def;
204     auto enum_val = enum_def.FindByValue(value.constant);
205     return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
206                     : value.constant;
207   }
208
209   std::string GenDefaultValue(const FieldDef &field,
210                               bool enableLangOverrides) const {
211     auto &value = field.value;
212     if (enableLangOverrides) {
213       // handles both enum case and vector of enum case
214       if (value.type.enum_def != nullptr &&
215           value.type.base_type != BASE_TYPE_UNION) {
216         return GenEnumDefaultValue(field);
217       }
218     }
219
220     auto longSuffix = "";
221     switch (value.type.base_type) {
222       case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
223       case BASE_TYPE_ULONG: return value.constant;
224       case BASE_TYPE_UINT:
225       case BASE_TYPE_LONG: return value.constant + longSuffix;
226       default:
227         if (IsFloat(value.type.base_type))
228           return CSharpFloatGen.GenFloatConstant(field);
229         else
230           return value.constant;
231     }
232   }
233
234   std::string GenDefaultValue(const FieldDef &field) const {
235     return GenDefaultValue(field, true);
236   }
237
238   std::string GenDefaultValueBasic(const FieldDef &field,
239                                    bool enableLangOverrides) const {
240     auto &value = field.value;
241     if (!IsScalar(value.type.base_type)) {
242       if (enableLangOverrides) {
243         switch (value.type.base_type) {
244           case BASE_TYPE_STRING: return "default(StringOffset)";
245           case BASE_TYPE_STRUCT:
246             return "default(Offset<" + WrapInNameSpace(*value.type.struct_def) +
247                    ">)";
248           case BASE_TYPE_VECTOR: return "default(VectorOffset)";
249           default: break;
250         }
251       }
252       return "0";
253     }
254     return GenDefaultValue(field, enableLangOverrides);
255   }
256
257   std::string GenDefaultValueBasic(const FieldDef &field) const {
258     return GenDefaultValueBasic(field, true);
259   }
260
261   void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
262     std::string &code = *code_ptr;
263     if (enum_def.generated) return;
264
265     // Generate enum definitions of the form:
266     // public static (final) int name = value;
267     // In Java, we use ints rather than the Enum feature, because we want them
268     // to map directly to how they're used in C/C++ and file formats.
269     // That, and Java Enums are expensive, and not universally liked.
270     GenComment(enum_def.doc_comment, code_ptr, &comment_config);
271
272     // In C# this indicates enumeration values can be treated as bit flags.
273     if (enum_def.attributes.Lookup("bit_flags")) {
274       code += "[System.FlagsAttribute]\n";
275     }
276     if (enum_def.attributes.Lookup("private")) {
277       code += "internal ";
278     } else {
279       code += "public ";
280     }
281     code += "enum " + enum_def.name;
282     code += " : " + GenTypeBasic(enum_def.underlying_type, false);
283     code += "\n{\n";
284     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
285       auto &ev = **it;
286       GenComment(ev.doc_comment, code_ptr, &comment_config, "  ");
287       code += "  ";
288       code += ev.name + " = ";
289       code += enum_def.ToString(ev);
290       code += ",\n";
291     }
292     // Close the class
293     code += "};\n\n";
294   }
295
296   // Returns the function name that is able to read a value of the given type.
297   std::string GenGetter(const Type &type) const {
298     switch (type.base_type) {
299       case BASE_TYPE_STRING: return "__p.__string";
300       case BASE_TYPE_STRUCT: return "__p.__struct";
301       case BASE_TYPE_UNION: return "__p.__union";
302       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
303       case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
304       default: {
305         std::string getter = "__p.bb.Get";
306         if (type.base_type == BASE_TYPE_BOOL) {
307           getter = "0!=" + getter;
308         } else if (GenTypeBasic(type, false) != "byte") {
309           getter += MakeCamel(GenTypeBasic(type, false));
310         }
311         return getter;
312       }
313     }
314   }
315
316   // Returns the function name that is able to read a value of the given type.
317   std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
318                                       const std::string &data_buffer,
319                                       const char *num = nullptr) const {
320     auto type = key_field->value.type;
321     auto dest_mask = "";
322     auto dest_cast = DestinationCast(type);
323     auto getter = data_buffer + ".Get";
324     if (GenTypeBasic(type, false) != "byte") {
325       getter += MakeCamel(GenTypeBasic(type, false));
326     }
327     getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
328              dest_mask;
329     return getter;
330   }
331
332   // Direct mutation is only allowed for scalar fields.
333   // Hence a setter method will only be generated for such fields.
334   std::string GenSetter(const Type &type) const {
335     if (IsScalar(type.base_type)) {
336       std::string setter = "__p.bb.Put";
337       if (GenTypeBasic(type, false) != "byte" &&
338           type.base_type != BASE_TYPE_BOOL) {
339         setter += MakeCamel(GenTypeBasic(type, false));
340       }
341       return setter;
342     } else {
343       return "";
344     }
345   }
346
347   // Returns the method name for use with add/put calls.
348   std::string GenMethod(const Type &type) const {
349     return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
350                                     : (IsStruct(type) ? "Struct" : "Offset");
351   }
352
353   // Recursively generate arguments for a constructor, to deal with nested
354   // structs.
355   void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
356                      const char *nameprefix, size_t array_count = 0) const {
357     std::string &code = *code_ptr;
358     for (auto it = struct_def.fields.vec.begin();
359          it != struct_def.fields.vec.end(); ++it) {
360       auto &field = **it;
361       const auto &field_type = field.value.type;
362       const auto array_field = IsArray(field_type);
363       const auto &type = array_field ? field_type.VectorType() : field_type;
364       const auto array_cnt = array_field ? (array_count + 1) : array_count;
365       if (IsStruct(type)) {
366         // Generate arguments for a struct inside a struct. To ensure names
367         // don't clash, and to make it obvious these arguments are constructing
368         // a nested struct, prefix the name with the field name.
369         GenStructArgs(*field_type.struct_def, code_ptr,
370                       (nameprefix + (field.name + "_")).c_str(), array_cnt);
371       } else {
372         code += ", ";
373         code += GenTypeBasic(type);
374         if (array_cnt > 0) {
375           code += "[";
376           for (size_t i = 1; i < array_cnt; i++) code += ",";
377           code += "]";
378         }
379         code += " ";
380         code += nameprefix;
381         code += MakeCamel(field.name, true);
382       }
383     }
384   }
385
386   // Recusively generate struct construction statements of the form:
387   // builder.putType(name);
388   // and insert manual padding.
389   void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
390                      const char *nameprefix, size_t index = 0,
391                      bool in_array = false) const {
392     std::string &code = *code_ptr;
393     std::string indent((index + 1) * 2, ' ');
394     code += indent + "  builder.Prep(";
395     code += NumToString(struct_def.minalign) + ", ";
396     code += NumToString(struct_def.bytesize) + ");\n";
397     for (auto it = struct_def.fields.vec.rbegin();
398          it != struct_def.fields.vec.rend(); ++it) {
399       auto &field = **it;
400       const auto &field_type = field.value.type;
401       if (field.padding) {
402         code += indent + "  builder.Pad(";
403         code += NumToString(field.padding) + ");\n";
404       }
405       if (IsStruct(field_type)) {
406         GenStructBody(*field_type.struct_def, code_ptr,
407                       (nameprefix + (field.name + "_")).c_str(), index,
408                       in_array);
409       } else {
410         const auto &type =
411             IsArray(field_type) ? field_type.VectorType() : field_type;
412         const auto index_var = "_idx" + NumToString(index);
413         if (IsArray(field_type)) {
414           code += indent + "  for (int " + index_var + " = ";
415           code += NumToString(field_type.fixed_length);
416           code += "; " + index_var + " > 0; " + index_var + "--) {\n";
417           in_array = true;
418         }
419         if (IsStruct(type)) {
420           GenStructBody(*field_type.struct_def, code_ptr,
421                         (nameprefix + (field.name + "_")).c_str(), index + 1,
422                         in_array);
423         } else {
424           code += IsArray(field_type) ? "  " : "";
425           code += indent + "  builder.Put";
426           code += GenMethod(type) + "(";
427           code += SourceCast(type);
428           auto argname = nameprefix + MakeCamel(field.name, true);
429           code += argname;
430           size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
431           if (array_cnt > 0) {
432             code += "[";
433             for (size_t i = 0; in_array && i < array_cnt; i++) {
434               code += "_idx" + NumToString(i) + "-1";
435               if (i != (array_cnt - 1)) code += ",";
436             }
437             code += "]";
438           }
439           code += ");\n";
440         }
441         if (IsArray(field_type)) { code += indent + "  }\n"; }
442       }
443     }
444   }
445   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
446                               const char *num = nullptr) const {
447     std::string key_offset =
448         "Table.__offset(" + NumToString(key_field->value.offset) + ", ";
449     if (num) {
450       key_offset += num;
451       key_offset += ".Value, builder.DataBuffer)";
452     } else {
453       key_offset += "bb.Length";
454       key_offset += " - tableOffset, bb)";
455     }
456     return key_offset;
457   }
458
459   std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
460     std::string key_getter = "      ";
461     key_getter += "int tableOffset = Table.";
462     key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
463     key_getter += ", bb);\n      ";
464     if (key_field->value.type.base_type == BASE_TYPE_STRING) {
465       key_getter += "int comp = Table.";
466       key_getter += "CompareStrings(";
467       key_getter += GenOffsetGetter(key_field);
468       key_getter += ", byteKey, bb);\n";
469     } else {
470       auto get_val = GenGetterForLookupByKey(key_field, "bb");
471       key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
472     }
473     return key_getter;
474   }
475
476   std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
477     std::string key_getter = "";
478     auto data_buffer = "builder.DataBuffer";
479     if (key_field->value.type.base_type == BASE_TYPE_STRING) {
480       key_getter += "Table.CompareStrings(";
481       key_getter += GenOffsetGetter(key_field, "o1") + ", ";
482       key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
483     } else {
484       auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
485       key_getter += field_getter;
486       field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
487       key_getter += ".CompareTo(" + field_getter + ")";
488     }
489     return key_getter;
490   }
491
492   void GenStruct(StructDef &struct_def, std::string *code_ptr) const {
493     if (struct_def.generated) return;
494     std::string &code = *code_ptr;
495
496     // Generate a struct accessor class, with methods of the form:
497     // public type name() { return bb.getType(i + offset); }
498     // or for tables of the form:
499     // public type name() {
500     //   int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
501     // }
502     GenComment(struct_def.doc_comment, code_ptr, &comment_config);
503     if (struct_def.attributes.Lookup("private")) {
504       code += "internal ";
505     } else {
506       code += "public ";
507     }
508     if (struct_def.attributes.Lookup("csharp_partial")) {
509       // generate a partial class for this C# struct/table
510       code += "partial ";
511     }
512     code += "struct " + struct_def.name;
513     code += " : IFlatbufferObject";
514     code += "\n{\n";
515     code += "  private ";
516     code += struct_def.fixed ? "Struct" : "Table";
517     code += " __p;\n";
518
519     code += "  public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
520
521     if (!struct_def.fixed) {
522       // Generate verson check method.
523       // Force compile time error if not using the same version runtime.
524       code += "  public static void ValidateVersion() {";
525       code += " FlatBufferConstants.";
526       code += "FLATBUFFERS_1_11_1(); ";
527       code += "}\n";
528
529       // Generate a special accessor for the table that when used as the root
530       // of a FlatBuffer
531       std::string method_name = "GetRootAs" + struct_def.name;
532       std::string method_signature =
533           "  public static " + struct_def.name + " " + method_name;
534
535       // create convenience method that doesn't require an existing object
536       code += method_signature + "(ByteBuffer _bb) ";
537       code += "{ return " + method_name + "(_bb, new " + struct_def.name +
538               "()); }\n";
539
540       // create method that allows object reuse
541       code +=
542           method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
543       code += "return (obj.__assign(_bb.GetInt(_bb.Position";
544       code += ") + _bb.Position";
545       code += ", _bb)); }\n";
546       if (parser_.root_struct_def_ == &struct_def) {
547         if (parser_.file_identifier_.length()) {
548           // Check if a buffer has the identifier.
549           code += "  public static ";
550           code += "bool " + struct_def.name;
551           code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
552           code += "Table.__has_identifier(_bb, \"";
553           code += parser_.file_identifier_;
554           code += "\"); }\n";
555         }
556       }
557     }
558     // Generate the __init method that sets the field in a pre-existing
559     // accessor object. This is to allow object reuse.
560     code += "  public void __init(int _i, ByteBuffer _bb) ";
561     code += "{ ";
562     code += "__p = new ";
563     code += struct_def.fixed ? "Struct" : "Table";
564     code += "(_i, _bb); ";
565     code += "}\n";
566     code +=
567         "  public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
568     code += "{ __init(_i, _bb); return this; }\n\n";
569     for (auto it = struct_def.fields.vec.begin();
570          it != struct_def.fields.vec.end(); ++it) {
571       auto &field = **it;
572       if (field.deprecated) continue;
573       GenComment(field.doc_comment, code_ptr, &comment_config, "  ");
574       std::string type_name = GenTypeGet(field.value.type);
575       std::string type_name_dest = GenTypeGet(field.value.type);
576       std::string conditional_cast = "";
577       std::string optional = "";
578       if (!struct_def.fixed &&
579           (field.value.type.base_type == BASE_TYPE_STRUCT ||
580            field.value.type.base_type == BASE_TYPE_UNION ||
581            (field.value.type.base_type == BASE_TYPE_VECTOR &&
582             (field.value.type.element == BASE_TYPE_STRUCT ||
583              field.value.type.element == BASE_TYPE_UNION)))) {
584         optional = "?";
585         conditional_cast = "(" + type_name_dest + optional + ")";
586       }
587       std::string dest_mask = "";
588       std::string dest_cast = DestinationCast(field.value.type);
589       std::string src_cast = SourceCast(field.value.type);
590       std::string method_start = "  public " + type_name_dest + optional + " " +
591                                  MakeCamel(field.name, true);
592       std::string obj = "(new " + type_name + "())";
593
594       // Most field accessors need to retrieve and test the field offset first,
595       // this is the prefix code for that:
596       auto offset_prefix =
597           IsArray(field.value.type)
598               ? " { return "
599               : (" { int o = __p.__offset(" + NumToString(field.value.offset) +
600                  "); return o != 0 ? ");
601       // Generate the accessors that don't do object reuse.
602       if (field.value.type.base_type == BASE_TYPE_STRUCT) {
603       } else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
604                  field.value.type.element == BASE_TYPE_STRUCT) {
605       } else if (field.value.type.base_type == BASE_TYPE_UNION ||
606                  (field.value.type.base_type == BASE_TYPE_VECTOR &&
607                   field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
608         method_start += "<TTable>";
609         type_name = type_name_dest;
610       }
611       std::string getter = dest_cast + GenGetter(field.value.type);
612       code += method_start;
613       std::string default_cast = "";
614       // only create default casts for c# scalars or vectors of scalars
615       if ((IsScalar(field.value.type.base_type) ||
616            (field.value.type.base_type == BASE_TYPE_VECTOR &&
617             IsScalar(field.value.type.element)))) {
618         // For scalars, default value will be returned by GetDefaultValue().
619         // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
620         // that doesn't need to be casted. However, default values for enum
621         // elements of vectors are integer literals ("0") and are still casted
622         // for clarity.
623         if (field.value.type.enum_def == nullptr ||
624             field.value.type.base_type == BASE_TYPE_VECTOR) {
625           default_cast = "(" + type_name_dest + ")";
626         }
627       }
628       std::string member_suffix = "; ";
629       if (IsScalar(field.value.type.base_type)) {
630         code += " { get";
631         member_suffix += "} ";
632         if (struct_def.fixed) {
633           code += " { return " + getter;
634           code += "(__p.bb_pos + ";
635           code += NumToString(field.value.offset) + ")";
636           code += dest_mask;
637         } else {
638           code += offset_prefix + getter;
639           code += "(o + __p.bb_pos)" + dest_mask;
640           code += " : " + default_cast;
641           code += GenDefaultValue(field);
642         }
643       } else {
644         switch (field.value.type.base_type) {
645           case BASE_TYPE_STRUCT:
646             code += " { get";
647             member_suffix += "} ";
648             if (struct_def.fixed) {
649               code += " { return " + obj + ".__assign(" + "__p.";
650               code += "bb_pos + " + NumToString(field.value.offset) + ", ";
651               code += "__p.bb)";
652             } else {
653               code += offset_prefix + conditional_cast;
654               code += obj + ".__assign(";
655               code += field.value.type.struct_def->fixed
656                           ? "o + __p.bb_pos"
657                           : "__p.__indirect(o + __p.bb_pos)";
658               code += ", __p.bb) : null";
659             }
660             break;
661           case BASE_TYPE_STRING:
662             code += " { get";
663             member_suffix += "} ";
664             code += offset_prefix + getter + "(o + " + "__p.";
665             code += "bb_pos) : null";
666             break;
667           case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH();  // fall thru
668           case BASE_TYPE_VECTOR: {
669             auto vectortype = field.value.type.VectorType();
670             if (vectortype.base_type == BASE_TYPE_UNION) {
671               conditional_cast = "(TTable?)";
672               getter += "<TTable>";
673             }
674             code += "(";
675             if (vectortype.base_type == BASE_TYPE_STRUCT) {
676               getter = obj + ".__assign";
677             } else if (vectortype.base_type == BASE_TYPE_UNION) {
678             }
679             code += "int j)";
680             const auto body = offset_prefix + conditional_cast + getter + "(";
681             if (vectortype.base_type == BASE_TYPE_UNION) {
682               code += " where TTable : struct, IFlatbufferObject" + body;
683             } else {
684               code += body;
685             }
686             std::string index = "__p.";
687             if (IsArray(field.value.type)) {
688               index += "bb_pos + " + NumToString(field.value.offset) + " + ";
689             } else {
690               index += "__vector(o) + ";
691             }
692             index += "j * " + NumToString(InlineSize(vectortype));
693             if (vectortype.base_type == BASE_TYPE_STRUCT) {
694               code += vectortype.struct_def->fixed
695                           ? index
696                           : "__p.__indirect(" + index + ")";
697               code += ", __p.bb";
698             } else {
699               code += index;
700             }
701             code += ")" + dest_mask;
702             if (!IsArray(field.value.type)) {
703               code += " : ";
704               code +=
705                   field.value.type.element == BASE_TYPE_BOOL
706                       ? "false"
707                       : (IsScalar(field.value.type.element) ? default_cast + "0"
708                                                             : "null");
709             }
710
711             break;
712           }
713           case BASE_TYPE_UNION:
714             code += "() where TTable : struct, IFlatbufferObject";
715             code += offset_prefix + "(TTable?)" + getter;
716             code += "<TTable>(o + __p.bb_pos) : null";
717             break;
718           default: FLATBUFFERS_ASSERT(0);
719         }
720       }
721       code += member_suffix;
722       code += "}\n";
723       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
724         code += "  public int " + MakeCamel(field.name, true);
725         code += "Length";
726         code += " { get";
727         code += offset_prefix;
728         code += "__p.__vector_len(o) : 0; ";
729         code += "} ";
730         code += "}\n";
731         // See if we should generate a by-key accessor.
732         if (field.value.type.element == BASE_TYPE_STRUCT &&
733             !field.value.type.struct_def->fixed) {
734           auto &sd = *field.value.type.struct_def;
735           auto &fields = sd.fields.vec;
736           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
737             auto &key_field = **kit;
738             if (key_field.key) {
739               auto qualified_name = WrapInNameSpace(sd);
740               code += "  public " + qualified_name + "? ";
741               code += MakeCamel(field.name, true) + "ByKey(";
742               code += GenTypeGet(key_field.value.type) + " key)";
743               code += offset_prefix;
744               code += qualified_name + ".__lookup_by_key(";
745               code += "__p.__vector(o), key, ";
746               code += "__p.bb) : null; ";
747               code += "}\n";
748               break;
749             }
750           }
751         }
752       }
753       // Generate a ByteBuffer accessor for strings & vectors of scalars.
754       if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
755            IsScalar(field.value.type.VectorType().base_type)) ||
756           field.value.type.base_type == BASE_TYPE_STRING) {
757         code += "#if ENABLE_SPAN_T\n";
758         code += "  public Span<" + GenTypeBasic(field.value.type.VectorType()) +
759                 "> Get";
760         code += MakeCamel(field.name, true);
761         code += "Bytes() { return ";
762         code += "__p.__vector_as_span<" +
763                 GenTypeBasic(field.value.type.VectorType()) + ">(";
764         code += NumToString(field.value.offset);
765         code +=
766             ", " + NumToString(SizeOf(field.value.type.VectorType().base_type));
767         code += "); }\n";
768         code += "#else\n";
769         code += "  public ArraySegment<byte>? Get";
770         code += MakeCamel(field.name, true);
771         code += "Bytes() { return ";
772         code += "__p.__vector_as_arraysegment(";
773         code += NumToString(field.value.offset);
774         code += "); }\n";
775         code += "#endif\n";
776
777         // For direct blockcopying the data into a typed array
778         code += "  public ";
779         code += GenTypeBasic(field.value.type.VectorType());
780         code += "[] Get";
781         code += MakeCamel(field.name, true);
782         code += "Array() { ";
783         if (IsEnum(field.value.type.VectorType())) {
784           // Since __vector_as_array does not work for enum types,
785           // fill array using an explicit loop.
786           code += "int o = __p.__offset(";
787           code += NumToString(field.value.offset);
788           code += "); if (o == 0) return null; int p = ";
789           code += "__p.__vector(o); int l = ";
790           code += "__p.__vector_len(o); ";
791           code += GenTypeBasic(field.value.type.VectorType());
792           code += "[] a = new ";
793           code += GenTypeBasic(field.value.type.VectorType());
794           code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
795           code += "(p + i * ";
796           code += NumToString(InlineSize(field.value.type.VectorType()));
797           code += "); } return a;";
798         } else {
799           code += "return ";
800           code += "__p.__vector_as_array<";
801           code += GenTypeBasic(field.value.type.VectorType());
802           code += ">(";
803           code += NumToString(field.value.offset);
804           code += ");";
805         }
806         code += " }\n";
807       }
808       // generate object accessors if is nested_flatbuffer
809       if (field.nested_flatbuffer) {
810         auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
811         auto nested_method_name =
812             MakeCamel(field.name, true) + "As" + field.nested_flatbuffer->name;
813         auto get_nested_method_name = nested_method_name;
814         get_nested_method_name = "Get" + nested_method_name;
815         conditional_cast = "(" + nested_type_name + "?)";
816         obj = "(new " + nested_type_name + "())";
817         code += "  public " + nested_type_name + "? ";
818         code += get_nested_method_name + "(";
819         code += ") { int o = __p.__offset(";
820         code += NumToString(field.value.offset) + "); ";
821         code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
822         code += "__p.";
823         code += "__indirect(__p.__vector(o)), ";
824         code += "__p.bb) : null; }\n";
825       }
826       // Generate mutators for scalar fields or vectors of scalars.
827       if (parser_.opts.mutable_buffer) {
828         auto is_series = (IsSeries(field.value.type));
829         const auto &underlying_type =
830             is_series ? field.value.type.VectorType() : field.value.type;
831         // Boolean parameters have to be explicitly converted to byte
832         // representation.
833         auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
834                                     ? "(byte)(" + field.name + " ? 1 : 0)"
835                                     : field.name;
836         auto mutator_prefix = MakeCamel("mutate", true);
837         // A vector mutator also needs the index of the vector element it should
838         // mutate.
839         auto mutator_params = (is_series ? "(int j, " : "(") +
840                               GenTypeGet(underlying_type) + " " + field.name +
841                               ") { ";
842         auto setter_index =
843             is_series
844                 ? "__p." +
845                       (IsArray(field.value.type)
846                            ? "bb_pos + " + NumToString(field.value.offset)
847                            : "__vector(o)") +
848                       +" + j * " + NumToString(InlineSize(underlying_type))
849                 : (struct_def.fixed
850                        ? "__p.bb_pos + " + NumToString(field.value.offset)
851                        : "o + __p.bb_pos");
852         if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
853           code += "  public ";
854           code += struct_def.fixed ? "void " : "bool ";
855           code += mutator_prefix + MakeCamel(field.name, true);
856           code += mutator_params;
857           if (struct_def.fixed) {
858             code += GenSetter(underlying_type) + "(" + setter_index + ", ";
859             code += src_cast + setter_parameter + "); }\n";
860           } else {
861             code += "int o = __p.__offset(";
862             code += NumToString(field.value.offset) + ");";
863             code += " if (o != 0) { " + GenSetter(underlying_type);
864             code += "(" + setter_index + ", " + src_cast + setter_parameter +
865                     "); return true; } else { return false; } }\n";
866           }
867         }
868       }
869       if (parser_.opts.java_primitive_has_method &&
870           IsScalar(field.value.type.base_type) && !struct_def.fixed) {
871         auto vt_offset_constant = "  public static final int VT_" +
872                                   MakeScreamingCamel(field.name) + " = " +
873                                   NumToString(field.value.offset) + ";";
874
875         code += vt_offset_constant;
876         code += "\n";
877       }
878     }
879     code += "\n";
880     flatbuffers::FieldDef *key_field = nullptr;
881     if (struct_def.fixed) {
882       // create a struct constructor function
883       code += "  public static " + GenOffsetType(struct_def) + " ";
884       code += "Create";
885       code += struct_def.name + "(FlatBufferBuilder builder";
886       GenStructArgs(struct_def, code_ptr, "");
887       code += ") {\n";
888       GenStructBody(struct_def, code_ptr, "");
889       code += "    return ";
890       code += GenOffsetConstruct(struct_def, "builder.Offset");
891       code += ";\n  }\n";
892     } else {
893       // Generate a method that creates a table in one go. This is only possible
894       // when the table has no struct fields, since those have to be created
895       // inline, and there's no way to do so in Java.
896       bool has_no_struct_fields = true;
897       int num_fields = 0;
898       for (auto it = struct_def.fields.vec.begin();
899            it != struct_def.fields.vec.end(); ++it) {
900         auto &field = **it;
901         if (field.deprecated) continue;
902         if (IsStruct(field.value.type)) {
903           has_no_struct_fields = false;
904         } else {
905           num_fields++;
906         }
907       }
908       // JVM specifications restrict default constructor params to be < 255.
909       // Longs and doubles take up 2 units, so we set the limit to be < 127.
910       if (has_no_struct_fields && num_fields && num_fields < 127) {
911         // Generate a table constructor of the form:
912         // public static int createName(FlatBufferBuilder builder, args...)
913         code += "  public static " + GenOffsetType(struct_def) + " ";
914         code += "Create" + struct_def.name;
915         code += "(FlatBufferBuilder builder";
916         for (auto it = struct_def.fields.vec.begin();
917              it != struct_def.fields.vec.end(); ++it) {
918           auto &field = **it;
919           if (field.deprecated) continue;
920           code += ",\n      ";
921           code += GenTypeBasic(field.value.type);
922           code += " ";
923           code += field.name;
924           if (!IsScalar(field.value.type.base_type)) code += "Offset";
925
926           code += " = ";
927           code += GenDefaultValueBasic(field);
928         }
929         code += ") {\n    builder.";
930         code += "StartTable(";
931         code += NumToString(struct_def.fields.vec.size()) + ");\n";
932         for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
933              size; size /= 2) {
934           for (auto it = struct_def.fields.vec.rbegin();
935                it != struct_def.fields.vec.rend(); ++it) {
936             auto &field = **it;
937             if (!field.deprecated &&
938                 (!struct_def.sortbysize ||
939                  size == SizeOf(field.value.type.base_type))) {
940               code += "    " + struct_def.name + ".";
941               code += "Add";
942               code += MakeCamel(field.name) + "(builder, " + field.name;
943               if (!IsScalar(field.value.type.base_type)) code += "Offset";
944               code += ");\n";
945             }
946           }
947         }
948         code += "    return " + struct_def.name + ".";
949         code += "End" + struct_def.name;
950         code += "(builder);\n  }\n\n";
951       }
952       // Generate a set of static methods that allow table construction,
953       // of the form:
954       // public static void addName(FlatBufferBuilder builder, short name)
955       // { builder.addShort(id, name, default); }
956       // Unlike the Create function, these always work.
957       code += "  public static void Start";
958       code += struct_def.name;
959       code += "(FlatBufferBuilder builder) { builder.";
960       code += "StartTable(";
961       code += NumToString(struct_def.fields.vec.size()) + "); }\n";
962       for (auto it = struct_def.fields.vec.begin();
963            it != struct_def.fields.vec.end(); ++it) {
964         auto &field = **it;
965         if (field.deprecated) continue;
966         if (field.key) key_field = &field;
967         code += "  public static void Add";
968         code += MakeCamel(field.name);
969         code += "(FlatBufferBuilder builder, ";
970         code += GenTypeBasic(field.value.type);
971         auto argname = MakeCamel(field.name, false);
972         if (!IsScalar(field.value.type.base_type)) argname += "Offset";
973         code += " " + argname + ") { builder.Add";
974         code += GenMethod(field.value.type) + "(";
975         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
976         code += SourceCastBasic(field.value.type);
977         code += argname;
978         if (!IsScalar(field.value.type.base_type) &&
979             field.value.type.base_type != BASE_TYPE_UNION) {
980           code += ".Value";
981         }
982         code += ", ";
983         code += GenDefaultValue(field, false);
984         code += "); }\n";
985         if (field.value.type.base_type == BASE_TYPE_VECTOR) {
986           auto vector_type = field.value.type.VectorType();
987           auto alignment = InlineAlignment(vector_type);
988           auto elem_size = InlineSize(vector_type);
989           if (!IsStruct(vector_type)) {
990             code += "  public static VectorOffset ";
991             code += "Create";
992             code += MakeCamel(field.name);
993             code += "Vector(FlatBufferBuilder builder, ";
994             code += GenTypeBasic(vector_type) + "[] data) ";
995             code += "{ builder.StartVector(";
996             code += NumToString(elem_size);
997             code += ", data.Length, ";
998             code += NumToString(alignment);
999             code += "); for (int i = data.";
1000             code += "Length - 1; i >= 0; i--) builder.";
1001             code += "Add";
1002             code += GenMethod(vector_type);
1003             code += "(";
1004             code += SourceCastBasic(vector_type);
1005             code += "data[i]";
1006             if ((vector_type.base_type == BASE_TYPE_STRUCT ||
1007                  vector_type.base_type == BASE_TYPE_STRING))
1008               code += ".Value";
1009             code += "); return ";
1010             code += "builder.EndVector(); }\n";
1011
1012             code += "  public static VectorOffset ";
1013             code += "Create";
1014             code += MakeCamel(field.name);
1015             code += "VectorBlock(FlatBufferBuilder builder, ";
1016             code += GenTypeBasic(vector_type) + "[] data) ";
1017             code += "{ builder.StartVector(";
1018             code += NumToString(elem_size);
1019             code += ", data.Length, ";
1020             code += NumToString(alignment);
1021             code += "); builder.Add(data); return builder.EndVector(); }\n";
1022           }
1023           // Generate a method to start a vector, data to be added manually
1024           // after.
1025           code += "  public static void Start";
1026           code += MakeCamel(field.name);
1027           code += "Vector(FlatBufferBuilder builder, int numElems) ";
1028           code += "{ builder.StartVector(";
1029           code += NumToString(elem_size);
1030           code += ", numElems, " + NumToString(alignment);
1031           code += "); }\n";
1032         }
1033       }
1034       code += "  public static " + GenOffsetType(struct_def) + " ";
1035       code += "End" + struct_def.name;
1036       code += "(FlatBufferBuilder builder) {\n    int o = builder.";
1037       code += "EndTable();\n";
1038       for (auto it = struct_def.fields.vec.begin();
1039            it != struct_def.fields.vec.end(); ++it) {
1040         auto &field = **it;
1041         if (!field.deprecated && field.required) {
1042           code += "    builder.Required(o, ";
1043           code += NumToString(field.value.offset);
1044           code += ");  // " + field.name + "\n";
1045         }
1046       }
1047       code += "    return " + GenOffsetConstruct(struct_def, "o") + ";\n  }\n";
1048       if (parser_.root_struct_def_ == &struct_def) {
1049         std::string size_prefix[] = { "", "SizePrefixed" };
1050         for (int i = 0; i < 2; ++i) {
1051           code += "  public static void ";
1052           code += "Finish" + size_prefix[i] + struct_def.name;
1053           code +=
1054               "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1055           code += " offset) {";
1056           code += " builder.Finish" + size_prefix[i] + "(offset";
1057           code += ".Value";
1058
1059           if (parser_.file_identifier_.length())
1060             code += ", \"" + parser_.file_identifier_ + "\"";
1061           code += "); }\n";
1062         }
1063       }
1064     }
1065     // Only generate key compare function for table,
1066     // because `key_field` is not set for struct
1067     if (struct_def.has_key && !struct_def.fixed) {
1068       FLATBUFFERS_ASSERT(key_field);
1069       code += "\n  public static VectorOffset ";
1070       code += "CreateSortedVectorOf" + struct_def.name;
1071       code += "(FlatBufferBuilder builder, ";
1072       code += "Offset<" + struct_def.name + ">";
1073       code += "[] offsets) {\n";
1074       code += "    Array.Sort(offsets, (Offset<" + struct_def.name +
1075               "> o1, Offset<" + struct_def.name + "> o2) => " +
1076               GenKeyGetter(key_field);
1077       code += ");\n";
1078       code += "    return builder.CreateVectorOfTables(offsets);\n  }\n";
1079
1080       code += "\n  public static " + struct_def.name + "?";
1081       code += " __lookup_by_key(";
1082       code += "int vectorLocation, ";
1083       code += GenTypeGet(key_field->value.type);
1084       code += " key, ByteBuffer bb) {\n";
1085       if (key_field->value.type.base_type == BASE_TYPE_STRING) {
1086         code += "    byte[] byteKey = ";
1087         code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
1088       }
1089       code += "    int span = ";
1090       code += "bb.GetInt(vectorLocation - 4);\n";
1091       code += "    int start = 0;\n";
1092       code += "    while (span != 0) {\n";
1093       code += "      int middle = span / 2;\n";
1094       code += GenLookupKeyGetter(key_field);
1095       code += "      if (comp > 0) {\n";
1096       code += "        span = middle;\n";
1097       code += "      } else if (comp < 0) {\n";
1098       code += "        middle++;\n";
1099       code += "        start += middle;\n";
1100       code += "        span -= middle;\n";
1101       code += "      } else {\n";
1102       code += "        return ";
1103       code += "new " + struct_def.name + "()";
1104       code += ".__assign(tableOffset, bb);\n";
1105       code += "      }\n    }\n";
1106       code += "    return null;\n";
1107       code += "  }\n";
1108     }
1109     code += "};\n\n";
1110   }
1111
1112   void GenVectorAccessObject(StructDef &struct_def,
1113                              std::string *code_ptr) const {
1114     auto &code = *code_ptr;
1115     // Generate a vector of structs accessor class.
1116     code += "\n";
1117     code += "  ";
1118     if (!struct_def.attributes.Lookup("private")) code += "public ";
1119     code += "static struct Vector : BaseVector\n{\n";
1120
1121     // Generate the __assign method that sets the field in a pre-existing
1122     // accessor object. This is to allow object reuse.
1123     std::string method_indent = "    ";
1124     code += method_indent + "public Vector ";
1125     code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1126     code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1127
1128     auto type_name = struct_def.name;
1129     auto method_start = method_indent + "public " + type_name + " Get";
1130     // Generate the accessors that don't do object reuse.
1131     code += method_start + "(int j) { return Get";
1132     code += "(new " + type_name + "(), j); }\n";
1133     code += method_start + "(" + type_name + " obj, int j) { ";
1134     code += " return obj.__assign(";
1135     code += struct_def.fixed ? "__p.__element(j)"
1136                              : "__p.__indirect(__p.__element(j), bb)";
1137     code += ", __p.bb); }\n";
1138     // See if we should generate a by-key accessor.
1139     if (!struct_def.fixed) {
1140       auto &fields = struct_def.fields.vec;
1141       for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1142         auto &key_field = **kit;
1143         if (key_field.key) {
1144           auto nullable_annotation =
1145               parser_.opts.gen_nullable ? "@Nullable " : "";
1146           code += method_indent + nullable_annotation;
1147           code += "public " + type_name + "? ";
1148           code += "GetByKey(";
1149           code += GenTypeGet(key_field.value.type) + " key) { ";
1150           code += " return __lookup_by_key(null, ";
1151           code += "__p.__vector(), key, ";
1152           code += "__p.bb); ";
1153           code += "}\n";
1154           code += method_indent + nullable_annotation;
1155           code += "public " + type_name + "?" + " ";
1156           code += "GetByKey(";
1157           code += type_name + "? obj, ";
1158           code += GenTypeGet(key_field.value.type) + " key) { ";
1159           code += " return __lookup_by_key(obj, ";
1160           code += "__p.__vector(), key, ";
1161           code += "__p.bb); ";
1162           code += "}\n";
1163           break;
1164         }
1165       }
1166     }
1167     code += "  }\n";
1168   }
1169
1170   // This tracks the current namespace used to determine if a type need to be
1171   // prefixed by its namespace
1172   const Namespace *cur_name_space_;
1173 };
1174 }  // namespace csharp
1175
1176 bool GenerateCSharp(const Parser &parser, const std::string &path,
1177                     const std::string &file_name) {
1178   csharp::CSharpGenerator generator(parser, path, file_name);
1179   return generator.generate();
1180 }
1181
1182 }  // namespace flatbuffers