JsonValidator.cpp 23 KB

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