33#include < iostream>
44#include < filesystem> // C++17
55#include < sstream>
6+ #include < algorithm>
67// //////////////////////////////////////////////////////////////////////////////
78
89namespace sjv
910{
11+
12+ // ////////// PUBLIC
13+
1014 bool SJV::verify_json (const json &input, const json &rules)
1115 {
1216 log.clear ();
1317 return verify_json (" /" , input, rules);
14- }
18+ };
19+
20+ json SJV::inject_defaults (const json &input, const json &rules)
21+ {
22+ // The code below assumes that the input satisfies the rules
23+ assert (verify_json (input, rules));
24+
25+ // Find all the default rules
26+ json default_rules = collect_default_rules (rules);
27+
28+ // Flatten the input
29+ json flat = input.flatten ();
30+ json out_flat = flat;
31+
32+ // For each rule, go over all entries of a flattened input
33+ for (auto rule : default_rules)
34+ {
35+ for (auto e : flat.items ())
36+ {
37+ // If the pointer matches a strict subset of it, add it to the flattened
38+ std::tuple<bool , string> subset = is_subset_pointer (string (rule[" pointer" ]), e.key ());
39+ if (std::get<0 >(subset))
40+ out_flat[std::get<1 >(subset)] = rule[" default" ];
41+ }
42+ }
43+
44+ // Unflatten the input
45+ json output = out_flat.unflatten ();
46+
47+ // Certify the validity of the final file
48+ assert (verify_json (input, rules));
49+
50+ return output;
51+ };
52+
53+ std::string SJV::log2str ()
54+ {
55+ std::stringstream s;
56+
57+ for (log_item i : log)
58+ {
59+ s << i.first << " : " << i.second << std::endl;
60+ }
61+ return s.str ();
62+ };
63+
64+ // ////////// PRIVATE
1565
1666 bool SJV::verify_json (const string &pointer, const json &input, const json &rules)
1767 {
@@ -44,7 +94,7 @@ namespace sjv
4494 // Make sure there are some rules for the boxed version before recursively checking
4595 if (collect_pointer (new_pointer, rules).size () > 0 )
4696 if (verify_json (new_pointer, input, rules))
47- return true ;
97+ return true ;
4898 }
4999
50100 std::stringstream s;
@@ -274,17 +324,21 @@ namespace sjv
274324 return false ;
275325
276326 return true ;
277- }
327+ };
278328
279- std::string SJV::log2str ( )
329+ json SJV::collect_default_rules ( const json &rules )
280330 {
281- std::stringstream s;
331+ // Find all rules that apply for the input node
332+ // TODO: accelerate this
282333
283- for (log_item i : log)
334+ std::vector<json> matching_rules;
335+ for (auto i : rules)
284336 {
285- s << i.first << " : " << i.second << std::endl;
337+ if (i.contains (" default" ))
338+ matching_rules.push_back (i);
286339 }
287- return s.str ();
340+
341+ return matching_rules;
288342 };
289343
290344 json SJV::collect_default_rules (const string &pointer, const json &rules)
@@ -307,7 +361,7 @@ namespace sjv
307361 return std::find (list.begin (), list.end (), item) != list.end ();
308362 };
309363
310- std::vector<json> SJV::collect_pointer (const string& pointer, const json& rules)
364+ std::vector<json> SJV::collect_pointer (const string & pointer, const json & rules)
311365 {
312366 std::vector<json> matching_rules;
313367 for (auto i : rules)
@@ -317,4 +371,84 @@ namespace sjv
317371 }
318372 return matching_rules;
319373 };
374+
375+ json SJV::find_valid_rule (const string &pointer, const json &input, const json &rules)
376+ {
377+ for (auto i : collect_pointer (pointer, rules))
378+ if (verify_rule (input, i))
379+ return i;
380+
381+ return json ();
382+ };
383+
384+ std::tuple<bool , string> SJV::is_subset_pointer (const string &json, const string &pointer)
385+ {
386+
387+ // Splits a string into tokens using the deliminator delim
388+ auto tokenize = [](std::string const &str, const char delim) {
389+ size_t start;
390+ size_t end = 0 ;
391+ std::vector<string> out;
392+
393+ while ((start = str.find_first_not_of (delim, end)) != std::string::npos)
394+ {
395+ end = str.find (delim, start);
396+ out.push_back (str.substr (start, end - start));
397+ }
398+
399+ return out;
400+ };
401+
402+ // Replaces occurrences of a substring
403+ auto replace_all = [](std::string str, const std::string &from, const std::string &to) {
404+ size_t start_pos = 0 ;
405+ while ((start_pos = str.find (from, start_pos)) != std::string::npos)
406+ {
407+ str.replace (start_pos, from.length (), to);
408+ start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
409+ }
410+ return str;
411+ };
412+
413+ // Check if a string is an integer
414+ auto is_number = [](const string &str) {
415+ for (char const &c : str)
416+ if (std::isdigit (c) == 0 )
417+ return false ;
418+ return true ;
419+ };
420+
421+ // Tokenize json_pointer
422+ std::vector<string> json_t = tokenize (json, ' /' );
423+ std::vector<string> pointer_t = tokenize (pointer, ' /' );
424+
425+ // if the json is not shorter or if there are no tokens, give up
426+ if ((json_t .size () >= pointer_t .size ())
427+ || (pointer_t .size () == 0 ))
428+ return {false , " " };
429+
430+ std::string buf = " /" ;
431+ // if it is shorter, match every entry
432+ for (unsigned i = 0 ; i < pointer_t .size (); ++i)
433+ {
434+ // if there is no corresponding entry on json, copy and move on
435+ if (json_t .size () < i)
436+ buf.append (replace_all (pointer_t [i], " *" , " 0" ) + " /" );
437+
438+ // if there is an entry on json and it is the same, move on
439+ if (json_t [i] == pointer_t [i])
440+ buf.append (pointer_t [i] + " /" );
441+
442+ // if the pointer contains a star, then accept any integer on json
443+ if (pointer_t [i] == " *" )
444+ if (is_number (json_t [i]))
445+ buf.append (json_t [i] + " /" );
446+
447+ // if no rule matches it is not a match
448+ return {false , " " };
449+ }
450+
451+ return {true , buf};
452+ };
453+
320454} // namespace sjv
0 commit comments