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