JsonDetail.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. /*
  2. * JsonDetail.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 "JsonDetail.h"
  12. #include "VCMI_Lib.h"
  13. #include "CGeneralTextHandler.h"
  14. #include "CModHandler.h"
  15. #include "filesystem/Filesystem.h"
  16. #include "ScopeGuard.h"
  17. static const JsonNode nullNode;
  18. template<typename Iterator>
  19. void JsonWriter::writeContainer(Iterator begin, Iterator end)
  20. {
  21. if (begin == end)
  22. return;
  23. prefix += '\t';
  24. writeEntry(begin++);
  25. while (begin != end)
  26. {
  27. out << (compactMode ? ", " : ",\n");
  28. writeEntry(begin++);
  29. }
  30. out << (compactMode ? "" : "\n");
  31. prefix.resize(prefix.size()-1);
  32. }
  33. void JsonWriter::writeEntry(JsonMap::const_iterator entry)
  34. {
  35. if(!compactMode)
  36. {
  37. if (!entry->second.meta.empty())
  38. out << prefix << " // " << entry->second.meta << "\n";
  39. out << prefix;
  40. }
  41. writeString(entry->first);
  42. out << " : ";
  43. writeNode(entry->second);
  44. }
  45. void JsonWriter::writeEntry(JsonVector::const_iterator entry)
  46. {
  47. if(!compactMode)
  48. {
  49. if (!entry->meta.empty())
  50. out << prefix << " // " << entry->meta << "\n";
  51. out << prefix;
  52. }
  53. writeNode(*entry);
  54. }
  55. void JsonWriter::writeString(const std::string &string)
  56. {
  57. static const std::string escaped = "\"\\\b\f\n\r\t/";
  58. static const std::array<char, 8> escaped_code = {'\"', '\\', 'b', 'f', 'n', 'r', 't', '/'};
  59. out <<'\"';
  60. size_t pos=0, start=0;
  61. for (; pos<string.size(); pos++)
  62. {
  63. //we need to check if special character was been already escaped
  64. if((string[pos] == '\\')
  65. && (pos+1 < string.size())
  66. && (std::find(escaped_code.begin(), escaped_code.end(), string[pos+1]) != escaped_code.end()) )
  67. {
  68. pos++; //write unchanged, next simbol also checked
  69. }
  70. else
  71. {
  72. size_t escapedPos = escaped.find(string[pos]);
  73. if (escapedPos != std::string::npos)
  74. {
  75. out.write(string.data()+start, pos - start);
  76. out << '\\' << escaped_code[escapedPos];
  77. start = pos+1;
  78. }
  79. }
  80. }
  81. out.write(string.data()+start, pos - start);
  82. out <<'\"';
  83. }
  84. void JsonWriter::writeNode(const JsonNode &node)
  85. {
  86. bool originalMode = compactMode;
  87. if(compact && !compactMode && node.isCompact())
  88. compactMode = true;
  89. switch(node.getType())
  90. {
  91. break; case JsonNode::JsonType::DATA_NULL:
  92. out << "null";
  93. break; case JsonNode::JsonType::DATA_BOOL:
  94. if (node.Bool())
  95. out << "true";
  96. else
  97. out << "false";
  98. break; case JsonNode::JsonType::DATA_FLOAT:
  99. out << node.Float();
  100. break; case JsonNode::JsonType::DATA_STRING:
  101. writeString(node.String());
  102. break; case JsonNode::JsonType::DATA_VECTOR:
  103. out << "[" << (compactMode ? " " : "\n");
  104. writeContainer(node.Vector().begin(), node.Vector().end());
  105. out << (compactMode ? " " : prefix) << "]";
  106. break; case JsonNode::JsonType::DATA_STRUCT:
  107. out << "{" << (compactMode ? " " : "\n");
  108. writeContainer(node.Struct().begin(), node.Struct().end());
  109. out << (compactMode ? " " : prefix) << "}";
  110. break; case JsonNode::JsonType::DATA_INTEGER:
  111. out << node.Integer();
  112. }
  113. compactMode = originalMode;
  114. }
  115. JsonWriter::JsonWriter(std::ostream & output, bool compact)
  116. : out(output), compact(compact)
  117. {
  118. }
  119. ////////////////////////////////////////////////////////////////////////////////
  120. JsonParser::JsonParser(const char * inputString, size_t stringSize):
  121. input(inputString, stringSize),
  122. lineCount(1),
  123. lineStart(0),
  124. pos(0)
  125. {
  126. }
  127. JsonNode JsonParser::parse(std::string fileName)
  128. {
  129. JsonNode root;
  130. if (input.size() == 0)
  131. {
  132. error("File is empty", false);
  133. }
  134. else
  135. {
  136. if (!Unicode::isValidString(&input[0], input.size()))
  137. error("Not a valid UTF-8 file", false);
  138. extractValue(root);
  139. extractWhitespace(false);
  140. //Warn if there are any non-whitespace symbols left
  141. if (pos < input.size())
  142. error("Not all file was parsed!", true);
  143. }
  144. if (!errors.empty())
  145. {
  146. logMod->warn("File %s is not a valid JSON file!", fileName);
  147. logMod->warn(errors);
  148. }
  149. return root;
  150. }
  151. bool JsonParser::isValid()
  152. {
  153. return errors.empty();
  154. }
  155. bool JsonParser::extractSeparator()
  156. {
  157. if (!extractWhitespace())
  158. return false;
  159. if ( input[pos] !=':')
  160. return error("Separator expected");
  161. pos++;
  162. return true;
  163. }
  164. bool JsonParser::extractValue(JsonNode &node)
  165. {
  166. if (!extractWhitespace())
  167. return false;
  168. switch (input[pos])
  169. {
  170. case '\"': return extractString(node);
  171. case 'n' : return extractNull(node);
  172. case 't' : return extractTrue(node);
  173. case 'f' : return extractFalse(node);
  174. case '{' : return extractStruct(node);
  175. case '[' : return extractArray(node);
  176. case '-' : return extractFloat(node);
  177. default:
  178. {
  179. if (input[pos] >= '0' && input[pos] <= '9')
  180. return extractFloat(node);
  181. return error("Value expected!");
  182. }
  183. }
  184. }
  185. bool JsonParser::extractWhitespace(bool verbose)
  186. {
  187. while (true)
  188. {
  189. while (pos < input.size() && (ui8)input[pos] <= ' ')
  190. {
  191. if (input[pos] == '\n')
  192. {
  193. lineCount++;
  194. lineStart = pos+1;
  195. }
  196. pos++;
  197. }
  198. if (pos >= input.size() || input[pos] != '/')
  199. break;
  200. pos++;
  201. if (pos == input.size())
  202. break;
  203. if (input[pos] == '/')
  204. pos++;
  205. else
  206. error("Comments must consist from two slashes!", true);
  207. while (pos < input.size() && input[pos] != '\n')
  208. pos++;
  209. }
  210. if (pos >= input.size() && verbose)
  211. return error("Unexpected end of file!");
  212. return true;
  213. }
  214. bool JsonParser::extractEscaping(std::string &str)
  215. {
  216. switch(input[pos])
  217. {
  218. break; case '\"': str += '\"';
  219. break; case '\\': str += '\\';
  220. break; case 'b': str += '\b';
  221. break; case 'f': str += '\f';
  222. break; case 'n': str += '\n';
  223. break; case 'r': str += '\r';
  224. break; case 't': str += '\t';
  225. break; case '/': str += '/';
  226. break; default: return error("Unknown escape sequence!", true);
  227. }
  228. return true;
  229. }
  230. bool JsonParser::extractString(std::string &str)
  231. {
  232. if (input[pos] != '\"')
  233. return error("String expected!");
  234. pos++;
  235. size_t first = pos;
  236. while (pos != input.size())
  237. {
  238. if (input[pos] == '\"') // Correct end of string
  239. {
  240. str.append( &input[first], pos-first);
  241. pos++;
  242. return true;
  243. }
  244. if (input[pos] == '\\') // Escaping
  245. {
  246. str.append( &input[first], pos-first);
  247. pos++;
  248. if (pos == input.size())
  249. break;
  250. extractEscaping(str);
  251. first = pos + 1;
  252. }
  253. if (input[pos] == '\n') // end-of-line
  254. {
  255. str.append( &input[first], pos-first);
  256. return error("Closing quote not found!", true);
  257. }
  258. if ((unsigned char)(input[pos]) < ' ') // control character
  259. {
  260. str.append( &input[first], pos-first);
  261. first = pos+1;
  262. error("Illegal character in the string!", true);
  263. }
  264. pos++;
  265. }
  266. return error("Unterminated string!");
  267. }
  268. bool JsonParser::extractString(JsonNode &node)
  269. {
  270. std::string str;
  271. if (!extractString(str))
  272. return false;
  273. node.setType(JsonNode::JsonType::DATA_STRING);
  274. node.String() = str;
  275. return true;
  276. }
  277. bool JsonParser::extractLiteral(const std::string &literal)
  278. {
  279. if (literal.compare(0, literal.size(), &input[pos], literal.size()) != 0)
  280. {
  281. while (pos < input.size() && ((input[pos]>'a' && input[pos]<'z')
  282. || (input[pos]>'A' && input[pos]<'Z')))
  283. pos++;
  284. return error("Unknown literal found", true);
  285. }
  286. pos += literal.size();
  287. return true;
  288. }
  289. bool JsonParser::extractNull(JsonNode &node)
  290. {
  291. if (!extractLiteral("null"))
  292. return false;
  293. node.clear();
  294. return true;
  295. }
  296. bool JsonParser::extractTrue(JsonNode &node)
  297. {
  298. if (!extractLiteral("true"))
  299. return false;
  300. node.Bool() = true;
  301. return true;
  302. }
  303. bool JsonParser::extractFalse(JsonNode &node)
  304. {
  305. if (!extractLiteral("false"))
  306. return false;
  307. node.Bool() = false;
  308. return true;
  309. }
  310. bool JsonParser::extractStruct(JsonNode &node)
  311. {
  312. node.setType(JsonNode::JsonType::DATA_STRUCT);
  313. pos++;
  314. if (!extractWhitespace())
  315. return false;
  316. //Empty struct found
  317. if (input[pos] == '}')
  318. {
  319. pos++;
  320. return true;
  321. }
  322. while (true)
  323. {
  324. if (!extractWhitespace())
  325. return false;
  326. std::string key;
  327. if (!extractString(key))
  328. return false;
  329. if (node.Struct().find(key) != node.Struct().end())
  330. error("Dublicated element encountered!", true);
  331. if (!extractSeparator())
  332. return false;
  333. if (!extractElement(node.Struct()[key], '}'))
  334. return false;
  335. if (input[pos] == '}')
  336. {
  337. pos++;
  338. return true;
  339. }
  340. }
  341. }
  342. bool JsonParser::extractArray(JsonNode &node)
  343. {
  344. pos++;
  345. node.setType(JsonNode::JsonType::DATA_VECTOR);
  346. if (!extractWhitespace())
  347. return false;
  348. //Empty array found
  349. if (input[pos] == ']')
  350. {
  351. pos++;
  352. return true;
  353. }
  354. while (true)
  355. {
  356. //NOTE: currently 50% of time is this vector resizing.
  357. //May be useful to use list during parsing and then swap() all items to vector
  358. node.Vector().resize(node.Vector().size()+1);
  359. if (!extractElement(node.Vector().back(), ']'))
  360. return false;
  361. if (input[pos] == ']')
  362. {
  363. pos++;
  364. return true;
  365. }
  366. }
  367. }
  368. bool JsonParser::extractElement(JsonNode &node, char terminator)
  369. {
  370. if (!extractValue(node))
  371. return false;
  372. if (!extractWhitespace())
  373. return false;
  374. bool comma = (input[pos] == ',');
  375. if (comma )
  376. {
  377. pos++;
  378. if (!extractWhitespace())
  379. return false;
  380. }
  381. if (input[pos] == terminator)
  382. {
  383. //FIXME: MOD COMPATIBILITY: Too many of these right now, re-enable later
  384. //if (comma)
  385. //error("Extra comma found!", true);
  386. return true;
  387. }
  388. if (!comma)
  389. error("Comma expected!", true);
  390. return true;
  391. }
  392. bool JsonParser::extractFloat(JsonNode &node)
  393. {
  394. assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
  395. bool negative=false;
  396. double result=0;
  397. si64 integerPart = 0;
  398. bool isFloat = false;
  399. if (input[pos] == '-')
  400. {
  401. pos++;
  402. negative = true;
  403. }
  404. if (input[pos] < '0' || input[pos] > '9')
  405. return error("Number expected!");
  406. //Extract integer part
  407. while (input[pos] >= '0' && input[pos] <= '9')
  408. {
  409. integerPart = integerPart*10+(input[pos]-'0');
  410. pos++;
  411. }
  412. result = integerPart;
  413. if (input[pos] == '.')
  414. {
  415. //extract fractional part
  416. isFloat = true;
  417. pos++;
  418. double fractMult = 0.1;
  419. if (input[pos] < '0' || input[pos] > '9')
  420. return error("Decimal part expected!");
  421. while (input[pos] >= '0' && input[pos] <= '9')
  422. {
  423. result = result + fractMult*(input[pos]-'0');
  424. fractMult /= 10;
  425. pos++;
  426. }
  427. }
  428. if(input[pos] == 'e')
  429. {
  430. //extract exponential part
  431. pos++;
  432. isFloat = true;
  433. bool powerNegative = false;
  434. double power = 0;
  435. if(input[pos] == '-')
  436. {
  437. pos++;
  438. powerNegative = true;
  439. }
  440. else if(input[pos] == '+')
  441. {
  442. pos++;
  443. }
  444. if (input[pos] < '0' || input[pos] > '9')
  445. return error("Exponential part expected!");
  446. while (input[pos] >= '0' && input[pos] <= '9')
  447. {
  448. power = power*10 + (input[pos]-'0');
  449. pos++;
  450. }
  451. if(powerNegative)
  452. power = -power;
  453. result *= std::pow(10, power);
  454. }
  455. if(isFloat)
  456. {
  457. if(negative)
  458. result = -result;
  459. node.setType(JsonNode::JsonType::DATA_FLOAT);
  460. node.Float() = result;
  461. }
  462. else
  463. {
  464. if(negative)
  465. integerPart = -integerPart;
  466. node.setType(JsonNode::JsonType::DATA_INTEGER);
  467. node.Integer() = integerPart;
  468. }
  469. return true;
  470. }
  471. bool JsonParser::error(const std::string &message, bool warning)
  472. {
  473. std::ostringstream stream;
  474. std::string type(warning?" warning: ":" error: ");
  475. stream << "At line " << lineCount << ", position "<<pos-lineStart
  476. << type << message <<"\n";
  477. errors += stream.str();
  478. return warning;
  479. }
  480. ///////////////////////////////////////////////////////////////////////////////
  481. //TODO: integer support
  482. static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
  483. {
  484. {"null", JsonNode::JsonType::DATA_NULL},
  485. {"boolean", JsonNode::JsonType::DATA_BOOL},
  486. {"number", JsonNode::JsonType::DATA_FLOAT},
  487. {"string", JsonNode::JsonType::DATA_STRING},
  488. {"array", JsonNode::JsonType::DATA_VECTOR},
  489. {"object", JsonNode::JsonType::DATA_STRUCT}
  490. };
  491. namespace
  492. {
  493. namespace Common
  494. {
  495. std::string emptyCheck(Validation::ValidationData &, const JsonNode &, const JsonNode &, const JsonNode &)
  496. {
  497. // check is not needed - e.g. incorporated into another check
  498. return "";
  499. }
  500. std::string notImplementedCheck(Validation::ValidationData &, const JsonNode &, const JsonNode &, const JsonNode &)
  501. {
  502. return "Not implemented entry in schema";
  503. }
  504. std::string schemaListCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data,
  505. std::string errorMsg, std::function<bool(size_t)> isValid)
  506. {
  507. std::string errors = "<tested schemas>\n";
  508. size_t result = 0;
  509. for(auto & schemaEntry : schema.Vector())
  510. {
  511. std::string error = check(schemaEntry, data, validator);
  512. if (error.empty())
  513. {
  514. result++;
  515. }
  516. else
  517. {
  518. errors += error;
  519. errors += "<end of schema>\n";
  520. }
  521. }
  522. if (isValid(result))
  523. return "";
  524. else
  525. return validator.makeErrorMessage(errorMsg) + errors;
  526. }
  527. std::string allOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  528. {
  529. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&](size_t count)
  530. {
  531. return count == schema.Vector().size();
  532. });
  533. }
  534. std::string anyOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  535. {
  536. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [&](size_t count)
  537. {
  538. return count > 0;
  539. });
  540. }
  541. std::string oneOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  542. {
  543. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [&](size_t count)
  544. {
  545. return count == 1;
  546. });
  547. }
  548. std::string notCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  549. {
  550. if (check(schema, data, validator).empty())
  551. return validator.makeErrorMessage("Successful validation against negative check");
  552. return "";
  553. }
  554. std::string enumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  555. {
  556. for(auto & enumEntry : schema.Vector())
  557. {
  558. if (data == enumEntry)
  559. return "";
  560. }
  561. return validator.makeErrorMessage("Key must have one of predefined values");
  562. }
  563. std::string typeCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  564. {
  565. const auto typeName = schema.String();
  566. auto it = stringToType.find(typeName);
  567. if(it == stringToType.end())
  568. {
  569. return validator.makeErrorMessage("Unknown type in schema:" + typeName);
  570. }
  571. JsonNode::JsonType type = it->second;
  572. //FIXME: hack for integer values
  573. if(data.isNumber() && type == JsonNode::JsonType::DATA_FLOAT)
  574. return "";
  575. if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
  576. return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
  577. return "";
  578. }
  579. std::string refCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  580. {
  581. std::string URI = schema.String();
  582. //node must be validated using schema pointed by this reference and not by data here
  583. //Local reference. Turn it into more easy to handle remote ref
  584. if (boost::algorithm::starts_with(URI, "#"))
  585. URI = validator.usedSchemas.back() + URI;
  586. return check(URI, data, validator);
  587. }
  588. std::string formatCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  589. {
  590. auto formats = Validation::getKnownFormats();
  591. std::string errors;
  592. auto checker = formats.find(schema.String());
  593. if (checker != formats.end())
  594. {
  595. std::string result = checker->second(data);
  596. if (!result.empty())
  597. errors += validator.makeErrorMessage(result);
  598. }
  599. else
  600. errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
  601. return errors;
  602. }
  603. }
  604. namespace String
  605. {
  606. std::string maxLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  607. {
  608. if (data.String().size() > schema.Float())
  609. return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
  610. return "";
  611. }
  612. std::string minLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  613. {
  614. if (data.String().size() < schema.Float())
  615. return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
  616. return "";
  617. }
  618. }
  619. namespace Number
  620. {
  621. std::string maximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  622. {
  623. if (baseSchema["exclusiveMaximum"].Bool())
  624. {
  625. if (data.Float() >= schema.Float())
  626. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  627. }
  628. else
  629. {
  630. if (data.Float() > schema.Float())
  631. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  632. }
  633. return "";
  634. }
  635. std::string minimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  636. {
  637. if (baseSchema["exclusiveMinimum"].Bool())
  638. {
  639. if (data.Float() <= schema.Float())
  640. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  641. }
  642. else
  643. {
  644. if (data.Float() < schema.Float())
  645. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  646. }
  647. return "";
  648. }
  649. std::string multipleOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  650. {
  651. double result = data.Float() / schema.Float();
  652. if (floor(result) != result)
  653. return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
  654. return "";
  655. }
  656. }
  657. namespace Vector
  658. {
  659. std::string itemEntryCheck(Validation::ValidationData & validator, const JsonVector items, const JsonNode & schema, size_t index)
  660. {
  661. validator.currentPath.push_back(JsonNode());
  662. validator.currentPath.back().Float() = index;
  663. auto onExit = vstd::makeScopeGuard([&]()
  664. {
  665. validator.currentPath.pop_back();
  666. });
  667. if (!schema.isNull())
  668. return check(schema, items[index], validator);
  669. return "";
  670. }
  671. std::string itemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  672. {
  673. std::string errors;
  674. for (size_t i=0; i<data.Vector().size(); i++)
  675. {
  676. if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
  677. {
  678. if (schema.Vector().size() > i)
  679. errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
  680. }
  681. else
  682. {
  683. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  684. }
  685. }
  686. return errors;
  687. }
  688. std::string additionalItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  689. {
  690. std::string errors;
  691. // "items" is struct or empty (defaults to empty struct) - validation always successful
  692. const JsonNode & items = baseSchema["items"];
  693. if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
  694. return "";
  695. for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
  696. {
  697. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  698. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  699. else if (!schema.isNull() && schema.Bool() == false)
  700. errors += validator.makeErrorMessage("Unknown entry found");
  701. }
  702. return errors;
  703. }
  704. std::string minItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  705. {
  706. if (data.Vector().size() < schema.Float())
  707. return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
  708. return "";
  709. }
  710. std::string maxItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  711. {
  712. if (data.Vector().size() > schema.Float())
  713. return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
  714. return "";
  715. }
  716. std::string uniqueItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  717. {
  718. if (schema.Bool())
  719. {
  720. for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
  721. {
  722. auto itB = itA;
  723. while (++itB != schema.Vector().end())
  724. {
  725. if (*itA == *itB)
  726. return validator.makeErrorMessage("List must consist from unique items");
  727. }
  728. }
  729. }
  730. return "";
  731. }
  732. }
  733. namespace Struct
  734. {
  735. std::string maxPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  736. {
  737. if (data.Struct().size() > schema.Float())
  738. return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
  739. return "";
  740. }
  741. std::string minPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  742. {
  743. if (data.Struct().size() < schema.Float())
  744. return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
  745. return "";
  746. }
  747. std::string uniquePropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  748. {
  749. for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
  750. {
  751. auto itB = itA;
  752. while (++itB != data.Struct().end())
  753. {
  754. if (itA->second == itB->second)
  755. return validator.makeErrorMessage("List must consist from unique items");
  756. }
  757. }
  758. return "";
  759. }
  760. std::string requiredCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  761. {
  762. std::string errors;
  763. for(auto & required : schema.Vector())
  764. {
  765. if (data[required.String()].isNull())
  766. errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
  767. }
  768. return errors;
  769. }
  770. std::string dependenciesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  771. {
  772. std::string errors;
  773. for(auto & deps : schema.Struct())
  774. {
  775. if (!data[deps.first].isNull())
  776. {
  777. if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
  778. {
  779. JsonVector depList = deps.second.Vector();
  780. for(auto & depEntry : depList)
  781. {
  782. if (data[depEntry.String()].isNull())
  783. errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
  784. }
  785. }
  786. else
  787. {
  788. if (!check(deps.second, data, validator).empty())
  789. errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
  790. }
  791. }
  792. }
  793. return errors;
  794. }
  795. std::string propertyEntryCheck(Validation::ValidationData & validator, const JsonNode &node, const JsonNode & schema, std::string nodeName)
  796. {
  797. validator.currentPath.push_back(JsonNode());
  798. validator.currentPath.back().String() = nodeName;
  799. auto onExit = vstd::makeScopeGuard([&]()
  800. {
  801. validator.currentPath.pop_back();
  802. });
  803. // there is schema specifically for this item
  804. if (!schema.isNull())
  805. return check(schema, node, validator);
  806. return "";
  807. }
  808. std::string propertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  809. {
  810. std::string errors;
  811. for(auto & entry : data.Struct())
  812. errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
  813. return errors;
  814. }
  815. std::string additionalPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  816. {
  817. std::string errors;
  818. for(auto & entry : data.Struct())
  819. {
  820. if (baseSchema["properties"].Struct().count(entry.first) == 0)
  821. {
  822. // try generic additionalItems schema
  823. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  824. errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
  825. // or, additionalItems field can be bool which indicates if such items are allowed
  826. else if (!schema.isNull() && schema.Bool() == false) // present and set to false - error
  827. errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
  828. }
  829. }
  830. return errors;
  831. }
  832. }
  833. namespace Formats
  834. {
  835. bool testFilePresence(std::string scope, ResourceID resource)
  836. {
  837. std::set<std::string> allowedScopes;
  838. if (scope != "core" && scope != "") // all real mods may have dependencies
  839. {
  840. //NOTE: recursive dependencies are not allowed at the moment - update code if this changes
  841. allowedScopes = VLC->modh->getModData(scope).dependencies;
  842. allowedScopes.insert("core"); // all mods can use H3 files
  843. }
  844. allowedScopes.insert(scope); // mods can use their own files
  845. for (auto & entry : allowedScopes)
  846. {
  847. if (CResourceHandler::get(entry)->existsResource(resource))
  848. return true;
  849. }
  850. return false;
  851. }
  852. #define TEST_FILE(scope, prefix, file, type) \
  853. if (testFilePresence(scope, ResourceID(prefix + file, type))) \
  854. return ""
  855. std::string testAnimation(std::string path, std::string scope)
  856. {
  857. TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
  858. TEST_FILE(scope, "Sprites/", path, EResType::TEXT);
  859. return "Animation file \"" + path + "\" was not found";
  860. }
  861. std::string textFile(const JsonNode & node)
  862. {
  863. TEST_FILE(node.meta, "", node.String(), EResType::TEXT);
  864. return "Text file \"" + node.String() + "\" was not found";
  865. }
  866. std::string musicFile(const JsonNode & node)
  867. {
  868. TEST_FILE(node.meta, "", node.String(), EResType::MUSIC);
  869. return "Music file \"" + node.String() + "\" was not found";
  870. }
  871. std::string soundFile(const JsonNode & node)
  872. {
  873. TEST_FILE(node.meta, "Sounds/", node.String(), EResType::SOUND);
  874. return "Sound file \"" + node.String() + "\" was not found";
  875. }
  876. std::string defFile(const JsonNode & node)
  877. {
  878. TEST_FILE(node.meta, "Sprites/", node.String(), EResType::ANIMATION);
  879. return "Def file \"" + node.String() + "\" was not found";
  880. }
  881. std::string animationFile(const JsonNode & node)
  882. {
  883. return testAnimation(node.String(), node.meta);
  884. }
  885. std::string imageFile(const JsonNode & node)
  886. {
  887. TEST_FILE(node.meta, "Data/", node.String(), EResType::IMAGE);
  888. TEST_FILE(node.meta, "Sprites/", node.String(), EResType::IMAGE);
  889. if (node.String().find(':') != std::string::npos)
  890. return testAnimation(node.String().substr(0, node.String().find(':')), node.meta);
  891. return "Image file \"" + node.String() + "\" was not found";
  892. }
  893. std::string videoFile(const JsonNode & node)
  894. {
  895. TEST_FILE(node.meta, "Video/", node.String(), EResType::VIDEO);
  896. return "Video file \"" + node.String() + "\" was not found";
  897. }
  898. #undef TEST_FILE
  899. }
  900. Validation::TValidatorMap createCommonFields()
  901. {
  902. Validation::TValidatorMap ret;
  903. ret["format"] = Common::formatCheck;
  904. ret["allOf"] = Common::allOfCheck;
  905. ret["anyOf"] = Common::anyOfCheck;
  906. ret["oneOf"] = Common::oneOfCheck;
  907. ret["enum"] = Common::enumCheck;
  908. ret["type"] = Common::typeCheck;
  909. ret["not"] = Common::notCheck;
  910. ret["$ref"] = Common::refCheck;
  911. // fields that don't need implementation
  912. ret["title"] = Common::emptyCheck;
  913. ret["$schema"] = Common::emptyCheck;
  914. ret["default"] = Common::emptyCheck;
  915. ret["description"] = Common::emptyCheck;
  916. ret["definitions"] = Common::emptyCheck;
  917. return ret;
  918. }
  919. Validation::TValidatorMap createStringFields()
  920. {
  921. Validation::TValidatorMap ret = createCommonFields();
  922. ret["maxLength"] = String::maxLengthCheck;
  923. ret["minLength"] = String::minLengthCheck;
  924. ret["pattern"] = Common::notImplementedCheck;
  925. return ret;
  926. }
  927. Validation::TValidatorMap createNumberFields()
  928. {
  929. Validation::TValidatorMap ret = createCommonFields();
  930. ret["maximum"] = Number::maximumCheck;
  931. ret["minimum"] = Number::minimumCheck;
  932. ret["multipleOf"] = Number::multipleOfCheck;
  933. ret["exclusiveMaximum"] = Common::emptyCheck;
  934. ret["exclusiveMinimum"] = Common::emptyCheck;
  935. return ret;
  936. }
  937. Validation::TValidatorMap createVectorFields()
  938. {
  939. Validation::TValidatorMap ret = createCommonFields();
  940. ret["items"] = Vector::itemsCheck;
  941. ret["minItems"] = Vector::minItemsCheck;
  942. ret["maxItems"] = Vector::maxItemsCheck;
  943. ret["uniqueItems"] = Vector::uniqueItemsCheck;
  944. ret["additionalItems"] = Vector::additionalItemsCheck;
  945. return ret;
  946. }
  947. Validation::TValidatorMap createStructFields()
  948. {
  949. Validation::TValidatorMap ret = createCommonFields();
  950. ret["additionalProperties"] = Struct::additionalPropertiesCheck;
  951. ret["uniqueProperties"] = Struct::uniquePropertiesCheck;
  952. ret["maxProperties"] = Struct::maxPropertiesCheck;
  953. ret["minProperties"] = Struct::minPropertiesCheck;
  954. ret["dependencies"] = Struct::dependenciesCheck;
  955. ret["properties"] = Struct::propertiesCheck;
  956. ret["required"] = Struct::requiredCheck;
  957. ret["patternProperties"] = Common::notImplementedCheck;
  958. return ret;
  959. }
  960. Validation::TFormatMap createFormatMap()
  961. {
  962. Validation::TFormatMap ret;
  963. ret["textFile"] = Formats::textFile;
  964. ret["musicFile"] = Formats::musicFile;
  965. ret["soundFile"] = Formats::soundFile;
  966. ret["defFile"] = Formats::defFile;
  967. ret["animationFile"] = Formats::animationFile;
  968. ret["imageFile"] = Formats::imageFile;
  969. ret["videoFile"] = Formats::videoFile;
  970. return ret;
  971. }
  972. }
  973. namespace Validation
  974. {
  975. std::string ValidationData::makeErrorMessage(const std::string &message)
  976. {
  977. std::string errors;
  978. errors += "At ";
  979. if (!currentPath.empty())
  980. {
  981. for(const JsonNode &path : currentPath)
  982. {
  983. errors += "/";
  984. if (path.getType() == JsonNode::JsonType::DATA_STRING)
  985. errors += path.String();
  986. else
  987. errors += boost::lexical_cast<std::string>(static_cast<unsigned>(path.Float()));
  988. }
  989. }
  990. else
  991. errors += "<root>";
  992. errors += "\n\t Error: " + message + "\n";
  993. return errors;
  994. }
  995. std::string check(std::string schemaName, const JsonNode & data)
  996. {
  997. ValidationData validator;
  998. return check(schemaName, data, validator);
  999. }
  1000. std::string check(std::string schemaName, const JsonNode & data, ValidationData & validator)
  1001. {
  1002. validator.usedSchemas.push_back(schemaName);
  1003. auto onscopeExit = vstd::makeScopeGuard([&]()
  1004. {
  1005. validator.usedSchemas.pop_back();
  1006. });
  1007. return check(JsonUtils::getSchema(schemaName), data, validator);
  1008. }
  1009. std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator)
  1010. {
  1011. const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
  1012. std::string errors;
  1013. for(auto & entry : schema.Struct())
  1014. {
  1015. auto checker = knownFields.find(entry.first);
  1016. if (checker != knownFields.end())
  1017. errors += checker->second(validator, schema, entry.second, data);
  1018. //else
  1019. // errors += validator.makeErrorMessage("Unknown entry in schema " + entry.first);
  1020. }
  1021. return errors;
  1022. }
  1023. const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type)
  1024. {
  1025. static const TValidatorMap commonFields = createCommonFields();
  1026. static const TValidatorMap numberFields = createNumberFields();
  1027. static const TValidatorMap stringFields = createStringFields();
  1028. static const TValidatorMap vectorFields = createVectorFields();
  1029. static const TValidatorMap structFields = createStructFields();
  1030. switch (type)
  1031. {
  1032. case JsonNode::JsonType::DATA_FLOAT:
  1033. case JsonNode::JsonType::DATA_INTEGER:
  1034. return numberFields;
  1035. case JsonNode::JsonType::DATA_STRING: return stringFields;
  1036. case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
  1037. case JsonNode::JsonType::DATA_STRUCT: return structFields;
  1038. default: return commonFields;
  1039. }
  1040. }
  1041. const TFormatMap & getKnownFormats()
  1042. {
  1043. static TFormatMap knownFormats = createFormatMap();
  1044. return knownFormats;
  1045. }
  1046. } // Validation namespace