JsonValidator.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  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. try
  426. {
  427. if (CResourceHandler::get(entry)->existsResource(resource))
  428. return true;
  429. }
  430. catch (const std::out_of_range & e)
  431. {
  432. throw std::out_of_range("Failed to find filesystem of mod '" + entry + "' when testing file '" + resource.getOriginalName() + "' for mod '" + scope + "'");
  433. }
  434. }
  435. #endif
  436. return false;
  437. }
  438. #define TEST_FILE(scope, prefix, file, type) \
  439. if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
  440. return ""
  441. static std::string testAnimation(const std::string & path, const std::string & scope)
  442. {
  443. TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
  444. TEST_FILE(scope, "Sprites/", path, EResType::JSON);
  445. return "Animation file \"" + path + "\" was not found";
  446. }
  447. static std::string textFile(const JsonNode & node)
  448. {
  449. TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
  450. return "Text file \"" + node.String() + "\" was not found";
  451. }
  452. static std::string musicFile(const JsonNode & node)
  453. {
  454. TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
  455. TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
  456. return "Music file \"" + node.String() + "\" was not found";
  457. }
  458. static std::string soundFile(const JsonNode & node)
  459. {
  460. TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
  461. return "Sound file \"" + node.String() + "\" was not found";
  462. }
  463. static std::string animationFile(const JsonNode & node)
  464. {
  465. return testAnimation(node.String(), node.getModScope());
  466. }
  467. static std::string imageFile(const JsonNode & node)
  468. {
  469. TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
  470. TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
  471. if (node.String().find(':') != std::string::npos)
  472. return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
  473. return "Image file \"" + node.String() + "\" was not found";
  474. }
  475. static std::string videoFile(const JsonNode & node)
  476. {
  477. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
  478. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO_LOW_QUALITY);
  479. return "Video file \"" + node.String() + "\" was not found";
  480. }
  481. #undef TEST_FILE
  482. static std::string version(const JsonNode & node)
  483. {
  484. auto version = CModVersion::fromString(node.String());
  485. if (version == CModVersion())
  486. return "Failed to parse mod version: " + node.toCompactString() + ". Expected format X.Y.Z, where X, Y, Z are non-negative numbers";
  487. return "";
  488. }
  489. JsonValidator::TValidatorMap createCommonFields()
  490. {
  491. JsonValidator::TValidatorMap ret;
  492. ret["format"] = formatCheck;
  493. ret["allOf"] = allOfCheck;
  494. ret["anyOf"] = anyOfCheck;
  495. ret["oneOf"] = oneOfCheck;
  496. ret["enum"] = enumCheck;
  497. ret["const"] = constCheck;
  498. ret["type"] = typeCheck;
  499. ret["not"] = notCheck;
  500. ret["$ref"] = refCheck;
  501. ret["if"] = ifCheck;
  502. ret["then"] = emptyCheck; // implemented as part of "if check"
  503. // fields that don't need implementation
  504. ret["title"] = emptyCheck;
  505. ret["$schema"] = emptyCheck;
  506. ret["default"] = emptyCheck;
  507. ret["defaultIOS"] = emptyCheck;
  508. ret["defaultAndroid"] = emptyCheck;
  509. ret["defaultWindows"] = emptyCheck;
  510. ret["description"] = emptyCheck;
  511. ret["definitions"] = emptyCheck;
  512. // Not implemented
  513. ret["propertyNames"] = notImplementedCheck;
  514. ret["contains"] = notImplementedCheck;
  515. ret["examples"] = notImplementedCheck;
  516. return ret;
  517. }
  518. JsonValidator::TValidatorMap createStringFields()
  519. {
  520. JsonValidator::TValidatorMap ret = createCommonFields();
  521. ret["maxLength"] = maxLengthCheck;
  522. ret["minLength"] = minLengthCheck;
  523. ret["pattern"] = notImplementedCheck;
  524. return ret;
  525. }
  526. JsonValidator::TValidatorMap createNumberFields()
  527. {
  528. JsonValidator::TValidatorMap ret = createCommonFields();
  529. ret["maximum"] = maximumCheck;
  530. ret["minimum"] = minimumCheck;
  531. ret["multipleOf"] = multipleOfCheck;
  532. ret["exclusiveMaximum"] = exclusiveMaximumCheck;
  533. ret["exclusiveMinimum"] = exclusiveMinimumCheck;
  534. return ret;
  535. }
  536. JsonValidator::TValidatorMap createVectorFields()
  537. {
  538. JsonValidator::TValidatorMap ret = createCommonFields();
  539. ret["items"] = itemsCheck;
  540. ret["minItems"] = minItemsCheck;
  541. ret["maxItems"] = maxItemsCheck;
  542. ret["uniqueItems"] = uniqueItemsCheck;
  543. ret["additionalItems"] = additionalItemsCheck;
  544. return ret;
  545. }
  546. JsonValidator::TValidatorMap createStructFields()
  547. {
  548. JsonValidator::TValidatorMap ret = createCommonFields();
  549. ret["additionalProperties"] = additionalPropertiesCheck;
  550. ret["uniqueProperties"] = uniquePropertiesCheck;
  551. ret["maxProperties"] = maxPropertiesCheck;
  552. ret["minProperties"] = minPropertiesCheck;
  553. ret["dependencies"] = dependenciesCheck;
  554. ret["properties"] = propertiesCheck;
  555. ret["required"] = requiredCheck;
  556. ret["patternProperties"] = notImplementedCheck;
  557. return ret;
  558. }
  559. JsonValidator::TFormatMap createFormatMap()
  560. {
  561. JsonValidator::TFormatMap ret;
  562. ret["textFile"] = textFile;
  563. ret["musicFile"] = musicFile;
  564. ret["soundFile"] = soundFile;
  565. ret["animationFile"] = animationFile;
  566. ret["imageFile"] = imageFile;
  567. ret["videoFile"] = videoFile;
  568. ret["version"] = version;
  569. //TODO:
  570. // uri-reference
  571. // uri-template
  572. // json-pointer
  573. return ret;
  574. }
  575. std::string JsonValidator::makeErrorMessage(const std::string &message)
  576. {
  577. std::string errors;
  578. errors += "At ";
  579. if (!currentPath.empty())
  580. {
  581. for(const JsonNode &path : currentPath)
  582. {
  583. errors += "/";
  584. if (path.getType() == JsonNode::JsonType::DATA_STRING)
  585. errors += path.String();
  586. else
  587. errors += std::to_string(static_cast<unsigned>(path.Float()));
  588. }
  589. }
  590. else
  591. errors += "<root>";
  592. errors += "\n\t Error: " + message + "\n";
  593. return errors;
  594. }
  595. std::string JsonValidator::check(const std::string & schemaName, const JsonNode & data)
  596. {
  597. usedSchemas.push_back(schemaName);
  598. auto onscopeExit = vstd::makeScopeGuard([this]()
  599. {
  600. usedSchemas.pop_back();
  601. });
  602. return check(JsonUtils::getSchema(schemaName), data);
  603. }
  604. std::string JsonValidator::check(const JsonNode & schema, const JsonNode & data)
  605. {
  606. const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
  607. std::string errors;
  608. for(const auto & entry : schema.Struct())
  609. {
  610. auto checker = knownFields.find(entry.first);
  611. if (checker != knownFields.end())
  612. errors += checker->second(*this, schema, entry.second, data);
  613. }
  614. return errors;
  615. }
  616. const JsonValidator::TValidatorMap & JsonValidator::getKnownFieldsFor(JsonNode::JsonType type)
  617. {
  618. static const TValidatorMap commonFields = createCommonFields();
  619. static const TValidatorMap numberFields = createNumberFields();
  620. static const TValidatorMap stringFields = createStringFields();
  621. static const TValidatorMap vectorFields = createVectorFields();
  622. static const TValidatorMap structFields = createStructFields();
  623. switch (type)
  624. {
  625. case JsonNode::JsonType::DATA_FLOAT:
  626. case JsonNode::JsonType::DATA_INTEGER:
  627. return numberFields;
  628. case JsonNode::JsonType::DATA_STRING: return stringFields;
  629. case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
  630. case JsonNode::JsonType::DATA_STRUCT: return structFields;
  631. default: return commonFields;
  632. }
  633. }
  634. const JsonValidator::TFormatMap & JsonValidator::getKnownFormats()
  635. {
  636. static const TFormatMap knownFormats = createFormatMap();
  637. return knownFormats;
  638. }
  639. VCMI_LIB_NAMESPACE_END