@ -26,12 +26,13 @@ import java.util.Set;
import java.util.SortedSet ;
import java.util.SortedSet ;
import java.util.TreeSet ;
import java.util.TreeSet ;
import java.util.stream.Collectors ;
import java.util.stream.Collectors ;
import java.util.stream.Stream ;
import org.springframework.http.server.reactive.PathContainer ;
import org.springframework.lang.Nullable ;
import org.springframework.lang.Nullable ;
import org.springframework.util.StringUtils ;
import org.springframework.util.StringUtils ;
import org.springframework.web.server.ServerWebExchange ;
import org.springframework.web.server.ServerWebExchange ;
import org.springframework.web.util.pattern.PathPattern ;
import org.springframework.web.util.pattern.PathPattern ;
import org.springframework.web.util.pattern.PathPatternComparator ;
import org.springframework.web.util.pattern.PathPatternParser ;
import org.springframework.web.util.pattern.PathPatternParser ;
/ * *
/ * *
@ -44,17 +45,18 @@ import org.springframework.web.util.pattern.PathPatternParser;
* /
* /
public final class PatternsRequestCondition extends AbstractRequestCondition < PatternsRequestCondition > {
public final class PatternsRequestCondition extends AbstractRequestCondition < PatternsRequestCondition > {
private final Lis t< PathPattern > patterns ;
private final Se t< PathPattern > patterns ;
private final PathPatternParser parser ;
private final PathPatternParser parser ;
/ * *
/ * *
* Creates a new instance with the given URL patterns .
* Creates a new instance with the given URL patterns .
* Each pattern is prepended with "/" if not already .
* Each pattern is prepended with "/" if not already .
* @param patterns 0 or more URL patterns ; if 0 the condition will match to every request .
* @param patterns 0 or more URL patterns ; if 0 the condition will match to every request .
* /
* /
public PatternsRequestCondition ( String . . . patterns ) {
public PatternsRequestCondition ( String . . . patterns ) {
this ( asList ( patterns ) , null ) ;
this ( patterns , null ) ;
}
}
/ * *
/ * *
@ -64,44 +66,39 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* @param patternParser for parsing string patterns
* @param patternParser for parsing string patterns
* /
* /
public PatternsRequestCondition ( String [ ] patterns , PathPatternParser patternParser ) {
public PatternsRequestCondition ( String [ ] patterns , PathPatternParser patternParser ) {
this ( Arrays . asList ( patterns ) , patternParser ) ;
this ( asList ( patterns ) , patternParser ) ;
}
}
/ * *
/ * *
* Private constructor accepting a collection of raw patterns .
* Private constructor accepting a collection of raw patterns .
* /
* /
private PatternsRequestCondition ( Collection < String > patterns , PathPatternParser patternParser ) {
private PatternsRequestCondition ( Collection < String > patterns , PathPatternParser parser ) {
this . parser = patternParser ! = null ? patternParser : new PathPatternParser ( ) ;
this . parser = ( parser ! = null ? parser : new PathPatternParser ( ) ) ;
this . patterns = new ArrayList < > ( ) ;
this . patterns = toSortedSet ( patterns . stream ( ) . map ( pattern - > parse ( pattern , this . parser ) ) ) ;
patterns . forEach ( pattern - > {
}
if ( StringUtils . hasText ( pattern ) & & ! pattern . startsWith ( "/" ) ) {
pattern = "/" + pattern ;
private static PathPattern parse ( String pattern , PathPatternParser parser ) {
}
if ( StringUtils . hasText ( pattern ) & & ! pattern . startsWith ( "/" ) ) {
this . patterns . add ( this . parser . parse ( pattern ) ) ;
pattern = "/" + pattern ;
} ) ;
}
return parser . parse ( pattern ) ;
}
private static Set < PathPattern > toSortedSet ( Stream < PathPattern > stream ) {
Set < PathPattern > result = stream . sorted ( ) . collect ( Collectors . toCollection ( TreeSet : : new ) ) ;
return Collections . unmodifiableSet ( result ) ;
}
}
/ * *
/ * *
* Private constructor accepting a list of path patterns .
* Private constructor accepting a list of path patterns .
* /
* /
private PatternsRequestCondition ( List < PathPattern > patterns , PathPatternParser patternParser ) {
private PatternsRequestCondition ( List < PathPattern > patterns , PathPatternParser patternParser ) {
this . patterns = patterns ;
this . patterns = toSortedSet ( patterns . stream ( ) ) ;
this . parser = patternParser ;
this . parser = patternParser ;
}
}
private static List < String > asList ( String . . . patterns ) {
return ( patterns ! = null ? Arrays . asList ( patterns ) : Collections . emptyList ( ) ) ;
}
public Set < PathPattern > getPatterns ( ) {
public Set < PathPattern > getPatterns ( ) {
return new TreeSet < > ( this . patterns ) ;
return this . patterns ;
}
public Set < String > getPatternStrings ( ) {
return this . patterns . stream ( )
. map ( PathPattern : : toString ) . collect ( Collectors . toSet ( ) ) ;
}
}
@Override
@Override
@ -143,14 +140,12 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
else {
else {
combined . add ( this . parser . parse ( "" ) ) ;
combined . add ( this . parser . parse ( "" ) ) ;
}
}
return new PatternsRequestCondition ( combined , this . parser ) ;
return new PatternsRequestCondition ( combined , this . parser ) ;
}
}
/ * *
/ * *
* Checks if any of the patterns match the given request and returns an instance
* Checks if any of the patterns match the given request and returns an instance
* that is guaranteed to contain matching patterns , sorted with a
* that is guaranteed to contain matching patterns , sorted .
* { @link PathPatternComparator } .
* @param exchange the current exchange
* @param exchange the current exchange
* @return the same instance if the condition contains no patterns ;
* @return the same instance if the condition contains no patterns ;
* or a new condition with sorted matching patterns ;
* or a new condition with sorted matching patterns ;
@ -162,11 +157,9 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
if ( this . patterns . isEmpty ( ) ) {
if ( this . patterns . isEmpty ( ) ) {
return this ;
return this ;
}
}
SortedSet < PathPattern > matches = getMatchingPatterns ( exchange ) ;
String lookupPath = exchange . getRequest ( ) . getPath ( ) . pathWithinApplication ( ) . value ( ) ;
return matches . isEmpty ( ) ? null :
SortedSet < PathPattern > matches = getMatchingPatterns ( lookupPath ) ;
new PatternsRequestCondition ( new ArrayList < PathPattern > ( matches ) , this . parser ) ;
return matches . isEmpty ( ) ? null : new PatternsRequestCondition (
new ArrayList < PathPattern > ( matches ) , this . parser ) ;
}
}
/ * *
/ * *
@ -175,20 +168,19 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* { @link # getMatchingCondition ( ServerWebExchange ) } .
* { @link # getMatchingCondition ( ServerWebExchange ) } .
* This method is provided as an alternative to be used if no request is available
* This method is provided as an alternative to be used if no request is available
* ( e . g . introspection , tooling , etc ) .
* ( e . g . introspection , tooling , etc ) .
* @param lookupPath the lookup path to match to existing patterns
* @param exchange the current exchange
* @return a sorted set of matching patterns sorted with the closest match first
* @return a sorted set of matching patterns sorted with the closest match first
* /
* /
public SortedSet < PathPattern > getMatchingPatterns ( String lookupPath ) {
private SortedSet < PathPattern > getMatchingPatterns ( ServerWebExchange exchange ) {
PathContainer lookupPath = exchange . getRequest ( ) . getPath ( ) . pathWithinApplication ( ) ;
return patterns . stream ( )
return patterns . stream ( )
. filter ( pattern - > pattern . matches ( lookupPath ) )
. filter ( pattern - > pattern . matches ( lookupPath ) )
. collect ( Collectors . toCollection ( ( ) - >
. collect ( Collectors . toCollection ( TreeSet : : new ) ) ;
new TreeSet < > ( new PathPatternComparator ( lookupPath ) ) ) ) ;
}
}
/ * *
/ * *
* Compare the two conditions based on the URL patterns they contain .
* Compare the two conditions based on the URL patterns they contain .
* Patterns are compared one at a time , from top to bottom via
* Patterns are compared one at a time , from top to bottom . If all compared
* { @link PathPatternComparator } . If all compared
* patterns match equally , but one instance has more patterns , it is
* patterns match equally , but one instance has more patterns , it is
* considered a closer match .
* considered a closer match .
* < p > It is assumed that both instances have been obtained via
* < p > It is assumed that both instances have been obtained via
@ -198,14 +190,10 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* /
* /
@Override
@Override
public int compareTo ( PatternsRequestCondition other , ServerWebExchange exchange ) {
public int compareTo ( PatternsRequestCondition other , ServerWebExchange exchange ) {
String lookupPath = exchange . getRequest ( ) . getPath ( ) . pathWithinApplication ( ) . value ( ) ;
Iterator < PathPattern > iterator = this . patterns . iterator ( ) ;
PathPatternComparator comparator = new PathPatternComparator ( lookupPath ) ;
Iterator < PathPattern > iteratorOther = other . getPatterns ( ) . iterator ( ) ;
Iterator < PathPattern > iterator = this . patterns . stream ( )
. sorted ( comparator ) . collect ( Collectors . toList ( ) ) . iterator ( ) ;
Iterator < PathPattern > iteratorOther = other . getPatterns ( ) . stream ( )
. sorted ( comparator ) . collect ( Collectors . toList ( ) ) . iterator ( ) ;
while ( iterator . hasNext ( ) & & iteratorOther . hasNext ( ) ) {
while ( iterator . hasNext ( ) & & iteratorOther . hasNext ( ) ) {
int result = comparator . compare ( iterator . next ( ) , iteratorOther . next ( ) ) ;
int result = iterator . next ( ) . compareTo ( iteratorOther . next ( ) ) ;
if ( result ! = 0 ) {
if ( result ! = 0 ) {
return result ;
return result ;
}
}