JsonDetail.cpp 34 KB

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