JsonDetail.cpp 34 KB

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