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