44using JsonApiDotNetCore . Configuration ;
55using JsonApiDotNetCore . Queries . Expressions ;
66using JsonApiDotNetCore . QueryStrings ;
7+ using JsonApiDotNetCore . QueryStrings . FieldChains ;
78using JsonApiDotNetCore . Resources ;
89using JsonApiDotNetCore . Resources . Annotations ;
910using JsonApiDotNetCore . Resources . Internal ;
1011
1112namespace JsonApiDotNetCore . Queries . Internal . Parsing ;
1213
14+ /// <summary>
15+ /// Parses the JSON:API 'filter' query string parameter value.
16+ /// </summary>
1317[ PublicAPI ]
1418public class FilterParser : QueryExpressionParser
1519{
1620 private const string PlaceholderMessageForAttributeNotString = "JsonApiDotNetCore:Placeholder_for_attribute_not_string" ;
1721
1822 private readonly IResourceFactory _resourceFactory ;
1923 private readonly IEnumerable < IFilterValueConverter > _filterValueConverters ;
20- private ResourceType ? _resourceTypeInScope ;
24+ private ResourceType _resourceTypeInScope = null ! ;
2125
2226 public FilterParser ( IResourceFactory resourceFactory , IEnumerable < IFilterValueConverter > filterValueConverters )
2327 {
@@ -28,11 +32,11 @@ public FilterParser(IResourceFactory resourceFactory, IEnumerable<IFilterValueCo
2832 _filterValueConverters = filterValueConverters ;
2933 }
3034
31- public FilterExpression Parse ( string source , ResourceType resourceTypeInScope )
35+ public FilterExpression Parse ( string source , ResourceType resourceType )
3236 {
33- ArgumentGuard . NotNull ( resourceTypeInScope ) ;
37+ ArgumentGuard . NotNull ( resourceType ) ;
3438
35- return InScopeOfResourceType ( resourceTypeInScope , ( ) =>
39+ return InScopeOfResourceType ( resourceType , ( ) =>
3640 {
3741 Tokenize ( source ) ;
3842
@@ -142,11 +146,11 @@ protected ComparisonExpression ParseComparison(string operatorName)
142146 EatSingleCharacterToken ( TokenKind . OpenParen ) ;
143147
144148 // Allow equality comparison of a to-one relationship with null.
145- FieldChainRequirements leftChainRequirements = comparisonOperator == ComparisonOperator . Equals
146- ? FieldChainRequirements . EndsInAttribute | FieldChainRequirements . EndsInToOne
147- : FieldChainRequirements . EndsInAttribute ;
149+ FieldChainPattern leftChainPattern = comparisonOperator == ComparisonOperator . Equals
150+ ? BuiltInPatterns . ToOneChainEndingInAttributeOrToOne
151+ : BuiltInPatterns . ToOneChainEndingInAttribute ;
148152
149- QueryExpression leftTerm = ParseCountOrField ( leftChainRequirements ) ;
153+ QueryExpression leftTerm = ParseCountOrField ( leftChainPattern ) ;
150154
151155 EatSingleCharacterToken ( TokenKind . Comma ) ;
152156
@@ -155,12 +159,12 @@ protected ComparisonExpression ParseComparison(string operatorName)
155159 if ( leftTerm is CountExpression )
156160 {
157161 Func < string , int , object > rightConstantValueConverter = GetConstantValueConverterForCount ( ) ;
158- rightTerm = ParseCountOrConstantOrField ( FieldChainRequirements . EndsInAttribute , rightConstantValueConverter ) ;
162+ rightTerm = ParseCountOrConstantOrField ( rightConstantValueConverter ) ;
159163 }
160164 else if ( leftTerm is ResourceFieldChainExpression fieldChain && fieldChain . Fields [ ^ 1 ] is AttrAttribute attribute )
161165 {
162166 Func < string , int , object > rightConstantValueConverter = GetConstantValueConverterForAttribute ( attribute , typeof ( ComparisonExpression ) ) ;
163- rightTerm = ParseCountOrConstantOrNullOrField ( FieldChainRequirements . EndsInAttribute , rightConstantValueConverter ) ;
167+ rightTerm = ParseCountOrConstantOrNullOrField ( rightConstantValueConverter ) ;
164168 }
165169 else
166170 {
@@ -179,7 +183,9 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
179183
180184 int fieldChainStartPosition = GetNextTokenPositionOrEnd ( ) ;
181185
182- ResourceFieldChainExpression targetAttributeChain = ParseFieldChain ( FieldChainRequirements . EndsInAttribute , null ) ;
186+ ResourceFieldChainExpression targetAttributeChain =
187+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
188+
183189 var targetAttribute = ( AttrAttribute ) targetAttributeChain . Fields [ ^ 1 ] ;
184190
185191 EatSingleCharacterToken ( TokenKind . Comma ) ;
@@ -193,7 +199,7 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
193199 }
194200 catch ( QueryParseException exception ) when ( exception . Message == PlaceholderMessageForAttributeNotString )
195201 {
196- int attributePosition = GetPositionOfLastField ( targetAttributeChain , fieldChainStartPosition ) ;
202+ int attributePosition = fieldChainStartPosition + GetRelativePositionOfLastFieldInChain ( targetAttributeChain ) ;
197203 throw new QueryParseException ( "Attribute of type 'String' expected." , attributePosition ) ;
198204 }
199205
@@ -203,24 +209,14 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
203209 return new MatchTextExpression ( targetAttributeChain , constant , matchKind ) ;
204210 }
205211
206- private static int GetPositionOfLastField ( ResourceFieldChainExpression fieldChain , int startPosition )
207- {
208- int position = 0 ;
209-
210- for ( int index = 0 ; index < fieldChain . Fields . Count - 1 ; index ++ )
211- {
212- position += fieldChain . Fields [ index ] . PublicName . Length + 1 ;
213- }
214-
215- return startPosition + position ;
216- }
217-
218212 protected AnyExpression ParseAny ( )
219213 {
220214 EatText ( Keywords . Any ) ;
221215 EatSingleCharacterToken ( TokenKind . OpenParen ) ;
222216
223- ResourceFieldChainExpression targetAttributeChain = ParseFieldChain ( FieldChainRequirements . EndsInAttribute , null ) ;
217+ ResourceFieldChainExpression targetAttributeChain =
218+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
219+
224220 var targetAttribute = ( AttrAttribute ) targetAttributeChain . Fields [ ^ 1 ] ;
225221
226222 EatSingleCharacterToken ( TokenKind . Comma ) ;
@@ -251,7 +247,9 @@ protected HasExpression ParseHas()
251247 EatText ( Keywords . Has ) ;
252248 EatSingleCharacterToken ( TokenKind . OpenParen ) ;
253249
254- ResourceFieldChainExpression targetCollection = ParseFieldChain ( FieldChainRequirements . EndsInToMany , null ) ;
250+ ResourceFieldChainExpression targetCollection =
251+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInToMany , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
252+
255253 FilterExpression ? filter = null ;
256254
257255 if ( TokenStack . TryPeek ( out Token ? nextToken ) && nextToken . Kind == TokenKind . Comma )
@@ -280,7 +278,7 @@ private IsTypeExpression ParseIsType()
280278
281279 EatSingleCharacterToken ( TokenKind . Comma ) ;
282280
283- ResourceType baseType = targetToOneRelationship != null ? ( ( RelationshipAttribute ) targetToOneRelationship . Fields [ ^ 1 ] ) . RightType : _resourceTypeInScope ! ;
281+ ResourceType baseType = targetToOneRelationship != null ? ( ( RelationshipAttribute ) targetToOneRelationship . Fields [ ^ 1 ] ) . RightType : _resourceTypeInScope ;
284282 ResourceType derivedType = ParseDerivedType ( baseType ) ;
285283
286284 FilterExpression ? child = TryParseFilterInIsType ( derivedType ) ;
@@ -297,7 +295,7 @@ private IsTypeExpression ParseIsType()
297295 return null ;
298296 }
299297
300- return ParseFieldChain ( FieldChainRequirements . EndsInToOne , "Relationship name or , expected." ) ;
298+ return ParseFieldChain ( BuiltInPatterns . ToOneChain , FieldChainPatternMatchOptions . None , _resourceTypeInScope , "Relationship name or , expected." ) ;
301299 }
302300
303301 private ResourceType ParseDerivedType ( ResourceType baseType )
@@ -359,21 +357,21 @@ private ResourceType ResolveDerivedType(ResourceType baseType, string derivedTyp
359357 return filter ;
360358 }
361359
362- protected QueryExpression ParseCountOrField ( FieldChainRequirements chainRequirements )
360+ protected QueryExpression ParseCountOrField ( FieldChainPattern pattern )
363361 {
364- CountExpression ? count = TryParseCount ( ) ;
362+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
365363
366364 if ( count != null )
367365 {
368366 return count ;
369367 }
370368
371- return ParseFieldChain ( chainRequirements , "Count function or field name expected." ) ;
369+ return ParseFieldChain ( pattern , FieldChainPatternMatchOptions . None , _resourceTypeInScope , "Count function or field name expected." ) ;
372370 }
373371
374- protected QueryExpression ParseCountOrConstantOrField ( FieldChainRequirements chainRequirements , Func < string , int , object > constantValueConverter )
372+ protected QueryExpression ParseCountOrConstantOrField ( Func < string , int , object > constantValueConverter )
375373 {
376- CountExpression ? count = TryParseCount ( ) ;
374+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
377375
378376 if ( count != null )
379377 {
@@ -387,12 +385,13 @@ protected QueryExpression ParseCountOrConstantOrField(FieldChainRequirements cha
387385 return constant ;
388386 }
389387
390- return ParseFieldChain ( chainRequirements , "Count function, value between quotes or field name expected." ) ;
388+ return ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope ,
389+ "Count function, value between quotes or field name expected." ) ;
391390 }
392391
393- protected QueryExpression ParseCountOrConstantOrNullOrField ( FieldChainRequirements chainRequirements , Func < string , int , object > constantValueConverter )
392+ protected QueryExpression ParseCountOrConstantOrNullOrField ( Func < string , int , object > constantValueConverter )
394393 {
395- CountExpression ? count = TryParseCount ( ) ;
394+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
396395
397396 if ( count != null )
398397 {
@@ -406,7 +405,8 @@ protected QueryExpression ParseCountOrConstantOrNullOrField(FieldChainRequiremen
406405 return constantOrNull ;
407406 }
408407
409- return ParseFieldChain ( chainRequirements , "Count function, value between quotes, null or field name expected." ) ;
408+ return ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope ,
409+ "Count function, value between quotes, null or field name expected." ) ;
410410 }
411411
412412 protected LiteralConstantExpression ? TryParseConstant ( Func < string , int , object > constantValueConverter )
@@ -546,34 +546,7 @@ private object DeObfuscateStringId(Type resourceClrType, string stringId)
546546 return tempResource . GetTypedId ( ) ;
547547 }
548548
549- protected override IImmutableList < ResourceFieldAttribute > OnResolveFieldChain ( string path , int position , FieldChainRequirements chainRequirements )
550- {
551- if ( chainRequirements == FieldChainRequirements . EndsInToMany )
552- {
553- return ChainResolver . ResolveToOneChainEndingInToMany ( _resourceTypeInScope ! , path , position , FieldChainInheritanceRequirement . Disabled ,
554- ValidateSingleField ) ;
555- }
556-
557- if ( chainRequirements == FieldChainRequirements . EndsInAttribute )
558- {
559- return ChainResolver . ResolveToOneChainEndingInAttribute ( _resourceTypeInScope ! , path , position , FieldChainInheritanceRequirement . Disabled ,
560- ValidateSingleField ) ;
561- }
562-
563- if ( chainRequirements == FieldChainRequirements . EndsInToOne )
564- {
565- return ChainResolver . ResolveToOneChain ( _resourceTypeInScope ! , path , position , ValidateSingleField ) ;
566- }
567-
568- if ( chainRequirements . HasFlag ( FieldChainRequirements . EndsInAttribute ) && chainRequirements . HasFlag ( FieldChainRequirements . EndsInToOne ) )
569- {
570- return ChainResolver . ResolveToOneChainEndingInAttributeOrToOne ( _resourceTypeInScope ! , path , position , ValidateSingleField ) ;
571- }
572-
573- throw new InvalidOperationException ( $ "Unexpected combination of chain requirement flags '{ chainRequirements } '.") ;
574- }
575-
576- protected override void ValidateSingleField ( ResourceFieldAttribute field , ResourceType resourceType , int position )
549+ protected override void ValidateField ( ResourceFieldAttribute field , int position )
577550 {
578551 if ( field . IsFilterBlocked ( ) )
579552 {
@@ -584,7 +557,7 @@ protected override void ValidateSingleField(ResourceFieldAttribute field, Resour
584557
585558 private TResult InScopeOfResourceType < TResult > ( ResourceType resourceType , Func < TResult > action )
586559 {
587- ResourceType ? backupType = _resourceTypeInScope ;
560+ ResourceType backupType = _resourceTypeInScope ;
588561
589562 try
590563 {
0 commit comments