• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/flatbuffers.h"
20 #include "flatbuffers/idl.h"
21 #include "flatbuffers/util.h"
22 #include "flatbuffers/flexbuffers.h"
23 
24 namespace flatbuffers {
25 
26 static bool GenStruct(const StructDef &struct_def, const Table *table,
27                       int indent, const IDLOptions &opts,
28                       std::string *_text);
29 
30 // If indentation is less than 0, that indicates we don't want any newlines
31 // either.
NewLine(const IDLOptions & opts)32 const char *NewLine(const IDLOptions &opts) {
33   return opts.indent_step >= 0 ? "\n" : "";
34 }
35 
Indent(const IDLOptions & opts)36 int Indent(const IDLOptions &opts) {
37   return std::max(opts.indent_step, 0);
38 }
39 
40 // Output an identifier with or without quotes depending on strictness.
OutputIdentifier(const std::string & name,const IDLOptions & opts,std::string * _text)41 void OutputIdentifier(const std::string &name, const IDLOptions &opts,
42                       std::string *_text) {
43   std::string &text = *_text;
44   if (opts.strict_json) text += "\"";
45   text += name;
46   if (opts.strict_json) text += "\"";
47 }
48 
49 // Print (and its template specialization below for pointers) generate text
50 // for a single FlatBuffer value into JSON format.
51 // The general case for scalars:
Print(T val,Type type,int,Type *,const IDLOptions & opts,std::string * _text)52 template<typename T> bool Print(T val, Type type, int /*indent*/,
53                                 Type * /*union_type*/,
54                                 const IDLOptions &opts,
55                                 std::string *_text) {
56   std::string &text = *_text;
57   if (type.enum_def && opts.output_enum_identifiers) {
58     auto enum_val = type.enum_def->ReverseLookup(static_cast<int>(val));
59     if (enum_val) {
60       OutputIdentifier(enum_val->name, opts, _text);
61       return true;
62     }
63   }
64 
65   if (type.base_type == BASE_TYPE_BOOL) {
66     text += val != 0 ? "true" : "false";
67   } else {
68     text += NumToString(val);
69   }
70 
71   return true;
72 }
73 
74 // Print a vector a sequence of JSON values, comma separated, wrapped in "[]".
PrintVector(const Vector<T> & v,Type type,int indent,const IDLOptions & opts,std::string * _text)75 template<typename T> bool PrintVector(const Vector<T> &v, Type type,
76                                       int indent, const IDLOptions &opts,
77                                       std::string *_text) {
78   std::string &text = *_text;
79   text += "[";
80   text += NewLine(opts);
81   for (uoffset_t i = 0; i < v.size(); i++) {
82     if (i) {
83       if (!opts.protobuf_ascii_alike) text += ",";
84       text += NewLine(opts);
85     }
86     text.append(indent + Indent(opts), ' ');
87     if (IsStruct(type)) {
88       if (!Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
89                  indent + Indent(opts), nullptr, opts, _text)) {
90         return false;
91       }
92     } else {
93       if (!Print(v[i], type, indent + Indent(opts), nullptr,
94                  opts, _text)) {
95         return false;
96       }
97     }
98   }
99   text += NewLine(opts);
100   text.append(indent, ' ');
101   text += "]";
102   return true;
103 }
104 
105 // Specialization of Print above for pointer types.
Print(const void * val,Type type,int indent,Type * union_type,const IDLOptions & opts,std::string * _text)106 template<> bool Print<const void *>(const void *val,
107                                     Type type, int indent,
108                                     Type *union_type,
109                                     const IDLOptions &opts,
110                                     std::string *_text) {
111   switch (type.base_type) {
112     case BASE_TYPE_UNION:
113       // If this assert hits, you have an corrupt buffer, a union type field
114       // was not present or was out of range.
115       assert(union_type);
116       return Print<const void *>(val, *union_type, indent, nullptr, opts,
117                                  _text);
118     case BASE_TYPE_STRUCT:
119       if (!GenStruct(*type.struct_def,
120                      reinterpret_cast<const Table *>(val),
121                      indent,
122                      opts,
123                      _text)) {
124         return false;
125       }
126       break;
127     case BASE_TYPE_STRING: {
128       auto s = reinterpret_cast<const String *>(val);
129       if (!EscapeString(s->c_str(), s->Length(), _text, opts.allow_non_utf8)) {
130         return false;
131       }
132       break;
133     }
134     case BASE_TYPE_VECTOR:
135       type = type.VectorType();
136       // Call PrintVector above specifically for each element type:
137       switch (type.base_type) {
138         #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
139           CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
140           case BASE_TYPE_ ## ENUM: \
141             if (!PrintVector<CTYPE>( \
142                   *reinterpret_cast<const Vector<CTYPE> *>(val), \
143                   type, indent, opts, _text)) { \
144               return false; \
145             } \
146             break;
147           FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
148         #undef FLATBUFFERS_TD
149       }
150       break;
151     default: assert(0);
152   }
153   return true;
154 }
155 
156 // Generate text for a scalar field.
GenField(const FieldDef & fd,const Table * table,bool fixed,const IDLOptions & opts,int indent,std::string * _text)157 template<typename T> static bool GenField(const FieldDef &fd,
158                                           const Table *table, bool fixed,
159                                           const IDLOptions &opts,
160                                           int indent,
161                                           std::string *_text) {
162   return Print(fixed ?
163     reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset) :
164     table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, nullptr,
165                                             opts, _text);
166 }
167 
168 static bool GenStruct(const StructDef &struct_def, const Table *table,
169 						  int indent, const IDLOptions &opts,
170 						  std::string *_text);
171 
172 // Generate text for non-scalar field.
GenFieldOffset(const FieldDef & fd,const Table * table,bool fixed,int indent,Type * union_type,const IDLOptions & opts,std::string * _text)173 static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
174                            int indent, Type *union_type,
175                            const IDLOptions &opts, std::string *_text) {
176   const void *val = nullptr;
177   if (fixed) {
178     // The only non-scalar fields in structs are structs.
179     assert(IsStruct(fd.value.type));
180     val = reinterpret_cast<const Struct *>(table)->
181             GetStruct<const void *>(fd.value.offset);
182   } else if (fd.flexbuffer) {
183     auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
184     auto root = flexbuffers::GetRoot(vec->data(), vec->size());
185     root.ToString(true, opts.strict_json, *_text);
186     return true;
187   } else if (fd.nested_flatbuffer) {
188     auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
189     auto root = GetRoot<Table>(vec->data());
190     return GenStruct(*fd.nested_flatbuffer, root, indent, opts, _text);
191   } else {
192     val = IsStruct(fd.value.type)
193       ? table->GetStruct<const void *>(fd.value.offset)
194       : table->GetPointer<const void *>(fd.value.offset);
195   }
196   return Print(val, fd.value.type, indent, union_type, opts, _text);
197 }
198 
199 // Generate text for a struct or table, values separated by commas, indented,
200 // and bracketed by "{}"
GenStruct(const StructDef & struct_def,const Table * table,int indent,const IDLOptions & opts,std::string * _text)201 static bool GenStruct(const StructDef &struct_def, const Table *table,
202                       int indent, const IDLOptions &opts,
203                       std::string *_text) {
204   std::string &text = *_text;
205   text += "{";
206   int fieldout = 0;
207   Type *union_type = nullptr;
208   for (auto it = struct_def.fields.vec.begin();
209        it != struct_def.fields.vec.end();
210        ++it) {
211     FieldDef &fd = **it;
212     auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
213     auto output_anyway = opts.output_default_scalars_in_json &&
214                          IsScalar(fd.value.type.base_type) &&
215                          !fd.deprecated;
216     if (is_present || output_anyway) {
217       if (fieldout++) {
218         if (!opts.protobuf_ascii_alike) text += ",";
219       }
220       text += NewLine(opts);
221       text.append(indent + Indent(opts), ' ');
222       OutputIdentifier(fd.name, opts, _text);
223       if (!opts.protobuf_ascii_alike ||
224           (fd.value.type.base_type != BASE_TYPE_STRUCT &&
225            fd.value.type.base_type != BASE_TYPE_VECTOR)) text += ":";
226       text += " ";
227       if (is_present) {
228         switch (fd.value.type.base_type) {
229            #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
230              CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
231              case BASE_TYPE_ ## ENUM: \
232                 if (!GenField<CTYPE>(fd, table, struct_def.fixed, \
233                                      opts, indent + Indent(opts), _text)) { \
234                   return false; \
235                 } \
236                 break;
237             FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
238           #undef FLATBUFFERS_TD
239           // Generate drop-thru case statements for all pointer types:
240           #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
241             CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
242             case BASE_TYPE_ ## ENUM:
243             FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
244           #undef FLATBUFFERS_TD
245               if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
246                                   union_type, opts, _text)) {
247                 return false;
248               }
249               break;
250         }
251         if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
252           auto enum_val = fd.value.type.enum_def->ReverseLookup(
253                                   table->GetField<uint8_t>(fd.value.offset, 0));
254           assert(enum_val);
255           union_type = &enum_val->union_type;
256         }
257       }
258       else
259       {
260         text += fd.value.constant;
261       }
262     }
263   }
264   text += NewLine(opts);
265   text.append(indent, ' ');
266   text += "}";
267   return true;
268 }
269 
270 // Generate a text representation of a flatbuffer in JSON format.
GenerateText(const Parser & parser,const void * flatbuffer,std::string * _text)271 bool GenerateText(const Parser &parser, const void *flatbuffer,
272                   std::string *_text) {
273   std::string &text = *_text;
274   assert(parser.root_struct_def_);  // call SetRootType()
275   text.reserve(1024);   // Reduce amount of inevitable reallocs.
276   if (!GenStruct(*parser.root_struct_def_,
277                  GetRoot<Table>(flatbuffer),
278                  0,
279                  parser.opts,
280                  _text)) {
281     return false;
282   }
283   text += NewLine(parser.opts);
284   return true;
285 }
286 
TextFileName(const std::string & path,const std::string & file_name)287 std::string TextFileName(const std::string &path,
288                          const std::string &file_name) {
289   return path + file_name + ".json";
290 }
291 
GenerateTextFile(const Parser & parser,const std::string & path,const std::string & file_name)292 bool GenerateTextFile(const Parser &parser,
293                       const std::string &path,
294                       const std::string &file_name) {
295   if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
296   std::string text;
297   if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
298     return false;
299   }
300   return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
301                                text,
302                                false);
303 }
304 
TextMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)305 std::string TextMakeRule(const Parser &parser,
306                          const std::string &path,
307                          const std::string &file_name) {
308   if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
309   std::string filebase = flatbuffers::StripPath(
310       flatbuffers::StripExtension(file_name));
311   std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
312   auto included_files = parser.GetIncludedFilesRecursive(
313       parser.root_struct_def_->file);
314   for (auto it = included_files.begin();
315        it != included_files.end(); ++it) {
316     make_rule += " " + *it;
317   }
318   return make_rule;
319 }
320 
321 }  // namespace flatbuffers
322 
323