4
4
using JsonApiDotNetCore . Configuration ;
5
5
using JsonApiDotNetCore . Queries . Expressions ;
6
6
using JsonApiDotNetCore . QueryStrings ;
7
+ using JsonApiDotNetCore . QueryStrings . FieldChains ;
7
8
using JsonApiDotNetCore . Resources ;
8
9
using JsonApiDotNetCore . Resources . Annotations ;
9
10
using JsonApiDotNetCore . Resources . Internal ;
10
11
11
12
namespace JsonApiDotNetCore . Queries . Internal . Parsing ;
12
13
14
+ /// <summary>
15
+ /// Parses the JSON:API 'filter' query string parameter value.
16
+ /// </summary>
13
17
[ PublicAPI ]
14
18
public class FilterParser : QueryExpressionParser
15
19
{
16
20
private const string PlaceholderMessageForAttributeNotString = "JsonApiDotNetCore:Placeholder_for_attribute_not_string" ;
17
21
18
22
private readonly IResourceFactory _resourceFactory ;
19
23
private readonly IEnumerable < IFilterValueConverter > _filterValueConverters ;
20
- private ResourceType ? _resourceTypeInScope ;
24
+ private ResourceType _resourceTypeInScope = null ! ;
21
25
22
26
public FilterParser ( IResourceFactory resourceFactory , IEnumerable < IFilterValueConverter > filterValueConverters )
23
27
{
@@ -28,11 +32,11 @@ public FilterParser(IResourceFactory resourceFactory, IEnumerable<IFilterValueCo
28
32
_filterValueConverters = filterValueConverters ;
29
33
}
30
34
31
- public FilterExpression Parse ( string source , ResourceType resourceTypeInScope )
35
+ public FilterExpression Parse ( string source , ResourceType resourceType )
32
36
{
33
- ArgumentGuard . NotNull ( resourceTypeInScope ) ;
37
+ ArgumentGuard . NotNull ( resourceType ) ;
34
38
35
- return InScopeOfResourceType ( resourceTypeInScope , ( ) =>
39
+ return InScopeOfResourceType ( resourceType , ( ) =>
36
40
{
37
41
Tokenize ( source ) ;
38
42
@@ -142,11 +146,11 @@ protected ComparisonExpression ParseComparison(string operatorName)
142
146
EatSingleCharacterToken ( TokenKind . OpenParen ) ;
143
147
144
148
// 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 ;
148
152
149
- QueryExpression leftTerm = ParseCountOrField ( leftChainRequirements ) ;
153
+ QueryExpression leftTerm = ParseCountOrField ( leftChainPattern ) ;
150
154
151
155
EatSingleCharacterToken ( TokenKind . Comma ) ;
152
156
@@ -155,12 +159,12 @@ protected ComparisonExpression ParseComparison(string operatorName)
155
159
if ( leftTerm is CountExpression )
156
160
{
157
161
Func < string , int , object > rightConstantValueConverter = GetConstantValueConverterForCount ( ) ;
158
- rightTerm = ParseCountOrConstantOrField ( FieldChainRequirements . EndsInAttribute , rightConstantValueConverter ) ;
162
+ rightTerm = ParseCountOrConstantOrField ( rightConstantValueConverter ) ;
159
163
}
160
164
else if ( leftTerm is ResourceFieldChainExpression fieldChain && fieldChain . Fields [ ^ 1 ] is AttrAttribute attribute )
161
165
{
162
166
Func < string , int , object > rightConstantValueConverter = GetConstantValueConverterForAttribute ( attribute , typeof ( ComparisonExpression ) ) ;
163
- rightTerm = ParseCountOrConstantOrNullOrField ( FieldChainRequirements . EndsInAttribute , rightConstantValueConverter ) ;
167
+ rightTerm = ParseCountOrConstantOrNullOrField ( rightConstantValueConverter ) ;
164
168
}
165
169
else
166
170
{
@@ -179,7 +183,9 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
179
183
180
184
int fieldChainStartPosition = GetNextTokenPositionOrEnd ( ) ;
181
185
182
- ResourceFieldChainExpression targetAttributeChain = ParseFieldChain ( FieldChainRequirements . EndsInAttribute , null ) ;
186
+ ResourceFieldChainExpression targetAttributeChain =
187
+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
188
+
183
189
var targetAttribute = ( AttrAttribute ) targetAttributeChain . Fields [ ^ 1 ] ;
184
190
185
191
EatSingleCharacterToken ( TokenKind . Comma ) ;
@@ -193,7 +199,7 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
193
199
}
194
200
catch ( QueryParseException exception ) when ( exception . Message == PlaceholderMessageForAttributeNotString )
195
201
{
196
- int attributePosition = GetPositionOfLastField ( targetAttributeChain , fieldChainStartPosition ) ;
202
+ int attributePosition = fieldChainStartPosition + GetRelativePositionOfLastFieldInChain ( targetAttributeChain ) ;
197
203
throw new QueryParseException ( "Attribute of type 'String' expected." , attributePosition ) ;
198
204
}
199
205
@@ -203,24 +209,14 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
203
209
return new MatchTextExpression ( targetAttributeChain , constant , matchKind ) ;
204
210
}
205
211
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
-
218
212
protected AnyExpression ParseAny ( )
219
213
{
220
214
EatText ( Keywords . Any ) ;
221
215
EatSingleCharacterToken ( TokenKind . OpenParen ) ;
222
216
223
- ResourceFieldChainExpression targetAttributeChain = ParseFieldChain ( FieldChainRequirements . EndsInAttribute , null ) ;
217
+ ResourceFieldChainExpression targetAttributeChain =
218
+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInAttribute , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
219
+
224
220
var targetAttribute = ( AttrAttribute ) targetAttributeChain . Fields [ ^ 1 ] ;
225
221
226
222
EatSingleCharacterToken ( TokenKind . Comma ) ;
@@ -251,7 +247,9 @@ protected HasExpression ParseHas()
251
247
EatText ( Keywords . Has ) ;
252
248
EatSingleCharacterToken ( TokenKind . OpenParen ) ;
253
249
254
- ResourceFieldChainExpression targetCollection = ParseFieldChain ( FieldChainRequirements . EndsInToMany , null ) ;
250
+ ResourceFieldChainExpression targetCollection =
251
+ ParseFieldChain ( BuiltInPatterns . ToOneChainEndingInToMany , FieldChainPatternMatchOptions . None , _resourceTypeInScope , null ) ;
252
+
255
253
FilterExpression ? filter = null ;
256
254
257
255
if ( TokenStack . TryPeek ( out Token ? nextToken ) && nextToken . Kind == TokenKind . Comma )
@@ -280,7 +278,7 @@ private IsTypeExpression ParseIsType()
280
278
281
279
EatSingleCharacterToken ( TokenKind . Comma ) ;
282
280
283
- ResourceType baseType = targetToOneRelationship != null ? ( ( RelationshipAttribute ) targetToOneRelationship . Fields [ ^ 1 ] ) . RightType : _resourceTypeInScope ! ;
281
+ ResourceType baseType = targetToOneRelationship != null ? ( ( RelationshipAttribute ) targetToOneRelationship . Fields [ ^ 1 ] ) . RightType : _resourceTypeInScope ;
284
282
ResourceType derivedType = ParseDerivedType ( baseType ) ;
285
283
286
284
FilterExpression ? child = TryParseFilterInIsType ( derivedType ) ;
@@ -297,7 +295,7 @@ private IsTypeExpression ParseIsType()
297
295
return null ;
298
296
}
299
297
300
- return ParseFieldChain ( FieldChainRequirements . EndsInToOne , "Relationship name or , expected." ) ;
298
+ return ParseFieldChain ( BuiltInPatterns . ToOneChain , FieldChainPatternMatchOptions . None , _resourceTypeInScope , "Relationship name or , expected." ) ;
301
299
}
302
300
303
301
private ResourceType ParseDerivedType ( ResourceType baseType )
@@ -359,21 +357,21 @@ private ResourceType ResolveDerivedType(ResourceType baseType, string derivedTyp
359
357
return filter ;
360
358
}
361
359
362
- protected QueryExpression ParseCountOrField ( FieldChainRequirements chainRequirements )
360
+ protected QueryExpression ParseCountOrField ( FieldChainPattern pattern )
363
361
{
364
- CountExpression ? count = TryParseCount ( ) ;
362
+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
365
363
366
364
if ( count != null )
367
365
{
368
366
return count ;
369
367
}
370
368
371
- return ParseFieldChain ( chainRequirements , "Count function or field name expected." ) ;
369
+ return ParseFieldChain ( pattern , FieldChainPatternMatchOptions . None , _resourceTypeInScope , "Count function or field name expected." ) ;
372
370
}
373
371
374
- protected QueryExpression ParseCountOrConstantOrField ( FieldChainRequirements chainRequirements , Func < string , int , object > constantValueConverter )
372
+ protected QueryExpression ParseCountOrConstantOrField ( Func < string , int , object > constantValueConverter )
375
373
{
376
- CountExpression ? count = TryParseCount ( ) ;
374
+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
377
375
378
376
if ( count != null )
379
377
{
@@ -387,12 +385,13 @@ protected QueryExpression ParseCountOrConstantOrField(FieldChainRequirements cha
387
385
return constant ;
388
386
}
389
387
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." ) ;
391
390
}
392
391
393
- protected QueryExpression ParseCountOrConstantOrNullOrField ( FieldChainRequirements chainRequirements , Func < string , int , object > constantValueConverter )
392
+ protected QueryExpression ParseCountOrConstantOrNullOrField ( Func < string , int , object > constantValueConverter )
394
393
{
395
- CountExpression ? count = TryParseCount ( ) ;
394
+ CountExpression ? count = TryParseCount ( FieldChainPatternMatchOptions . None , _resourceTypeInScope ) ;
396
395
397
396
if ( count != null )
398
397
{
@@ -406,7 +405,8 @@ protected QueryExpression ParseCountOrConstantOrNullOrField(FieldChainRequiremen
406
405
return constantOrNull ;
407
406
}
408
407
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." ) ;
410
410
}
411
411
412
412
protected LiteralConstantExpression ? TryParseConstant ( Func < string , int , object > constantValueConverter )
@@ -546,34 +546,7 @@ private object DeObfuscateStringId(Type resourceClrType, string stringId)
546
546
return tempResource . GetTypedId ( ) ;
547
547
}
548
548
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 )
577
550
{
578
551
if ( field . IsFilterBlocked ( ) )
579
552
{
@@ -584,7 +557,7 @@ protected override void ValidateSingleField(ResourceFieldAttribute field, Resour
584
557
585
558
private TResult InScopeOfResourceType < TResult > ( ResourceType resourceType , Func < TResult > action )
586
559
{
587
- ResourceType ? backupType = _resourceTypeInScope ;
560
+ ResourceType backupType = _resourceTypeInScope ;
588
561
589
562
try
590
563
{
0 commit comments