JsonValidator.cpp 22 KB

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