b97d4ea2a1c1ca2b46ba97939dbbe5eb43de7b4c
[platform/upstream/flatbuffers.git] / src / idl_gen_dart.cpp
1 /*
2  * Copyright 2018 Dan Field
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 #include <cassert>
19
20 #include "flatbuffers/code_generators.h"
21 #include "flatbuffers/flatbuffers.h"
22 #include "flatbuffers/idl.h"
23 #include "flatbuffers/util.h"
24
25 namespace flatbuffers {
26
27 static std::string GeneratedFileName(const std::string &path,
28                                      const std::string &file_name) {
29   return path + file_name + "_generated.dart";
30 }
31
32 namespace dart {
33
34 const std::string _kFb = "fb";
35 // see https://www.dartlang.org/guides/language/language-tour#keywords
36 // yeild*, async*, and sync* shouldn't be problems anyway but keeping them in
37 static const char *keywords[] = {
38   "abstract",   "deferred", "if",       "super",   "as",       "do",
39   "implements", "switch",   "assert",   "dynamic", "import",   "sync*",
40   "async",      "else",     "in",       "this",    "async*",   "enum",
41   "is",         "throw",    "await",    "export",  "library",  "true",
42   "break",      "external", "new",      "try",     "case",     "extends",
43   "null",       "typedef",  "catch",    "factory", "operator", "var",
44   "class",      "false",    "part",     "void",    "const",    "final",
45   "rethrow",    "while",    "continue", "finally", "return",   "with",
46   "covariant",  "for",      "set",      "yield",   "default",  "get",
47   "static",     "yield*"
48 };
49
50 // Iterate through all definitions we haven't generate code for (enums, structs,
51 // and tables) and output them to a single file.
52 class DartGenerator : public BaseGenerator {
53  public:
54   typedef std::map<std::string, std::string> namespace_code_map;
55
56   DartGenerator(const Parser &parser, const std::string &path,
57                 const std::string &file_name)
58       : BaseGenerator(parser, path, file_name, "", ".") {}
59   // Iterate through all definitions we haven't generate code for (enums,
60   // structs, and tables) and output them to a single file.
61   bool generate() {
62     std::string code;
63     namespace_code_map namespace_code;
64     GenerateEnums(&namespace_code);
65     GenerateStructs(&namespace_code);
66
67     for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) {
68       code.clear();
69       code = code + "// " + FlatBuffersGeneratedWarning() + "\n";
70       code = code +
71              "// ignore_for_file: unused_import, unused_field, "
72              "unused_local_variable\n\n";
73
74       if (!kv->first.empty()) {
75         code += "library " + kv->first + ";\n\n";
76       }
77
78       code += "import 'dart:typed_data' show Uint8List;\n";
79       code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb +
80               ";\n\n";
81
82       if (parser_.opts.include_dependence_headers) {
83         GenIncludeDependencies(&code, kv->first);
84       }
85
86       for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end();
87            ++kv2) {
88         if (kv2->first != kv->first) {
89           code += "import '" +
90                   GeneratedFileName("./", file_name_ + (!kv2->first.empty() ? "_" + kv2->first : "")) +
91                   "' as " + ImportAliasName(kv2->first) + ";\n";
92         }
93       }
94       code += "\n";
95       code += kv->second;
96
97       if (!SaveFile(
98               GeneratedFileName(path_, file_name_ + (!kv->first.empty() ? "_" + kv->first : "")).c_str(),
99               code, false)) {
100         return false;
101       }
102     }
103     return true;
104   }
105
106  private:
107   static std::string ImportAliasName(const std::string &ns) {
108     std::string ret;
109     ret.assign(ns);
110     size_t pos = ret.find('.');
111     while (pos != std::string::npos) {
112       ret.replace(pos, 1, "_");
113       pos = ret.find('.', pos + 1);
114     }
115
116     return ret;
117   }
118
119   static std::string BuildNamespaceName(const Namespace &ns) {
120     if (ns.components.empty()) {
121       return "";
122     }
123     std::stringstream sstream;
124     std::copy(ns.components.begin(), ns.components.end() - 1,
125               std::ostream_iterator<std::string>(sstream, "."));
126
127     auto ret = sstream.str() + ns.components.back();
128     for (size_t i = 0; i < ret.size(); i++) {
129       auto lower = tolower(ret[i]);
130       if (lower != ret[i]) {
131         ret[i] = static_cast<char>(lower);
132         if (i != 0 && ret[i - 1] != '.') {
133           ret.insert(i, "_");
134           i++;
135         }
136       }
137     }
138     // std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
139     return ret;
140   }
141
142   void GenIncludeDependencies(std::string* code, const std::string& the_namespace) {
143     for (auto it = parser_.included_files_.begin();
144          it != parser_.included_files_.end(); ++it) {
145       if (it->second.empty()) continue;
146
147       auto noext = flatbuffers::StripExtension(it->second);
148       auto basename = flatbuffers::StripPath(noext);
149
150       *code += "import '" + GeneratedFileName("", basename + (the_namespace == "" ? "" : "_" + the_namespace)) + "';\n";
151     }
152   }
153
154   static std::string EscapeKeyword(const std::string &name) {
155     for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
156       if (name == keywords[i]) { return MakeCamel(name + "_", false); }
157     }
158
159     return MakeCamel(name, false);
160   }
161
162   void GenerateEnums(namespace_code_map *namespace_code) {
163     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
164          ++it) {
165       auto &enum_def = **it;
166       GenEnum(enum_def, namespace_code);  // enum_code_ptr);
167     }
168   }
169
170   void GenerateStructs(namespace_code_map *namespace_code) {
171     for (auto it = parser_.structs_.vec.begin();
172          it != parser_.structs_.vec.end(); ++it) {
173       auto &struct_def = **it;
174       GenStruct(struct_def, namespace_code);
175     }
176   }
177
178   // Generate a documentation comment, if available.
179   static void GenDocComment(const std::vector<std::string> &dc,
180                             std::string *code_ptr,
181                             const std::string &extra_lines,
182                             const char *indent = nullptr) {
183     if (dc.empty() && extra_lines.empty()) {
184       // Don't output empty comment blocks with 0 lines of comment content.
185       return;
186     }
187
188     auto &code = *code_ptr;
189
190     for (auto it = dc.begin(); it != dc.end(); ++it) {
191       if (indent) code += indent;
192       code += "/// " + *it + "\n";
193     }
194     if (!extra_lines.empty()) {
195       if (!dc.empty()) {
196         if (indent) code += indent;
197         code += "///\n";
198       }
199       if (indent) code += indent;
200       std::string::size_type start = 0;
201       for (;;) {
202         auto end = extra_lines.find('\n', start);
203         if (end != std::string::npos) {
204           code += "/// " + extra_lines.substr(start, end - start) + "\n";
205           start = end + 1;
206         } else {
207           code += "/// " + extra_lines.substr(start) + "\n";
208           break;
209         }
210       }
211     }
212   }
213
214   static void GenDocComment(std::string *code_ptr,
215                             const std::string &extra_lines) {
216     GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
217   }
218
219   // Generate an enum declaration and an enum string lookup table.
220   void GenEnum(EnumDef &enum_def, namespace_code_map *namespace_code) {
221     if (enum_def.generated) return;
222     auto ns = BuildNamespaceName(*enum_def.defined_namespace);
223     std::string code;
224     GenDocComment(enum_def.doc_comment, &code, "");
225
226     auto name = enum_def.is_union ? enum_def.name + "TypeId" : enum_def.name;
227     auto is_bit_flags = enum_def.attributes.Lookup("bit_flags");
228
229     code += "class " + name + " {\n";
230     code += "  final int value;\n";
231     code += "  const " + name + "._(this.value);\n\n";
232     code += "  factory " + name + ".fromValue(int value) {\n";
233     code += "    if (value == null) value = 0;\n";
234
235     code += "    if (!values.containsKey(value)) {\n";
236     code +=
237         "      throw new StateError('Invalid value $value for bit flag enum ";
238     code += name + "');\n";
239     code += "    }\n";
240
241     code += "    return values[value];\n";
242     code += "  }\n\n";
243
244     // this is meaningless for bit_flags
245     // however, note that unlike "regular" dart enums this enum can still have
246     // holes.
247     if (!is_bit_flags) {
248       code += "  static const int minValue = " +
249               enum_def.ToString(*enum_def.MinValue()) + ";\n";
250       code += "  static const int maxValue = " +
251               enum_def.ToString(*enum_def.MaxValue()) + ";\n";
252     }
253
254     code +=
255         "  static bool containsValue(int value) =>"
256         " values.containsKey(value);\n\n";
257
258     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
259       auto &ev = **it;
260
261       if (!ev.doc_comment.empty()) {
262         if (it != enum_def.Vals().begin()) { code += '\n'; }
263         GenDocComment(ev.doc_comment, &code, "", "  ");
264       }
265       code += "  static const " + name + " " + ev.name + " = ";
266       code += "const " + name + "._(" + enum_def.ToString(ev) + ");\n";
267     }
268
269     code += "  static get values => {";
270     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
271       auto &ev = **it;
272       code += enum_def.ToString(ev) + ": " + ev.name + ",";
273     }
274     code += "};\n\n";
275
276     code += "  static const " + _kFb + ".Reader<" + name +
277             "> reader = const _" + name + "Reader();\n\n";
278     code += "  @override\n";
279     code += "  String toString() {\n";
280     code += "    return '" + name + "{value: $value}';\n";
281     code += "  }\n";
282     code += "}\n\n";
283
284     GenEnumReader(enum_def, name, &code);
285     (*namespace_code)[ns] += code;
286   }
287
288   void GenEnumReader(EnumDef &enum_def, const std::string &name,
289                      std::string *code_ptr) {
290     auto &code = *code_ptr;
291
292     code += "class _" + name + "Reader extends " + _kFb + ".Reader<" + name +
293             "> {\n";
294     code += "  const _" + name + "Reader();\n\n";
295     code += "  @override\n";
296     code += "  int get size => 1;\n\n";
297     code += "  @override\n";
298     code +=
299         "  " + name + " read(" + _kFb + ".BufferContext bc, int offset) =>\n";
300     code += "      new " + name + ".fromValue(const " + _kFb + "." +
301             GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n";
302     code += "}\n\n";
303   }
304
305   static std::string GenType(const Type &type) {
306     switch (type.base_type) {
307       case BASE_TYPE_BOOL: return "Bool";
308       case BASE_TYPE_CHAR: return "Int8";
309       case BASE_TYPE_UTYPE:
310       case BASE_TYPE_UCHAR: return "Uint8";
311       case BASE_TYPE_SHORT: return "Int16";
312       case BASE_TYPE_USHORT: return "Uint16";
313       case BASE_TYPE_INT: return "Int32";
314       case BASE_TYPE_UINT: return "Uint32";
315       case BASE_TYPE_LONG: return "Int64";
316       case BASE_TYPE_ULONG: return "Uint64";
317       case BASE_TYPE_FLOAT: return "Float32";
318       case BASE_TYPE_DOUBLE: return "Float64";
319       case BASE_TYPE_STRING: return "String";
320       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
321       case BASE_TYPE_STRUCT: return type.struct_def->name;
322       case BASE_TYPE_UNION: return type.enum_def->name + "TypeId";
323       default: return "Table";
324     }
325   }
326
327   std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
328                                 const FieldDef &def,
329                                 bool parent_is_vector = false) {
330     if (type.base_type == BASE_TYPE_BOOL) {
331       return "const " + _kFb + ".BoolReader()";
332     } else if (type.base_type == BASE_TYPE_VECTOR) {
333       return "const " + _kFb + ".ListReader<" +
334              GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" +
335              GenReaderTypeName(type.VectorType(), current_namespace, def,
336                                true) +
337              ")";
338     } else if (type.base_type == BASE_TYPE_STRING) {
339       return "const " + _kFb + ".StringReader()";
340     }
341     if (IsScalar(type.base_type)) {
342       if (type.enum_def && parent_is_vector) {
343         return GenDartTypeName(type, current_namespace, def) + ".reader";
344       }
345       return "const " + _kFb + "." + GenType(type) + "Reader()";
346     } else {
347       return GenDartTypeName(type, current_namespace, def) + ".reader";
348     }
349   }
350
351   std::string GenDartTypeName(const Type &type, Namespace *current_namespace,
352                               const FieldDef &def, bool addBuilder = false) {
353     if (type.enum_def) {
354       if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) {
355         return type.enum_def->name + "TypeId";
356       } else if (type.enum_def->is_union) {
357         return "dynamic";
358       } else if (type.base_type != BASE_TYPE_VECTOR) {
359         return type.enum_def->name;
360       }
361     }
362
363     switch (type.base_type) {
364       case BASE_TYPE_BOOL: return "bool";
365       case BASE_TYPE_LONG:
366       case BASE_TYPE_ULONG:
367       case BASE_TYPE_INT:
368       case BASE_TYPE_UINT:
369       case BASE_TYPE_SHORT:
370       case BASE_TYPE_USHORT:
371       case BASE_TYPE_CHAR:
372       case BASE_TYPE_UCHAR: return "int";
373       case BASE_TYPE_FLOAT:
374       case BASE_TYPE_DOUBLE: return "double";
375       case BASE_TYPE_STRING: return "String";
376       case BASE_TYPE_STRUCT:
377         return MaybeWrapNamespace(
378             type.struct_def->name + (addBuilder ? "ObjectBuilder" : ""),
379             current_namespace, def);
380       case BASE_TYPE_VECTOR:
381         return "List<" +
382                GenDartTypeName(type.VectorType(), current_namespace, def,
383                                addBuilder) +
384                ">";
385       default: assert(0); return "dynamic";
386     }
387   }
388
389   static const std::string MaybeWrapNamespace(const std::string &type_name,
390                                               Namespace *current_ns,
391                                               const FieldDef &field) {
392     auto curr_ns_str = BuildNamespaceName(*current_ns);
393     std::string field_ns_str = "";
394     if (field.value.type.struct_def) {
395       field_ns_str +=
396           BuildNamespaceName(*field.value.type.struct_def->defined_namespace);
397     } else if (field.value.type.enum_def) {
398       field_ns_str +=
399           BuildNamespaceName(*field.value.type.enum_def->defined_namespace);
400     }
401
402     if (field_ns_str != "" && field_ns_str != curr_ns_str) {
403       return ImportAliasName(field_ns_str) + "." + type_name;
404     } else {
405       return type_name;
406     }
407   }
408
409   // Generate an accessor struct with constructor for a flatbuffers struct.
410   void GenStruct(const StructDef &struct_def,
411                  namespace_code_map *namespace_code) {
412     if (struct_def.generated) return;
413
414     auto object_namespace = BuildNamespaceName(*struct_def.defined_namespace);
415     std::string code;
416
417     const auto &object_name = struct_def.name;
418
419     // Emit constructor
420
421     GenDocComment(struct_def.doc_comment, &code, "");
422
423     auto reader_name = "_" + object_name + "Reader";
424     auto builder_name = object_name + "Builder";
425     auto object_builder_name = object_name + "ObjectBuilder";
426
427     std::string reader_code, builder_code;
428
429     code += "class " + object_name + " {\n";
430
431     code += "  " + object_name + "._(this._bc, this._bcOffset);\n";
432     if (!struct_def.fixed) {
433       code += "  factory " + object_name + "(List<int> bytes) {\n";
434       code += "    " + _kFb + ".BufferContext rootRef = new " + _kFb +
435               ".BufferContext.fromBytes(bytes);\n";
436       code += "    return reader.read(rootRef, 0);\n";
437       code += "  }\n";
438     }
439
440     code += "\n";
441     code += "  static const " + _kFb + ".Reader<" + object_name +
442             "> reader = const " + reader_name + "();\n\n";
443
444     code += "  final " + _kFb + ".BufferContext _bc;\n";
445     code += "  final int _bcOffset;\n\n";
446
447     GenImplementationGetters(struct_def, &code);
448
449     code += "}\n\n";
450
451     GenReader(struct_def, &reader_name, &reader_code);
452     GenBuilder(struct_def, &builder_name, &builder_code);
453     GenObjectBuilder(struct_def, &object_builder_name, &builder_code);
454
455     code += reader_code;
456     code += builder_code;
457
458     (*namespace_code)[object_namespace] += code;
459   }
460
461   std::string NamespaceAliasFromUnionType(const std::string &in) {
462     if (in.find('_') == std::string::npos) { return in; }
463
464     std::stringstream ss(in);
465     std::string item;
466     std::vector<std::string> parts;
467     std::string ns;
468
469     while (std::getline(ss, item, '_')) { parts.push_back(item); }
470
471     for (auto it = parts.begin(); it != parts.end() - 1; ++it) {
472       auto &part = *it;
473
474       for (size_t i = 0; i < part.length(); i++) {
475         if (i && !isdigit(part[i]) &&
476             part[i] == static_cast<char>(toupper(part[i]))) {
477           ns += "_";
478           ns += static_cast<char>(tolower(part[i]));
479         } else {
480           ns += static_cast<char>(tolower(part[i]));
481         }
482       }
483       if (it != parts.end() - 2) { ns += "_"; }
484     }
485
486     return ns + "." + parts.back();
487   }
488
489   void GenImplementationGetters(const StructDef &struct_def,
490                                 std::string *code_ptr) {
491     auto &code = *code_ptr;
492
493     for (auto it = struct_def.fields.vec.begin();
494          it != struct_def.fields.vec.end(); ++it) {
495       auto &field = **it;
496       if (field.deprecated) continue;
497
498       std::string field_name = MakeCamel(field.name, false);
499       std::string type_name = GenDartTypeName(
500           field.value.type, struct_def.defined_namespace, field, false);
501
502       GenDocComment(field.doc_comment, &code, "", "  ");
503
504       code += "  " + type_name + " get " + field_name;
505       if (field.value.type.base_type == BASE_TYPE_UNION) {
506         code += " {\n";
507         code += "    switch (" + field_name + "Type?.value) {\n";
508         auto &enum_def = *field.value.type.enum_def;
509         for (auto en_it = enum_def.Vals().begin() + 1;
510              en_it != enum_def.Vals().end(); ++en_it) {
511           auto &ev = **en_it;
512
513           auto enum_name = NamespaceAliasFromUnionType(ev.name);
514           code += "      case " + enum_def.ToString(ev) + ": return " +
515                   enum_name + ".reader.vTableGet(_bc, _bcOffset, " +
516                   NumToString(field.value.offset) + ", null);\n";
517         }
518         code += "      default: return null;\n";
519         code += "    }\n";
520         code += "  }\n";
521       } else {
522         code += " => ";
523         if (field.value.type.enum_def &&
524             field.value.type.base_type != BASE_TYPE_VECTOR) {
525           code += "new " +
526                   GenDartTypeName(field.value.type,
527                                   struct_def.defined_namespace, field) +
528                   ".fromValue(";
529         }
530
531         code += GenReaderTypeName(field.value.type,
532                                   struct_def.defined_namespace, field);
533         if (struct_def.fixed) {
534           code +=
535               ".read(_bc, _bcOffset + " + NumToString(field.value.offset) + ")";
536         } else {
537           code += ".vTableGet(_bc, _bcOffset, " +
538                   NumToString(field.value.offset) + ", ";
539           if (!field.value.constant.empty() && field.value.constant != "0") {
540             if (IsBool(field.value.type.base_type)) {
541               code += "true";
542             } else {
543               code += field.value.constant;
544             }
545           } else {
546             if (IsBool(field.value.type.base_type)) {
547               code += "false";
548             } else if (IsScalar(field.value.type.base_type)) {
549               code += "0";
550             } else {
551               code += "null";
552             }
553           }
554           code += ")";
555         }
556         if (field.value.type.enum_def &&
557             field.value.type.base_type != BASE_TYPE_VECTOR) {
558           code += ")";
559         }
560         code += ";\n";
561       }
562     }
563
564     code += "\n";
565
566     code += "  @override\n";
567     code += "  String toString() {\n";
568     code += "    return '" + struct_def.name + "{";
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       code +=
574           MakeCamel(field.name, false) + ": $" + MakeCamel(field.name, false);
575       if (it != struct_def.fields.vec.end() - 1) { code += ", "; }
576     }
577     code += "}';\n";
578     code += "  }\n";
579   }
580
581   void GenReader(const StructDef &struct_def, std::string *reader_name_ptr,
582                  std::string *code_ptr) {
583     auto &code = *code_ptr;
584     auto &reader_name = *reader_name_ptr;
585     auto &impl_name = struct_def.name;
586
587     code += "class " + reader_name + " extends " + _kFb;
588     if (struct_def.fixed) {
589       code += ".StructReader<";
590     } else {
591       code += ".TableReader<";
592     }
593     code += impl_name + "> {\n";
594     code += "  const " + reader_name + "();\n\n";
595
596     if (struct_def.fixed) {
597       code += "  @override\n";
598       code += "  int get size => " + NumToString(struct_def.bytesize) + ";\n\n";
599     }
600     code += "  @override\n";
601     code += "  " + impl_name +
602             " createObject(fb.BufferContext bc, int offset) => \n    new " +
603             impl_name + "._(bc, offset);\n";
604     code += "}\n\n";
605   }
606
607   void GenBuilder(const StructDef &struct_def, std::string *builder_name_ptr,
608                   std::string *code_ptr) {
609     if (struct_def.fields.vec.size() == 0) { return; }
610     auto &code = *code_ptr;
611     auto &builder_name = *builder_name_ptr;
612
613     code += "class " + builder_name + " {\n";
614     code += "  " + builder_name + "(this.fbBuilder) {\n";
615     code += "    assert(fbBuilder != null);\n";
616     code += "  }\n\n";
617     code += "  final " + _kFb + ".Builder fbBuilder;\n\n";
618
619     if (struct_def.fixed) {
620       StructBuilderBody(struct_def, code_ptr);
621     } else {
622       TableBuilderBody(struct_def, code_ptr);
623     }
624
625     code += "}\n\n";
626   }
627
628   void StructBuilderBody(const StructDef &struct_def, std::string *code_ptr) {
629     auto &code = *code_ptr;
630
631     code += "  int finish(";
632     for (auto it = struct_def.fields.vec.begin();
633          it != struct_def.fields.vec.end(); ++it) {
634       auto &field = **it;
635       if (field.deprecated) continue;
636
637       if (IsStruct(field.value.type)) {
638         code += "fb.StructBuilder";
639       } else {
640         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
641                                 field);
642       }
643       code += " " + field.name;
644       if (it != struct_def.fields.vec.end() - 1) { code += ", "; }
645     }
646     code += ") {\n";
647
648     for (auto it = struct_def.fields.vec.rbegin();
649          it != struct_def.fields.vec.rend(); ++it) {
650       auto &field = **it;
651
652       if (field.deprecated) continue;
653
654       if (field.padding) {
655         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
656       }
657
658       if (IsStruct(field.value.type)) {
659         code += "    " + field.name + "();\n";
660       } else {
661         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
662         code += field.name;
663         if (field.value.type.enum_def) { code += "?.value"; }
664         code += ");\n";
665       }
666     }
667     code += "    return fbBuilder.offset;\n";
668     code += "  }\n\n";
669   }
670
671   void TableBuilderBody(const StructDef &struct_def, std::string *code_ptr) {
672     auto &code = *code_ptr;
673
674     code += "  void begin() {\n";
675     code += "    fbBuilder.startTable();\n";
676     code += "  }\n\n";
677
678     for (auto it = struct_def.fields.vec.begin();
679          it != struct_def.fields.vec.end(); ++it) {
680       auto &field = **it;
681       if (field.deprecated) continue;
682
683       auto offset = it - struct_def.fields.vec.begin();
684
685       if (IsScalar(field.value.type.base_type)) {
686         code += "  int add" + MakeCamel(field.name) + "(";
687         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
688                                 field);
689         code += " " + MakeCamel(field.name, false) + ") {\n";
690         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
691                 NumToString(offset) + ", ";
692         code += MakeCamel(field.name, false);
693         if (field.value.type.enum_def) { code += "?.value"; }
694         code += ");\n";
695       } else if (IsStruct(field.value.type)) {
696         code += "  int add" + MakeCamel(field.name) + "(int offset) {\n";
697         code +=
698             "    fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n";
699       } else {
700         code += "  int add" + MakeCamel(field.name) + "Offset(int offset) {\n";
701         code +=
702             "    fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n";
703       }
704       code += "    return fbBuilder.offset;\n";
705       code += "  }\n";
706     }
707
708     code += "\n";
709     code += "  int finish() {\n";
710     code += "    return fbBuilder.endTable();\n";
711     code += "  }\n";
712   }
713
714   void GenObjectBuilder(const StructDef &struct_def,
715                         std::string *builder_name_ptr, std::string *code_ptr) {
716     auto &code = *code_ptr;
717     auto &builder_name = *builder_name_ptr;
718
719     code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n";
720     for (auto it = struct_def.fields.vec.begin();
721          it != struct_def.fields.vec.end(); ++it) {
722       auto &field = **it;
723       if (field.deprecated) continue;
724       code += "  final " +
725               GenDartTypeName(field.value.type, struct_def.defined_namespace,
726                               field, true) +
727               " _" + MakeCamel(field.name, false) + ";\n";
728     }
729     code += "\n";
730     code += "  " + builder_name + "(";
731     if (struct_def.fields.vec.size() != 0) {
732       code +=
733
734           "{\n";
735       for (auto it = struct_def.fields.vec.begin();
736            it != struct_def.fields.vec.end(); ++it) {
737         auto &field = **it;
738         if (field.deprecated) continue;
739         code += "    " +
740                 GenDartTypeName(field.value.type, struct_def.defined_namespace,
741                                 field, true) +
742                 " " + MakeCamel(field.name, false) + ",\n";
743       }
744       code += "  })\n";
745       code += "      : ";
746       for (auto it = struct_def.fields.vec.begin();
747            it != struct_def.fields.vec.end(); ++it) {
748         auto &field = **it;
749         if (field.deprecated) continue;
750         code += "_" + MakeCamel(field.name, false) + " = " +
751                 MakeCamel(field.name, false);
752         if (it == struct_def.fields.vec.end() - 1) {
753           code += ";\n\n";
754         } else {
755           code += ",\n        ";
756         }
757       }
758     } else {
759       code += ");\n\n";
760     }
761
762     code += "  /// Finish building, and store into the [fbBuilder].\n";
763     code += "  @override\n";
764     code += "  int finish(\n";
765     code += "    " + _kFb + ".Builder fbBuilder) {\n";
766     code += "    assert(fbBuilder != null);\n";
767
768     for (auto it = struct_def.fields.vec.begin();
769          it != struct_def.fields.vec.end(); ++it) {
770       auto &field = **it;
771       if (field.deprecated) continue;
772       if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type))
773         continue;
774
775       code += "    final int " + MakeCamel(field.name, false) + "Offset";
776       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
777         code +=
778             " = _" + MakeCamel(field.name, false) + "?.isNotEmpty == true\n";
779         code += "        ? fbBuilder.writeList";
780         switch (field.value.type.VectorType().base_type) {
781           case BASE_TYPE_STRING:
782             code += "(_" + MakeCamel(field.name, false) +
783                     ".map((b) => fbBuilder.writeString(b)).toList())";
784             break;
785           case BASE_TYPE_STRUCT:
786             if (field.value.type.struct_def->fixed) {
787               code += "OfStructs(_" + MakeCamel(field.name, false) + ")";
788             } else {
789               code += "(_" + MakeCamel(field.name, false) +
790                       ".map((b) => b.getOrCreateOffset(fbBuilder)).toList())";
791             }
792             break;
793           default:
794             code += GenType(field.value.type.VectorType()) + "(_" +
795                     MakeCamel(field.name, false);
796             if (field.value.type.enum_def) { code += ".map((f) => f.value)"; }
797             code += ")";
798         }
799         code += "\n        : null;\n";
800       } else if (field.value.type.base_type == BASE_TYPE_STRING) {
801         code += " = fbBuilder.writeString(_" + MakeCamel(field.name, false) + ");\n";
802       } else {
803         code += " = _" + MakeCamel(field.name, false) +
804                 "?.getOrCreateOffset(fbBuilder);\n";
805       }
806     }
807
808     code += "\n";
809     if (struct_def.fixed) {
810       StructObjectBuilderBody(struct_def, code_ptr);
811     } else {
812       TableObjectBuilderBody(struct_def, code_ptr);
813     }
814     code += "  }\n\n";
815
816     code += "  /// Convenience method to serialize to byte list.\n";
817     code += "  @override\n";
818     code += "  Uint8List toBytes([String fileIdentifier]) {\n";
819     code += "    " + _kFb + ".Builder fbBuilder = new ";
820     code += _kFb + ".Builder();\n";
821     code += "    int offset = finish(fbBuilder);\n";
822     code += "    return fbBuilder.finish(offset, fileIdentifier);\n";
823     code += "  }\n";
824     code += "}\n";
825   }
826
827   void StructObjectBuilderBody(const StructDef &struct_def,
828                                std::string *code_ptr,
829                                bool prependUnderscore = true) {
830     auto &code = *code_ptr;
831
832     for (auto it = struct_def.fields.vec.rbegin();
833          it != struct_def.fields.vec.rend(); ++it) {
834       auto &field = **it;
835
836       if (field.deprecated) continue;
837
838       if (field.padding) {
839         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
840       }
841
842       if (IsStruct(field.value.type)) {
843         code += "    ";
844         if (prependUnderscore) { code += "_"; }
845         code += field.name + ".finish(fbBuilder);\n";
846       } else {
847         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
848         if (prependUnderscore) { code += "_"; }
849         code += field.name;
850         if (field.value.type.enum_def) { code += "?.value"; }
851         code += ");\n";
852       }
853     }
854
855     code += "    return fbBuilder.offset;\n";
856   }
857
858   void TableObjectBuilderBody(const StructDef &struct_def,
859                               std::string *code_ptr,
860                               bool prependUnderscore = true) {
861     std::string &code = *code_ptr;
862     code += "    fbBuilder.startTable();\n";
863
864     for (auto it = struct_def.fields.vec.begin();
865          it != struct_def.fields.vec.end(); ++it) {
866       auto &field = **it;
867
868       if (field.deprecated) continue;
869
870       auto offset = it - struct_def.fields.vec.begin();
871       if (IsScalar(field.value.type.base_type)) {
872         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
873                 NumToString(offset) + ", ";
874         if (prependUnderscore) { code += "_"; }
875         code += MakeCamel(field.name, false);
876         if (field.value.type.enum_def) { code += "?.value"; }
877         code += ");\n";
878       } else if (IsStruct(field.value.type)) {
879         code += "    if (";
880         if (prependUnderscore) { code += "_"; }
881         code += MakeCamel(field.name, false) + " != null) {\n";
882         code += "      fbBuilder.addStruct(" + NumToString(offset) + ", ";
883         code += "_" + MakeCamel(field.name, false) + ".finish(fbBuilder));\n";
884         code += "    }\n";
885       } else {
886         code +=
887             "    if (" + MakeCamel(field.name, false) + "Offset != null) {\n";
888         code += "      fbBuilder.addOffset(" + NumToString(offset) + ", " +
889                 MakeCamel(field.name, false) + "Offset);\n";
890         code += "    }\n";
891       }
892     }
893     code += "    return fbBuilder.endTable();\n";
894   }
895 };
896 }  // namespace dart
897
898 bool GenerateDart(const Parser &parser, const std::string &path,
899                   const std::string &file_name) {
900   dart::DartGenerator generator(parser, path, file_name);
901   return generator.generate();
902 }
903
904 std::string DartMakeRule(const Parser &parser, const std::string &path,
905                          const std::string &file_name) {
906   assert(parser.opts.lang <= IDLOptions::kMAX);
907
908   auto filebase =
909       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
910   auto make_rule = GeneratedFileName(path, filebase) + ": ";
911
912   auto included_files = parser.GetIncludedFilesRecursive(file_name);
913   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
914     make_rule += " " + *it;
915   }
916   return make_rule;
917 }
918
919 }  // namespace flatbuffers