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