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 (e.key (), string (rule[" pointer" ]));
39+ if (std::get<0 >(subset))
40+ if (!out_flat.contains (std::get<1 >(subset)))
41+ out_flat[std::get<1 >(subset)] = rule[" default" ];
42+
43+ // Try it also without the last entry to capture object types which are empty
44+ string key_parent = e.key ().substr (0 ,e.key ().find_last_of (" /\\ " ));
45+
46+ subset = is_subset_pointer (key_parent, string (rule[" pointer" ]));
47+ if (std::get<0 >(subset))
48+ if (!out_flat.contains (std::get<1 >(subset)))
49+ out_flat[std::get<1 >(subset)] = rule[" default" ];
50+
51+ }
52+ // Special case as "/" is not inserted in the flat representation
53+ std::tuple<bool , string> subset = is_subset_pointer (" /" , string (rule[" pointer" ]));
54+ if (std::get<0 >(subset))
55+ if (!out_flat.contains (std::get<1 >(subset)))
56+ out_flat[std::get<1 >(subset)] = rule[" default" ];
57+ }
58+
59+ // Unflatten the input
60+ json output = out_flat.unflatten ();
61+
62+ // Certify the validity of the final file
63+ assert (verify_json (input, rules));
64+
65+ return output;
66+ };
67+
68+ std::string SJV::log2str ()
69+ {
70+ std::stringstream s;
71+
72+ for (log_item i : log)
73+ {
74+ s << i.first << " : " << i.second << std::endl;
75+ }
76+ return s.str ();
77+ };
78+
79+ // ////////// PRIVATE
1580
1681 bool SJV::verify_json (const string &pointer, const json &input, const json &rules)
1782 {
@@ -26,6 +91,8 @@ namespace sjv
2691
2792 if (strict)
2893 return false ;
94+ else
95+ return true ;
2996 }
3097
3198 // Test all rules, only one must pass, otherwise throw exception
@@ -35,7 +102,7 @@ namespace sjv
35102 if (verify_rule (input, i))
36103 count++;
37104
38- if (count == 0 && !matching_rules.empty ())
105+ if (count == 0 && !matching_rules.empty ())
39106 {
40107 // Before giving up, try boxing a primitive type
41108 if (boxing_primitive && !input.is_array ())
@@ -44,7 +111,7 @@ namespace sjv
44111 // Make sure there are some rules for the boxed version before recursively checking
45112 if (collect_pointer (new_pointer, rules).size () > 0 )
46113 if (verify_json (new_pointer, input, rules))
47- return true ;
114+ return true ;
48115 }
49116
50117 std::stringstream s;
@@ -274,17 +341,21 @@ namespace sjv
274341 return false ;
275342
276343 return true ;
277- }
344+ };
278345
279- std::string SJV::log2str ( )
346+ json SJV::collect_default_rules ( const json &rules )
280347 {
281- std::stringstream s;
348+ // Find all rules that apply for the input node
349+ // TODO: accelerate this
282350
283- for (log_item i : log)
351+ std::vector<json> matching_rules;
352+ for (auto i : rules)
284353 {
285- s << i.first << " : " << i.second << std::endl;
354+ if (i.contains (" default" ))
355+ matching_rules.push_back (i);
286356 }
287- return s.str ();
357+
358+ return matching_rules;
288359 };
289360
290361 json SJV::collect_default_rules (const string &pointer, const json &rules)
@@ -307,7 +378,7 @@ namespace sjv
307378 return std::find (list.begin (), list.end (), item) != list.end ();
308379 };
309380
310- std::vector<json> SJV::collect_pointer (const string& pointer, const json& rules)
381+ std::vector<json> SJV::collect_pointer (const string & pointer, const json & rules)
311382 {
312383 std::vector<json> matching_rules;
313384 for (auto i : rules)
@@ -317,4 +388,89 @@ namespace sjv
317388 }
318389 return matching_rules;
319390 };
391+
392+ json SJV::find_valid_rule (const string &pointer, const json &input, const json &rules)
393+ {
394+ for (auto i : collect_pointer (pointer, rules))
395+ if (verify_rule (input, i))
396+ return i;
397+
398+ return json ();
399+ };
400+
401+ std::tuple<bool , string> SJV::is_subset_pointer (const string &json, const string &pointer)
402+ {
403+ // Splits a string into tokens using the deliminator delim
404+ auto tokenize = [](std::string const &str, const char delim) {
405+ size_t start;
406+ size_t end = 0 ;
407+ std::vector<string> out;
408+
409+ while ((start = str.find_first_not_of (delim, end)) != std::string::npos)
410+ {
411+ end = str.find (delim, start);
412+ out.push_back (str.substr (start, end - start));
413+ }
414+
415+ return out;
416+ };
417+
418+ // Replaces occurrences of a substring
419+ auto replace_all = [](std::string str, const std::string &from, const std::string &to) {
420+ size_t start_pos = 0 ;
421+ while ((start_pos = str.find (from, start_pos)) != std::string::npos)
422+ {
423+ str.replace (start_pos, from.length (), to);
424+ start_pos += to.length (); // Handles case where 'to' is a substring of 'from'
425+ }
426+ return str;
427+ };
428+
429+ // Check if a string is an integer
430+ auto is_number = [](const string &str) {
431+ for (char const &c : str)
432+ if (std::isdigit (c) == 0 )
433+ return false ;
434+ return true ;
435+ };
436+
437+ // Tokenize json_pointer
438+ std::vector<string> json_t = tokenize (json, ' /' );
439+ std::vector<string> pointer_t = tokenize (pointer, ' /' );
440+
441+ // if the json is not shorter or if there are no tokens, give up
442+ if ((json_t .size () >= pointer_t .size ())
443+ || (pointer_t .size () == 0 ))
444+ return {false , " " };
445+
446+ std::string buf = " " ;
447+ // if it is shorter, match every entry
448+ for (unsigned i = 0 ; i < pointer_t .size (); ++i)
449+ {
450+ // if there is no corresponding entry on json, copy and move on
451+ if (json_t .size () <= i)
452+ {
453+ buf.append (" /" + replace_all (pointer_t [i], " *" , " 0" ));
454+ }
455+ // if there is an entry on json and it is the same, move on
456+ else if (json_t [i] == pointer_t [i])
457+ {
458+ buf.append (" /" + pointer_t [i]);
459+ }
460+ // if the pointer contains a star, then accept any integer on json
461+ else if (pointer_t [i] == " *" )
462+ {
463+ if (is_number (json_t [i]))
464+ buf.append (" /" + json_t [i]);
465+ }
466+ // if no rule matches it is not a match
467+ else
468+ {
469+ return {false , " " };
470+ }
471+ }
472+
473+ return {true , buf};
474+ };
475+
320476} // namespace sjv
0 commit comments