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 #include <iostream>
18
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/idl.h"
21 #include "flatbuffers/util.h"
22
23 namespace flatbuffers {
24
25 namespace jsons {
26
GenNativeType(BaseType type)27 std::string GenNativeType(BaseType type) {
28 switch (type) {
29 case BASE_TYPE_BOOL: return "boolean";
30 case BASE_TYPE_CHAR:
31 case BASE_TYPE_UCHAR:
32 case BASE_TYPE_SHORT:
33 case BASE_TYPE_USHORT:
34 case BASE_TYPE_INT:
35 case BASE_TYPE_UINT:
36 case BASE_TYPE_LONG:
37 case BASE_TYPE_ULONG:
38 case BASE_TYPE_FLOAT:
39 case BASE_TYPE_DOUBLE: return "number";
40 case BASE_TYPE_STRING: return "string";
41 case BASE_TYPE_ARRAY: return "array";
42 default: return "";
43 }
44 }
45
GenFullName(const T * enum_def)46 template<class T> std::string GenFullName(const T *enum_def) {
47 std::string full_name;
48 const auto &name_spaces = enum_def->defined_namespace->components;
49 for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) {
50 full_name.append(*ns + "_");
51 }
52 full_name.append(enum_def->name);
53 return full_name;
54 }
55
GenTypeRef(const T * enum_def)56 template<class T> std::string GenTypeRef(const T *enum_def) {
57 return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\"";
58 }
59
GenType(const std::string & name)60 std::string GenType(const std::string &name) {
61 return "\"type\" : \"" + name + "\"";
62 }
63
GenType(const Type & type)64 std::string GenType(const Type &type) {
65 if (type.enum_def != nullptr && !type.enum_def->is_union) {
66 // it is a reference to an enum type
67 return GenTypeRef(type.enum_def);
68 }
69 switch (type.base_type) {
70 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
71 case BASE_TYPE_VECTOR: {
72 std::string typeline;
73 typeline.append("\"type\" : \"array\", \"items\" : { ");
74 if (type.element == BASE_TYPE_STRUCT) {
75 typeline.append(GenTypeRef(type.struct_def));
76 } else {
77 typeline.append(GenType(GenNativeType(type.element)));
78 }
79 typeline.append(" }");
80 return typeline;
81 }
82 case BASE_TYPE_STRUCT: {
83 return GenTypeRef(type.struct_def);
84 }
85 case BASE_TYPE_UNION: {
86 std::string union_type_string("\"anyOf\": [");
87 const auto &union_types = type.enum_def->Vals();
88 for (auto ut = union_types.cbegin(); ut < union_types.cend(); ++ut) {
89 auto &union_type = *ut;
90 if (union_type->union_type.base_type == BASE_TYPE_NONE) { continue; }
91 if (union_type->union_type.base_type == BASE_TYPE_STRUCT) {
92 union_type_string.append(
93 "{ " + GenTypeRef(union_type->union_type.struct_def) + " }");
94 }
95 if (union_type != *type.enum_def->Vals().rbegin()) {
96 union_type_string.append(",");
97 }
98 }
99 union_type_string.append("]");
100 return union_type_string;
101 }
102 case BASE_TYPE_UTYPE: return GenTypeRef(type.enum_def);
103 default: return GenType(GenNativeType(type.base_type));
104 }
105 }
106
107 class JsonSchemaGenerator : public BaseGenerator {
108 private:
109 CodeWriter code_;
110
111 public:
JsonSchemaGenerator(const Parser & parser,const std::string & path,const std::string & file_name)112 JsonSchemaGenerator(const Parser &parser, const std::string &path,
113 const std::string &file_name)
114 : BaseGenerator(parser, path, file_name, "", "", "json") {}
115
JsonSchemaGenerator(const BaseGenerator & base_generator)116 explicit JsonSchemaGenerator(const BaseGenerator &base_generator)
117 : BaseGenerator(base_generator) {}
118
GeneratedFileName(const std::string & path,const std::string & file_name,const IDLOptions & options) const119 std::string GeneratedFileName(const std::string &path,
120 const std::string &file_name,
121 const IDLOptions &options /* unused */) const {
122 (void)options;
123 return path + file_name + ".schema.json";
124 }
125
generate()126 bool generate() {
127 if (parser_.root_struct_def_ == nullptr) { return false; }
128 code_.Clear();
129 code_ += "{";
130 code_ += " \"$schema\": \"http://json-schema.org/draft-04/schema#\",";
131 code_ += " \"definitions\": {";
132 for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend();
133 ++e) {
134 code_ += " \"" + GenFullName(*e) + "\" : {";
135 code_ += " " + GenType("string") + ",";
136 std::string enumdef(" \"enum\": [");
137 for (auto enum_value = (*e)->Vals().begin();
138 enum_value != (*e)->Vals().end(); ++enum_value) {
139 enumdef.append("\"" + (*enum_value)->name + "\"");
140 if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); }
141 }
142 enumdef.append("]");
143 code_ += enumdef;
144 code_ += " },"; // close type
145 }
146 for (auto s = parser_.structs_.vec.cbegin();
147 s != parser_.structs_.vec.cend(); ++s) {
148 const auto &structure = *s;
149 code_ += " \"" + GenFullName(structure) + "\" : {";
150 code_ += " " + GenType("object") + ",";
151 std::string comment;
152 const auto &comment_lines = structure->doc_comment;
153 for (auto comment_line = comment_lines.cbegin();
154 comment_line != comment_lines.cend(); ++comment_line) {
155 comment.append(*comment_line);
156 }
157 if (comment.size() > 0) {
158 std::string description;
159 if (!EscapeString(comment.c_str(), comment.length(), &description, true,
160 true)) {
161 return false;
162 }
163 code_ += " \"description\" : " + description + ",";
164 }
165 code_ += " \"properties\" : {";
166
167 const auto &properties = structure->fields.vec;
168 for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) {
169 const auto &property = *prop;
170 std::string arrayInfo = "";
171 if (IsArray(property->value.type)) {
172 arrayInfo = ",\n \"minItems\": " +
173 NumToString(property->value.type.fixed_length) +
174 ",\n \"maxItems\": " +
175 NumToString(property->value.type.fixed_length);
176 }
177 std::string typeLine =
178 " \"" + property->name + "\" : {\n" + " " +
179 GenType(property->value.type) + arrayInfo + "\n }";
180 if (property != properties.back()) { typeLine.append(","); }
181 code_ += typeLine;
182 }
183 code_ += " },"; // close properties
184
185 std::vector<FieldDef *> requiredProperties;
186 std::copy_if(properties.begin(), properties.end(),
187 back_inserter(requiredProperties),
188 [](FieldDef const *prop) { return prop->required; });
189 if (requiredProperties.size() > 0) {
190 std::string required_string(" \"required\" : [");
191 for (auto req_prop = requiredProperties.cbegin();
192 req_prop != requiredProperties.cend(); ++req_prop) {
193 required_string.append("\"" + (*req_prop)->name + "\"");
194 if (*req_prop != requiredProperties.back()) {
195 required_string.append(", ");
196 }
197 }
198 required_string.append("],");
199 code_ += required_string;
200 }
201 code_ += " \"additionalProperties\" : false";
202 std::string closeType(" }");
203 if (*s != parser_.structs_.vec.back()) { closeType.append(","); }
204 code_ += closeType; // close type
205 }
206 code_ += " },"; // close definitions
207
208 // mark root type
209 code_ += " \"$ref\" : \"#/definitions/" +
210 GenFullName(parser_.root_struct_def_) + "\"";
211
212 code_ += "}"; // close schema root
213 const std::string file_path =
214 GeneratedFileName(path_, file_name_, parser_.opts);
215 const std::string final_code = code_.ToString();
216 return SaveFile(file_path.c_str(), final_code, false);
217 }
218 };
219 } // namespace jsons
220
GenerateJsonSchema(const Parser & parser,const std::string & path,const std::string & file_name)221 bool GenerateJsonSchema(const Parser &parser, const std::string &path,
222 const std::string &file_name) {
223 jsons::JsonSchemaGenerator generator(parser, path, file_name);
224 return generator.generate();
225 }
226 } // namespace flatbuffers
227