JsonValidator.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. /*
  2. * JsonValidator.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "JsonValidator.h"
  12. #include "JsonUtils.h"
  13. #include "../VCMI_Lib.h"
  14. #include "../filesystem/Filesystem.h"
  15. #include "../modding/ModScope.h"
  16. #include "../modding/CModHandler.h"
  17. #include "../ScopeGuard.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
  20. {
  21. {"null", JsonNode::JsonType::DATA_NULL},
  22. {"boolean", JsonNode::JsonType::DATA_BOOL},
  23. {"number", JsonNode::JsonType::DATA_FLOAT},
  24. {"integer", JsonNode::JsonType::DATA_INTEGER},
  25. {"string", JsonNode::JsonType::DATA_STRING},
  26. {"array", JsonNode::JsonType::DATA_VECTOR},
  27. {"object", JsonNode::JsonType::DATA_STRUCT}
  28. };
  29. namespace
  30. {
  31. namespace Common
  32. {
  33. std::string emptyCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  34. {
  35. // check is not needed - e.g. incorporated into another check
  36. return "";
  37. }
  38. std::string notImplementedCheck(Validation::ValidationData & validator,
  39. const JsonNode & baseSchema,
  40. const JsonNode & schema,
  41. const JsonNode & data)
  42. {
  43. return "Not implemented entry in schema";
  44. }
  45. std::string schemaListCheck(Validation::ValidationData & validator,
  46. const JsonNode & baseSchema,
  47. const JsonNode & schema,
  48. const JsonNode & data,
  49. const std::string & errorMsg,
  50. const std::function<bool(size_t)> & isValid)
  51. {
  52. std::string errors = "<tested schemas>\n";
  53. size_t result = 0;
  54. for(const auto & schemaEntry : schema.Vector())
  55. {
  56. std::string error = check(schemaEntry, data, validator);
  57. if (error.empty())
  58. {
  59. result++;
  60. }
  61. else
  62. {
  63. errors += error;
  64. errors += "<end of schema>\n";
  65. }
  66. }
  67. if (isValid(result))
  68. return "";
  69. else
  70. return validator.makeErrorMessage(errorMsg) + errors;
  71. }
  72. std::string allOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  73. {
  74. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&](size_t count)
  75. {
  76. return count == schema.Vector().size();
  77. });
  78. }
  79. std::string anyOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  80. {
  81. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [&](size_t count)
  82. {
  83. return count > 0;
  84. });
  85. }
  86. std::string oneOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  87. {
  88. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [&](size_t count)
  89. {
  90. return count == 1;
  91. });
  92. }
  93. std::string notCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  94. {
  95. if (check(schema, data, validator).empty())
  96. return validator.makeErrorMessage("Successful validation against negative check");
  97. return "";
  98. }
  99. std::string enumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  100. {
  101. for(const auto & enumEntry : schema.Vector())
  102. {
  103. if (data == enumEntry)
  104. return "";
  105. }
  106. return validator.makeErrorMessage("Key must have one of predefined values");
  107. }
  108. std::string typeCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  109. {
  110. const auto & typeName = schema.String();
  111. auto it = stringToType.find(typeName);
  112. if(it == stringToType.end())
  113. {
  114. return validator.makeErrorMessage("Unknown type in schema:" + typeName);
  115. }
  116. JsonNode::JsonType type = it->second;
  117. // for "number" type both float and integer are allowed
  118. if(type == JsonNode::JsonType::DATA_FLOAT && data.isNumber())
  119. return "";
  120. if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
  121. return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
  122. return "";
  123. }
  124. std::string refCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  125. {
  126. std::string URI = schema.String();
  127. //node must be validated using schema pointed by this reference and not by data here
  128. //Local reference. Turn it into more easy to handle remote ref
  129. if (boost::algorithm::starts_with(URI, "#"))
  130. {
  131. const std::string name = validator.usedSchemas.back();
  132. const std::string nameClean = name.substr(0, name.find('#'));
  133. URI = nameClean + URI;
  134. }
  135. return check(URI, data, validator);
  136. }
  137. std::string formatCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  138. {
  139. auto formats = Validation::getKnownFormats();
  140. std::string errors;
  141. auto checker = formats.find(schema.String());
  142. if (checker != formats.end())
  143. {
  144. if (data.isString())
  145. {
  146. std::string result = checker->second(data);
  147. if (!result.empty())
  148. errors += validator.makeErrorMessage(result);
  149. }
  150. else
  151. {
  152. errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
  153. }
  154. }
  155. else
  156. errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
  157. return errors;
  158. }
  159. }
  160. namespace String
  161. {
  162. std::string maxLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  163. {
  164. if (data.String().size() > schema.Float())
  165. return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
  166. return "";
  167. }
  168. std::string minLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  169. {
  170. if (data.String().size() < schema.Float())
  171. return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
  172. return "";
  173. }
  174. }
  175. namespace Number
  176. {
  177. std::string maximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  178. {
  179. if (data.Float() > schema.Float())
  180. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  181. return "";
  182. }
  183. std::string minimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  184. {
  185. if (data.Float() < schema.Float())
  186. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  187. return "";
  188. }
  189. std::string exclusiveMaximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  190. {
  191. if (data.Float() >= schema.Float())
  192. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  193. return "";
  194. }
  195. std::string exclusiveMinimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  196. {
  197. if (data.Float() <= schema.Float())
  198. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  199. return "";
  200. }
  201. std::string multipleOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  202. {
  203. double result = data.Float() / schema.Float();
  204. if (!vstd::isAlmostEqual(floor(result), result))
  205. return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
  206. return "";
  207. }
  208. }
  209. namespace Vector
  210. {
  211. std::string itemEntryCheck(Validation::ValidationData & validator, const JsonVector & items, const JsonNode & schema, size_t index)
  212. {
  213. validator.currentPath.emplace_back();
  214. validator.currentPath.back().Float() = static_cast<double>(index);
  215. auto onExit = vstd::makeScopeGuard([&]()
  216. {
  217. validator.currentPath.pop_back();
  218. });
  219. if (!schema.isNull())
  220. return check(schema, items[index], validator);
  221. return "";
  222. }
  223. std::string itemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  224. {
  225. std::string errors;
  226. for (size_t i=0; i<data.Vector().size(); i++)
  227. {
  228. if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
  229. {
  230. if (schema.Vector().size() > i)
  231. errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
  232. }
  233. else
  234. {
  235. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  236. }
  237. }
  238. return errors;
  239. }
  240. std::string additionalItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  241. {
  242. std::string errors;
  243. // "items" is struct or empty (defaults to empty struct) - validation always successful
  244. const JsonNode & items = baseSchema["items"];
  245. if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
  246. return "";
  247. for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
  248. {
  249. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  250. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  251. else if(!schema.isNull() && !schema.Bool())
  252. errors += validator.makeErrorMessage("Unknown entry found");
  253. }
  254. return errors;
  255. }
  256. std::string minItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  257. {
  258. if (data.Vector().size() < schema.Float())
  259. return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
  260. return "";
  261. }
  262. std::string maxItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  263. {
  264. if (data.Vector().size() > schema.Float())
  265. return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
  266. return "";
  267. }
  268. std::string uniqueItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  269. {
  270. if (schema.Bool())
  271. {
  272. for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
  273. {
  274. auto itB = itA;
  275. while (++itB != schema.Vector().end())
  276. {
  277. if (*itA == *itB)
  278. return validator.makeErrorMessage("List must consist from unique items");
  279. }
  280. }
  281. }
  282. return "";
  283. }
  284. }
  285. namespace Struct
  286. {
  287. std::string maxPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  288. {
  289. if (data.Struct().size() > schema.Float())
  290. return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
  291. return "";
  292. }
  293. std::string minPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  294. {
  295. if (data.Struct().size() < schema.Float())
  296. return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
  297. return "";
  298. }
  299. std::string uniquePropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  300. {
  301. for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
  302. {
  303. auto itB = itA;
  304. while (++itB != data.Struct().end())
  305. {
  306. if (itA->second == itB->second)
  307. return validator.makeErrorMessage("List must consist from unique items");
  308. }
  309. }
  310. return "";
  311. }
  312. std::string requiredCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  313. {
  314. std::string errors;
  315. for(const auto & required : schema.Vector())
  316. {
  317. if (data[required.String()].isNull())
  318. errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
  319. }
  320. return errors;
  321. }
  322. std::string dependenciesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  323. {
  324. std::string errors;
  325. for(const auto & deps : schema.Struct())
  326. {
  327. if (!data[deps.first].isNull())
  328. {
  329. if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
  330. {
  331. JsonVector depList = deps.second.Vector();
  332. for(auto & depEntry : depList)
  333. {
  334. if (data[depEntry.String()].isNull())
  335. errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
  336. }
  337. }
  338. else
  339. {
  340. if (!check(deps.second, data, validator).empty())
  341. errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
  342. }
  343. }
  344. }
  345. return errors;
  346. }
  347. std::string propertyEntryCheck(Validation::ValidationData & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
  348. {
  349. validator.currentPath.emplace_back();
  350. validator.currentPath.back().String() = nodeName;
  351. auto onExit = vstd::makeScopeGuard([&]()
  352. {
  353. validator.currentPath.pop_back();
  354. });
  355. // there is schema specifically for this item
  356. if (!schema.isNull())
  357. return check(schema, node, validator);
  358. return "";
  359. }
  360. std::string propertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  361. {
  362. std::string errors;
  363. for(const auto & entry : data.Struct())
  364. errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
  365. return errors;
  366. }
  367. std::string additionalPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  368. {
  369. std::string errors;
  370. for(const auto & entry : data.Struct())
  371. {
  372. if (baseSchema["properties"].Struct().count(entry.first) == 0)
  373. {
  374. // try generic additionalItems schema
  375. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  376. errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
  377. // or, additionalItems field can be bool which indicates if such items are allowed
  378. else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
  379. errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
  380. }
  381. }
  382. return errors;
  383. }
  384. }
  385. namespace Formats
  386. {
  387. bool testFilePresence(const std::string & scope, const ResourcePath & resource)
  388. {
  389. std::set<std::string> allowedScopes;
  390. if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
  391. {
  392. //NOTE: recursive dependencies are not allowed at the moment - update code if this changes
  393. bool found = true;
  394. allowedScopes = VLC->modh->getModDependencies(scope, found);
  395. if(!found)
  396. return false;
  397. allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
  398. }
  399. allowedScopes.insert(scope); // mods can use their own files
  400. for(const auto & entry : allowedScopes)
  401. {
  402. if (CResourceHandler::get(entry)->existsResource(resource))
  403. return true;
  404. }
  405. return false;
  406. }
  407. #define TEST_FILE(scope, prefix, file, type) \
  408. if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
  409. return ""
  410. std::string testAnimation(const std::string & path, const std::string & scope)
  411. {
  412. TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
  413. TEST_FILE(scope, "Sprites/", path, EResType::JSON);
  414. return "Animation file \"" + path + "\" was not found";
  415. }
  416. std::string textFile(const JsonNode & node)
  417. {
  418. TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
  419. return "Text file \"" + node.String() + "\" was not found";
  420. }
  421. std::string musicFile(const JsonNode & node)
  422. {
  423. TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
  424. TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
  425. return "Music file \"" + node.String() + "\" was not found";
  426. }
  427. std::string soundFile(const JsonNode & node)
  428. {
  429. TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
  430. return "Sound file \"" + node.String() + "\" was not found";
  431. }
  432. std::string defFile(const JsonNode & node)
  433. {
  434. return testAnimation(node.String(), node.getModScope());
  435. }
  436. std::string animationFile(const JsonNode & node)
  437. {
  438. return testAnimation(node.String(), node.getModScope());
  439. }
  440. std::string imageFile(const JsonNode & node)
  441. {
  442. TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
  443. TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
  444. if (node.String().find(':') != std::string::npos)
  445. return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
  446. return "Image file \"" + node.String() + "\" was not found";
  447. }
  448. std::string videoFile(const JsonNode & node)
  449. {
  450. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
  451. return "Video file \"" + node.String() + "\" was not found";
  452. }
  453. #undef TEST_FILE
  454. }
  455. Validation::TValidatorMap createCommonFields()
  456. {
  457. Validation::TValidatorMap ret;
  458. ret["format"] = Common::formatCheck;
  459. ret["allOf"] = Common::allOfCheck;
  460. ret["anyOf"] = Common::anyOfCheck;
  461. ret["oneOf"] = Common::oneOfCheck;
  462. ret["enum"] = Common::enumCheck;
  463. ret["type"] = Common::typeCheck;
  464. ret["not"] = Common::notCheck;
  465. ret["$ref"] = Common::refCheck;
  466. // fields that don't need implementation
  467. ret["title"] = Common::emptyCheck;
  468. ret["$schema"] = Common::emptyCheck;
  469. ret["default"] = Common::emptyCheck;
  470. ret["description"] = Common::emptyCheck;
  471. ret["definitions"] = Common::emptyCheck;
  472. // Not implemented
  473. ret["propertyNames"] = Common::emptyCheck;
  474. ret["contains"] = Common::emptyCheck;
  475. ret["const"] = Common::emptyCheck;
  476. ret["examples"] = Common::emptyCheck;
  477. return ret;
  478. }
  479. Validation::TValidatorMap createStringFields()
  480. {
  481. Validation::TValidatorMap ret = createCommonFields();
  482. ret["maxLength"] = String::maxLengthCheck;
  483. ret["minLength"] = String::minLengthCheck;
  484. ret["pattern"] = Common::notImplementedCheck;
  485. return ret;
  486. }
  487. Validation::TValidatorMap createNumberFields()
  488. {
  489. Validation::TValidatorMap ret = createCommonFields();
  490. ret["maximum"] = Number::maximumCheck;
  491. ret["minimum"] = Number::minimumCheck;
  492. ret["multipleOf"] = Number::multipleOfCheck;
  493. ret["exclusiveMaximum"] = Number::exclusiveMaximumCheck;
  494. ret["exclusiveMinimum"] = Number::exclusiveMinimumCheck;
  495. return ret;
  496. }
  497. Validation::TValidatorMap createVectorFields()
  498. {
  499. Validation::TValidatorMap ret = createCommonFields();
  500. ret["items"] = Vector::itemsCheck;
  501. ret["minItems"] = Vector::minItemsCheck;
  502. ret["maxItems"] = Vector::maxItemsCheck;
  503. ret["uniqueItems"] = Vector::uniqueItemsCheck;
  504. ret["additionalItems"] = Vector::additionalItemsCheck;
  505. return ret;
  506. }
  507. Validation::TValidatorMap createStructFields()
  508. {
  509. Validation::TValidatorMap ret = createCommonFields();
  510. ret["additionalProperties"] = Struct::additionalPropertiesCheck;
  511. ret["uniqueProperties"] = Struct::uniquePropertiesCheck;
  512. ret["maxProperties"] = Struct::maxPropertiesCheck;
  513. ret["minProperties"] = Struct::minPropertiesCheck;
  514. ret["dependencies"] = Struct::dependenciesCheck;
  515. ret["properties"] = Struct::propertiesCheck;
  516. ret["required"] = Struct::requiredCheck;
  517. ret["patternProperties"] = Common::notImplementedCheck;
  518. return ret;
  519. }
  520. Validation::TFormatMap createFormatMap()
  521. {
  522. Validation::TFormatMap ret;
  523. ret["textFile"] = Formats::textFile;
  524. ret["musicFile"] = Formats::musicFile;
  525. ret["soundFile"] = Formats::soundFile;
  526. ret["defFile"] = Formats::defFile;
  527. ret["animationFile"] = Formats::animationFile;
  528. ret["imageFile"] = Formats::imageFile;
  529. ret["videoFile"] = Formats::videoFile;
  530. //TODO:
  531. // uri-reference
  532. // uri-template
  533. // json-pointer
  534. return ret;
  535. }
  536. }
  537. namespace Validation
  538. {
  539. std::string ValidationData::makeErrorMessage(const std::string &message)
  540. {
  541. std::string errors;
  542. errors += "At ";
  543. if (!currentPath.empty())
  544. {
  545. for(const JsonNode &path : currentPath)
  546. {
  547. errors += "/";
  548. if (path.getType() == JsonNode::JsonType::DATA_STRING)
  549. errors += path.String();
  550. else
  551. errors += std::to_string(static_cast<unsigned>(path.Float()));
  552. }
  553. }
  554. else
  555. errors += "<root>";
  556. errors += "\n\t Error: " + message + "\n";
  557. return errors;
  558. }
  559. std::string check(const std::string & schemaName, const JsonNode & data)
  560. {
  561. ValidationData validator;
  562. return check(schemaName, data, validator);
  563. }
  564. std::string check(const std::string & schemaName, const JsonNode & data, ValidationData & validator)
  565. {
  566. validator.usedSchemas.push_back(schemaName);
  567. auto onscopeExit = vstd::makeScopeGuard([&]()
  568. {
  569. validator.usedSchemas.pop_back();
  570. });
  571. return check(JsonUtils::getSchema(schemaName), data, validator);
  572. }
  573. std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator)
  574. {
  575. const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
  576. std::string errors;
  577. for(const auto & entry : schema.Struct())
  578. {
  579. auto checker = knownFields.find(entry.first);
  580. if (checker != knownFields.end())
  581. errors += checker->second(validator, schema, entry.second, data);
  582. //else
  583. // errors += validator.makeErrorMessage("Unknown entry in schema " + entry.first);
  584. }
  585. return errors;
  586. }
  587. const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type)
  588. {
  589. static const TValidatorMap commonFields = createCommonFields();
  590. static const TValidatorMap numberFields = createNumberFields();
  591. static const TValidatorMap stringFields = createStringFields();
  592. static const TValidatorMap vectorFields = createVectorFields();
  593. static const TValidatorMap structFields = createStructFields();
  594. switch (type)
  595. {
  596. case JsonNode::JsonType::DATA_FLOAT:
  597. case JsonNode::JsonType::DATA_INTEGER:
  598. return numberFields;
  599. case JsonNode::JsonType::DATA_STRING: return stringFields;
  600. case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
  601. case JsonNode::JsonType::DATA_STRUCT: return structFields;
  602. default: return commonFields;
  603. }
  604. }
  605. const TFormatMap & getKnownFormats()
  606. {
  607. static TFormatMap knownFormats = createFormatMap();
  608. return knownFormats;
  609. }
  610. } // Validation namespace
  611. VCMI_LIB_NAMESPACE_END