@@ -513,6 +513,18 @@ auto walk_schema(const sourcemeta::core::JSON &schema, bool include_root,
513513 const sourcemeta::core::JSON &root, VisitedSchemas &visited,
514514 std::size_t &next_identifier) -> sourcemeta::core::JSON;
515515
516+ auto is_complex_schema (const sourcemeta::core::JSON &schema) -> bool;
517+
518+ auto walk_branching_subschema (const std::string &label,
519+ const std::string &synthetic_name,
520+ const sourcemeta::core::JSON &inner_schema,
521+ sourcemeta::core::JSON &doc_children,
522+ const sourcemeta::core::SchemaFrame &frame,
523+ const sourcemeta::core::JSON &root,
524+ VisitedSchemas &visited,
525+ std::size_t &next_identifier,
526+ bool include_properties) -> void;
527+
516528auto walk_branches (const std::string &keyword, const std::string &label,
517529 const sourcemeta::core::JSON &schema,
518530 sourcemeta::core::JSON &children,
@@ -574,8 +586,9 @@ auto resolve_ref(const sourcemeta::core::JSON &schema,
574586auto emit_row (const sourcemeta::core::JSON &schema, sourcemeta::core::JSON path,
575587 sourcemeta::core::JSON &rows,
576588 const sourcemeta::core::SchemaFrame &frame,
577- const sourcemeta::core::JSON &root, const VisitedSchemas &visited,
578- std::size_t &next_identifier) -> void {
589+ const sourcemeta::core::JSON &root, VisitedSchemas &visited,
590+ std::size_t &next_identifier,
591+ const bool expand_applicators = true ) -> void {
579592 auto row{sourcemeta::core::JSON::make_object ()};
580593 row.assign (" identifier" , sourcemeta::core::JSON {
581594 static_cast <std::int64_t >(next_identifier++)});
@@ -619,6 +632,34 @@ auto emit_row(const sourcemeta::core::JSON &schema, sourcemeta::core::JSON path,
619632 }
620633 }
621634
635+ if (expand_applicators && is_complex_schema (schema)) {
636+ auto row_children{sourcemeta::core::JSON::make_array ()};
637+ walk_branches (" anyOf" , " Any of" , schema, row_children, frame, root, visited,
638+ next_identifier);
639+ walk_branches (" oneOf" , " One of" , schema, row_children, frame, root, visited,
640+ next_identifier);
641+ walk_all_of (schema, rows, row_children, frame, root, visited,
642+ next_identifier);
643+ walk_if_then_else (schema, row_children, frame, root, visited,
644+ next_identifier);
645+ if (schema.is_object () && schema.defines (" not" )) {
646+ const auto ¬_schema{schema.at (" not" )};
647+ const auto has_inline{
648+ not_schema.is_object () &&
649+ !(not_schema.defines (" anyOf" ) || not_schema.defines (" oneOf" ) ||
650+ not_schema.defines (" allOf" ) || not_schema.defines (" not" )) &&
651+ !constraints_of (not_schema).empty ()};
652+ if (!has_inline) {
653+ walk_branching_subschema (" Must NOT match" , " value" , not_schema,
654+ row_children, frame, root, visited,
655+ next_identifier, false );
656+ }
657+ }
658+ if (!row_children.empty ()) {
659+ row.assign (" children" , std::move (row_children));
660+ }
661+ }
662+
622663 rows.push_back (std::move (row));
623664}
624665
@@ -688,6 +729,35 @@ auto walk_properties(const sourcemeta::core::JSON &schema,
688729
689730 const auto row_identifier{
690731 static_cast <std::size_t >(row.at (" identifier" ).to_integer ())};
732+
733+ if (is_complex_schema (resolved)) {
734+ auto prop_children{sourcemeta::core::JSON::make_array ()};
735+ walk_branches (" anyOf" , " Any of" , resolved, prop_children, frame, root,
736+ visited, next_identifier);
737+ walk_branches (" oneOf" , " One of" , resolved, prop_children, frame, root,
738+ visited, next_identifier);
739+ walk_all_of (resolved, rows, prop_children, frame, root, visited,
740+ next_identifier);
741+ walk_if_then_else (resolved, prop_children, frame, root, visited,
742+ next_identifier);
743+ if (resolved.defines (" not" )) {
744+ const auto ¬_schema{resolved.at (" not" )};
745+ const auto has_inline{
746+ not_schema.is_object () &&
747+ !(not_schema.defines (" anyOf" ) || not_schema.defines (" oneOf" ) ||
748+ not_schema.defines (" allOf" ) || not_schema.defines (" not" )) &&
749+ !constraints_of (not_schema).empty ()};
750+ if (!has_inline) {
751+ walk_branching_subschema (" Must NOT match" , " value" , not_schema,
752+ prop_children, frame, root, visited,
753+ next_identifier, false );
754+ }
755+ }
756+ if (!prop_children.empty ()) {
757+ row.assign (" children" , std::move (prop_children));
758+ }
759+ }
760+
691761 rows.push_back (std::move (row));
692762
693763 if (resolved.is_object () && resolved.defines (" type" ) &&
@@ -786,7 +856,63 @@ auto walk_wildcard_keyword(const sourcemeta::core::JSON &schema,
786856
787857 auto path{base_path};
788858 path.push_back (make_path_segment (" wildcard" , " *" ));
789- emit_row (value, std::move (path), rows, frame, root, visited, next_identifier);
859+ const auto wildcard_row_id{next_identifier};
860+ emit_row (value, path, rows, frame, root, visited, next_identifier);
861+
862+ if (value.defines (" type" ) && value.at (" type" ).is_string () &&
863+ value.at (" type" ).to_string () == " object" ) {
864+ visited.emplace (&value,
865+ VisitedEntry{.identifier = wildcard_row_id, .path = path});
866+ walk_properties (value, path, rows, frame, root, visited, next_identifier);
867+ walk_pattern_properties (value, path, rows, frame, root, visited,
868+ next_identifier);
869+ walk_wildcard_keyword (value, " additionalProperties" , path, rows, frame,
870+ root, visited, next_identifier);
871+ walk_wildcard_keyword (value, " unevaluatedProperties" , path, rows, frame,
872+ root, visited, next_identifier);
873+ if (!value.defines (" additionalProperties" ) &&
874+ !value.defines (" unevaluatedProperties" )) {
875+ auto open_path{path};
876+ open_path.push_back (make_path_segment (" wildcard" , " *" ));
877+ emit_row (sourcemeta::core::JSON {true }, std::move (open_path), rows, frame,
878+ root, visited, next_identifier);
879+ }
880+ visited.erase (&value);
881+ } else if (value.defines (" type" ) && value.at (" type" ).is_string () &&
882+ value.at (" type" ).to_string () == " array" &&
883+ value.defines (" items" ) && value.at (" items" ).is_object () &&
884+ !value.defines (" prefixItems" )) {
885+ const auto &items_schema{
886+ resolve_ref (value.at (" items" ), frame, root, visited)};
887+ if (items_schema.is_object ()) {
888+ auto items_path{path};
889+ items_path.push_back (make_path_segment (" wildcard" , " *" ));
890+ const auto items_row_id{next_identifier};
891+ emit_row (items_schema, items_path, rows, frame, root, visited,
892+ next_identifier);
893+ if (items_schema.defines (" type" ) && items_schema.at (" type" ).is_string () &&
894+ items_schema.at (" type" ).to_string () == " object" ) {
895+ visited.emplace (&items_schema, VisitedEntry{.identifier = items_row_id,
896+ .path = items_path});
897+ walk_properties (items_schema, items_path, rows, frame, root, visited,
898+ next_identifier);
899+ walk_pattern_properties (items_schema, items_path, rows, frame, root,
900+ visited, next_identifier);
901+ walk_wildcard_keyword (items_schema, " additionalProperties" , items_path,
902+ rows, frame, root, visited, next_identifier);
903+ walk_wildcard_keyword (items_schema, " unevaluatedProperties" , items_path,
904+ rows, frame, root, visited, next_identifier);
905+ if (!items_schema.defines (" additionalProperties" ) &&
906+ !items_schema.defines (" unevaluatedProperties" )) {
907+ auto open_path{items_path};
908+ open_path.push_back (make_path_segment (" wildcard" , " *" ));
909+ emit_row (sourcemeta::core::JSON {true }, std::move (open_path), rows,
910+ frame, root, visited, next_identifier);
911+ }
912+ visited.erase (&items_schema);
913+ }
914+ }
915+ }
790916}
791917
792918auto walk_pattern_properties (const sourcemeta::core::JSON &schema,
@@ -802,10 +928,35 @@ auto walk_pattern_properties(const sourcemeta::core::JSON &schema,
802928 }
803929
804930 for (const auto &entry : schema.at (" patternProperties" ).as_object ()) {
931+ const auto &resolved{resolve_ref (entry.second , frame, root, visited)};
805932 auto path{base_path};
806933 path.push_back (make_path_segment (" pattern" , entry.first ));
807- emit_row (entry.second , std::move (path), rows, frame, root, visited,
808- next_identifier);
934+
935+ const auto row_id{next_identifier};
936+ emit_row (resolved, path, rows, frame, root, visited, next_identifier);
937+
938+ if (resolved.is_object () && resolved.defines (" type" ) &&
939+ resolved.at (" type" ).is_string () &&
940+ resolved.at (" type" ).to_string () == " object" ) {
941+ visited.emplace (&resolved,
942+ VisitedEntry{.identifier = row_id, .path = path});
943+ walk_properties (resolved, path, rows, frame, root, visited,
944+ next_identifier);
945+ walk_pattern_properties (resolved, path, rows, frame, root, visited,
946+ next_identifier);
947+ walk_wildcard_keyword (resolved, " additionalProperties" , path, rows, frame,
948+ root, visited, next_identifier);
949+ walk_wildcard_keyword (resolved, " unevaluatedProperties" , path, rows,
950+ frame, root, visited, next_identifier);
951+ if (!resolved.defines (" additionalProperties" ) &&
952+ !resolved.defines (" unevaluatedProperties" )) {
953+ auto open_path{path};
954+ open_path.push_back (make_path_segment (" wildcard" , " *" ));
955+ emit_row (sourcemeta::core::JSON {true }, std::move (open_path), rows,
956+ frame, root, visited, next_identifier);
957+ }
958+ visited.erase (&resolved);
959+ }
809960 }
810961}
811962
@@ -1120,7 +1271,7 @@ auto walk_branching_subschema(const std::string &label,
11201271 visited, next_identifier);
11211272 }
11221273 emit_row (inner_schema, std::move (synthetic_path), table_rows, frame, root,
1123- visited, next_identifier);
1274+ visited, next_identifier, false );
11241275 walk_branches (" anyOf" , " Any of" , inner_schema, table_children, frame, root,
11251276 visited, next_identifier);
11261277 walk_branches (" oneOf" , " One of" , inner_schema, table_children, frame, root,
@@ -1204,7 +1355,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12041355 auto root_path{sourcemeta::core::JSON::make_array ()};
12051356 root_path.push_back (make_path_segment (" synthetic" , " root" ));
12061357 emit_row (schema, std::move (root_path), rows, frame, root, visited,
1207- next_identifier);
1358+ next_identifier, false );
12081359 const auto root_row_identifier{static_cast <std::size_t >(
12091360 rows.at (rows.size () - 1 ).at (" identifier" ).to_integer ())};
12101361 auto visited_root_path{sourcemeta::core::JSON::make_array ()};
@@ -1219,7 +1370,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12191370 auto root_path{sourcemeta::core::JSON::make_array ()};
12201371 root_path.push_back (make_path_segment (" synthetic" , " root" ));
12211372 emit_row (schema, std::move (root_path), rows, frame, root, visited,
1222- next_identifier);
1373+ next_identifier, false );
12231374 }
12241375 documentation.assign (" rows" , std::move (rows));
12251376 return documentation;
@@ -1229,7 +1380,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12291380 auto root_path{sourcemeta::core::JSON::make_array ()};
12301381 root_path.push_back (make_path_segment (" synthetic" , " root" ));
12311382 emit_row (schema, std::move (root_path), rows, frame, root, visited,
1232- next_identifier);
1383+ next_identifier, false );
12331384 }
12341385
12351386 const auto empty_path{sourcemeta::core::JSON::make_array ()};
0 commit comments