33using RelogicLabs . JsonSchema . Utilities ;
44using static RelogicLabs . JsonSchema . Message . ErrorCode ;
55using static RelogicLabs . JsonSchema . Message . ErrorDetail ;
6+ using static RelogicLabs . JsonSchema . Types . JDataType ;
67using static RelogicLabs . JsonSchema . Utilities . CommonUtilities ;
78
89namespace RelogicLabs . JsonSchema . Types ;
910
1011public sealed class JValidator : JBranch
1112{
12- public const string OptionalMarker = "?" ;
13+ internal const string OptionalMarker = "?" ;
14+ private readonly List < Exception > _exceptions ;
15+ private List < Exception > TryBuffer => Runtime . Exceptions . TryBuffer ;
16+
1317 public JNode ? Value { get ; }
1418 public IList < JFunction > Functions { get ; }
1519 public IList < JDataType > DataTypes { get ; }
@@ -18,6 +22,7 @@ public sealed class JValidator : JBranch
1822
1923 private JValidator ( Builder builder ) : base ( builder )
2024 {
25+ _exceptions = new List < Exception > ( ) ;
2126 Value = builder . Value ;
2227 Functions = RequireNonNull ( builder . Functions ) ;
2328 DataTypes = RequireNonNull ( builder . DataTypes ) ;
@@ -35,36 +40,86 @@ public override bool Match(JNode node)
3540 var value = CastType < IJsonType > ( node ) ;
3641 if ( value == null ) return false ;
3742 Runtime . Receivers . Receive ( Receivers , node ) ;
38- if ( node is JNull && DataTypes . Select ( d => d . IsMatchNull ( ) ) . AnyTrue ( ) )
43+ if ( node is JNull && DataTypes . Any ( d => d . IsMatchNull ( ) ) )
3944 return true ;
4045 if ( Value != null ) rValue &= Value . Match ( value . Node ) ;
4146 if ( ! rValue ) return FailWith ( new JsonSchemaException (
4247 new ErrorDetail ( VALD01 , ValidationFailed ) ,
43- ExpectedDetail . AsValueMismatch ( Value ! ) ,
44- ActualDetail . AsValueMismatch ( node ) ) ) ;
48+ ExpectedDetail . AsGeneralValueMismatch ( Value ! ) ,
49+ ActualDetail . AsGeneralValueMismatch ( node ) ) ) ;
4550 var rDataType = MatchDataType ( node ) ;
4651 var fDataType = rDataType && DataTypes . Count != 0 ;
4752 bool rFunction = Functions . Where ( f => f . IsApplicable ( node ) || ! fDataType )
48- . Select ( f => f . Match ( node ) ) . ForEachTrue ( ) || Functions . Count == 0 ;
53+ . ForEachTrue ( f => f . Match ( node ) ) ;
4954 return rValue & rDataType & rFunction ;
5055 }
5156
5257 private bool MatchDataType ( JNode node )
5358 {
54- if ( Runtime . TryExecute ( ( ) => CheckDataType ( node ) ) ) return true ;
55- DataTypes . Where ( d => ! d . Nested ) . ForEach ( d => d . MatchForReport ( node ) ) ;
56- DataTypes . Where ( d => d . Nested ) . ForEach ( d => d . MatchForReport ( node ) ) ;
59+ if ( Runtime . Exceptions . TryExecute ( ( ) => CheckDataType ( node ) ) ) return true ;
60+ SaveTryBuffer ( ) ;
61+ foreach ( var e in _exceptions ) FailWith ( e ) ;
5762 return false ;
5863 }
5964
65+ private static IEnumerable < Exception > ProcessTryBuffer ( List < Exception > buffer ) {
66+ var list = new List < Exception > ( buffer . Count ) ;
67+ foreach ( var e in buffer )
68+ {
69+ var result = MergeException ( TryGetLast ( list ) , e ) ;
70+ if ( result is not null ) list [ ^ 1 ] = result ;
71+ else list . Add ( e ) ;
72+ }
73+ return list ;
74+ }
75+
76+ private static JsonSchemaException ? MergeException ( Exception ? ex1 , Exception ? ex2 ) {
77+ if ( ex1 is not JsonSchemaException e1 ) return null ;
78+ if ( ex2 is not JsonSchemaException e2 ) return null ;
79+ if ( e1 . Code != e2 . Code ) return null ;
80+ var a1 = e1 . GetAttribute ( DataTypeName ) ;
81+ var a2 = e2 . GetAttribute ( DataTypeName ) ;
82+ if ( a1 is null || a2 is null ) return null ;
83+ var result = new JsonSchemaException ( e1 . Error , MergeExpected ( e1 , e2 ) , e2 . Actual ) ;
84+ result . SetAttribute ( DataTypeName , a1 + a2 ) ;
85+ return result ;
86+ }
87+
88+ private static ExpectedDetail MergeExpected ( JsonSchemaException ex1 ,
89+ JsonSchemaException ex2 ) {
90+ var typeName2 = ex2 . GetAttribute ( DataTypeName ) ;
91+ var expected1 = ex1 . Expected ;
92+ return new ExpectedDetail ( expected1 . Context , $ "{ expected1 . Message } or { typeName2 } ") ;
93+ }
94+
6095 private bool CheckDataType ( JNode node )
6196 {
62- var list1 = DataTypes . Where ( d => ! d . Nested ) . Select ( d => d . Match ( node ) ) . ToList ( ) ;
63- var result1 = list1 . AnyTrue ( ) ;
64- var list2 = DataTypes . Where ( d => d . Nested && ( d . IsApplicable ( node ) || ! result1 ) )
65- . Select ( d => d . Match ( node ) ) . ToList ( ) ;
66- var result2 = list2 . AnyTrue ( ) || list2 . Count == 0 ;
67- return ( result1 || list1 . Count == 0 ) && result2 ;
97+ var list1 = DataTypes . Where ( d => ! d . Nested ) . ToList ( ) ;
98+ var result1 = AnyMatch ( list1 , node ) ;
99+ if ( result1 ) TryBuffer . Clear ( ) ;
100+ var list2 = DataTypes . Where ( d => d . Nested && ( d . IsApplicable ( node ) || ! result1 ) ) . ToList ( ) ;
101+ if ( list2 . IsEmpty ( ) ) return result1 || list1 . IsEmpty ( ) ;
102+ if ( node is not JComposite composite ) return FailWith (
103+ new JsonSchemaException (
104+ new ErrorDetail ( DTYP03 , InvalidNonCompositeType ) ,
105+ ExpectedDetail . AsInvalidNonCompositeType ( list2 . First ( ) ) ,
106+ ActualDetail . AsInvalidNonCompositeType ( node ) ) ) ;
107+ SaveTryBuffer ( ) ;
108+ var result2 = composite . Components . ForEachTrue ( n => AnyMatch ( list2 , n ) ) ;
109+ return ( result1 || list1 . IsEmpty ( ) ) && result2 ;
110+ }
111+
112+ private bool AnyMatch ( List < JDataType > list , JNode node ) {
113+ TryBuffer . Clear ( ) ;
114+ foreach ( var d in list ) if ( d . Match ( node ) ) return true ;
115+ SaveTryBuffer ( ) ;
116+ return false ;
117+ }
118+
119+ private void SaveTryBuffer ( ) {
120+ if ( TryBuffer . IsEmpty ( ) ) return ;
121+ _exceptions . AddRange ( ProcessTryBuffer ( TryBuffer ) ) ;
122+ TryBuffer . Clear ( ) ;
68123 }
69124
70125 public override string ToString ( ) => (
0 commit comments