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