@ -26,6 +26,8 @@ import java.util.Arrays;
@@ -26,6 +26,8 @@ import java.util.Arrays;
import java.util.Collections ;
import java.util.Iterator ;
import java.util.List ;
import java.util.function.BiFunction ;
import java.util.function.UnaryOperator ;
import org.springframework.lang.NonNull ;
import org.springframework.lang.Nullable ;
@ -70,7 +72,7 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -70,7 +72,7 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent encode ( Charset charset ) {
public PathComponent encode ( BiFunction < String , Type , String > encoder ) {
return this ;
}
@ -79,7 +81,7 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -79,7 +81,7 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent expand ( UriTemplateVariables uriVariables ) {
public PathComponent expand ( UriTemplateVariables uriVariables , @Nullable UnaryOperator < String > encoder ) {
return this ;
}
@ -112,7 +114,10 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -112,7 +114,10 @@ final class HierarchicalUriComponents extends UriComponents {
private final MultiValueMap < String , String > queryParams ;
private final boolean encoded ;
private final EncodeState encodeState ;
@Nullable
private UnaryOperator < String > variableEncoder ;
/ * *
@ -125,11 +130,10 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -125,11 +130,10 @@ final class HierarchicalUriComponents extends UriComponents {
* @param queryParams the query parameters
* @param fragment the fragment
* @param encoded whether the components are already encoded
* @param verify whether the components need to be checked for illegal characters
* /
HierarchicalUriComponents ( @Nullable String scheme , @Nullable String fragment , @Nullable String userInfo ,
@Nullable String host , @Nullable String port , @Nullable PathComponent path ,
@Nullable MultiValueMap < String , String > queryParams , boolean encoded , boolean verify ) {
@Nullable MultiValueMap < String , String > queryParams , boolean encoded ) {
super ( scheme , fragment ) ;
@ -139,13 +143,31 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -139,13 +143,31 @@ final class HierarchicalUriComponents extends UriComponents {
this . path = ( path ! = null ? path : NULL_PATH_COMPONENT ) ;
this . queryParams = CollectionUtils . unmodifiableMultiValueMap (
queryParams ! = null ? queryParams : new LinkedMultiValueMap < > ( 0 ) ) ;
this . encoded = encoded ;
this . encodeState = encoded ? EncodeState . FULLY_ENCODED : EncodeState . RAW ;
if ( verify ) {
// Check for illegal characters..
if ( encoded ) {
verify ( ) ;
}
}
private HierarchicalUriComponents ( @Nullable String scheme , @Nullable String fragment , @Nullable String userInfo ,
@Nullable String host , @Nullable String port , @Nullable PathComponent path ,
@Nullable MultiValueMap < String , String > queryParams , EncodeState encodeState ,
@Nullable UnaryOperator < String > variableEncoder ) {
super ( scheme , fragment ) ;
this . userInfo = userInfo ;
this . host = host ;
this . port = port ;
this . path = ( path ! = null ? path : NULL_PATH_COMPONENT ) ;
this . queryParams = CollectionUtils . unmodifiableMultiValueMap (
queryParams ! = null ? queryParams : new LinkedMultiValueMap < > ( 0 ) ) ;
this . encodeState = encodeState ;
this . variableEncoder = variableEncoder ;
}
// Component getters
@ -232,15 +254,30 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -232,15 +254,30 @@ final class HierarchicalUriComponents extends UriComponents {
// Encoding
/ * *
* Encode all URI components using their specific encoding rules and return
* the result as a new { @code UriComponents } instance .
* @param charset the encoding of the values
* @return the encoded URI components
* /
HierarchicalUriComponents encodeTemplate ( Charset charset ) {
if ( this . encodeState . isEncoded ( ) ) {
return this ;
}
// Remember the charset to encode URI variables later..
this . variableEncoder = value - > encodeUriComponent ( value , charset , Type . URI ) ;
UriTemplateEncoder encoder = new UriTemplateEncoder ( charset ) ;
String schemeTo = ( getScheme ( ) ! = null ? encoder . apply ( getScheme ( ) , Type . SCHEME ) : null ) ;
String fragmentTo = ( getFragment ( ) ! = null ? encoder . apply ( getFragment ( ) , Type . FRAGMENT ) : null ) ;
String userInfoTo = ( getUserInfo ( ) ! = null ? encoder . apply ( getUserInfo ( ) , Type . USER_INFO ) : null ) ;
String hostTo = ( getHost ( ) ! = null ? encoder . apply ( getHost ( ) , getHostType ( ) ) : null ) ;
PathComponent pathTo = this . path . encode ( encoder ) ;
MultiValueMap < String , String > paramsTo = encodeQueryParams ( encoder ) ;
return new HierarchicalUriComponents ( schemeTo , fragmentTo , userInfoTo ,
hostTo , this . port , pathTo , paramsTo , EncodeState . TEMPLATE_ENCODED , this . variableEncoder ) ;
}
@Override
public HierarchicalUriComponents encode ( Charset charset ) {
if ( this . encoded ) {
if ( this . encodeState . isEnco ded ( ) ) {
return this ;
}
String scheme = getScheme ( ) ;
@ -249,20 +286,22 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -249,20 +286,22 @@ final class HierarchicalUriComponents extends UriComponents {
String fragmentTo = ( fragment ! = null ? encodeUriComponent ( fragment , charset , Type . FRAGMENT ) : null ) ;
String userInfoTo = ( this . userInfo ! = null ? encodeUriComponent ( this . userInfo , charset , Type . USER_INFO ) : null ) ;
String hostTo = ( this . host ! = null ? encodeUriComponent ( this . host , charset , getHostType ( ) ) : null ) ;
PathComponent pathTo = this . path . encode ( charset ) ;
MultiValueMap < String , String > paramsTo = encodeQueryParams ( charset ) ;
return new HierarchicalUriComponents (
schemeTo , fragmentTo , userInfoTo , hostTo , this . port , pathTo , paramsTo , true , false ) ;
BiFunction < String , Type , String > encoder = ( s , type ) - > encodeUriComponent ( s , charset , type ) ;
PathComponent pathTo = this . path . encode ( encoder ) ;
MultiValueMap < String , String > paramsTo = encodeQueryParams ( encoder ) ;
return new HierarchicalUriComponents ( schemeTo , fragmentTo , userInfoTo ,
hostTo , this . port , pathTo , paramsTo , EncodeState . FULLY_ENCODED , null ) ;
}
private MultiValueMap < String , String > encodeQueryParams ( Charset charset ) {
private MultiValueMap < String , String > encodeQueryParams ( BiFunction < String , Type , String > encoder ) {
int size = this . queryParams . size ( ) ;
MultiValueMap < String , String > result = new LinkedMultiValueMap < > ( size ) ;
this . queryParams . forEach ( ( key , values ) - > {
String name = encodeUriComponent ( key , charset , Type . QUERY_PARAM ) ;
String name = encoder . apply ( key , Type . QUERY_PARAM ) ;
List < String > encodedValues = new ArrayList < > ( values . size ( ) ) ;
for ( String value : values ) {
encodedValues . add ( encodeUriComponent ( value , charset , Type . QUERY_PARAM ) ) ;
encodedValues . add ( encoder . apply ( value , Type . QUERY_PARAM ) ) ;
}
result . put ( name , encodedValues ) ;
} ) ;
@ -324,18 +363,13 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -324,18 +363,13 @@ final class HierarchicalUriComponents extends UriComponents {
return ( this . host ! = null & & this . host . startsWith ( "[" ) ? Type . HOST_IPV6 : Type . HOST_IPV4 ) ;
}
// Verifying
/ * *
* Verifies all URI components to determine whether they contain any illegal
* characters , throwing an { @code IllegalArgumentException } if so .
* Check if any of the URI components contain any illegal characters .
* @throws IllegalArgumentException if any component has illegal characters
* /
private void verify ( ) {
if ( ! this . encoded ) {
return ;
}
verifyUriComponent ( getScheme ( ) , Type . SCHEME ) ;
verifyUriComponent ( this . userInfo , Type . USER_INFO ) ;
verifyUriComponent ( this . host , getHostType ( ) ) ;
@ -385,18 +419,20 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -385,18 +419,20 @@ final class HierarchicalUriComponents extends UriComponents {
@Override
protected HierarchicalUriComponents expandInternal ( UriTemplateVariables uriVariables ) {
Assert . state ( ! this . encoded , "Cannot expand an already encoded UriComponents object" ) ;
String schemeTo = expandUriComponent ( getScheme ( ) , uriVariables ) ;
String fragmentTo = expandUriComponent ( getFragment ( ) , uriVariables ) ;
String userInfoTo = expandUriComponent ( this . userInfo , uriVariables ) ;
String hostTo = expandUriComponent ( this . host , uriVariables ) ;
String portTo = expandUriComponent ( this . port , uriVariables ) ;
PathComponent pathTo = this . path . expand ( uriVariables ) ;
Assert . state ( ! this . encodeState . equals ( EncodeState . FULLY_ENCODED ) ,
"URI components already encoded, and could not possibly contain '{' or '}'." ) ;
String schemeTo = expandUriComponent ( getScheme ( ) , uriVariables , this . variableEncoder ) ;
String fragmentTo = expandUriComponent ( getFragment ( ) , uriVariables , this . variableEncoder ) ;
String userInfoTo = expandUriComponent ( this . userInfo , uriVariables , this . variableEncoder ) ;
String hostTo = expandUriComponent ( this . host , uriVariables , this . variableEncoder ) ;
String portTo = expandUriComponent ( this . port , uriVariables , this . variableEncoder ) ;
PathComponent pathTo = this . path . expand ( uriVariables , this . variableEncoder ) ;
MultiValueMap < String , String > paramsTo = expandQueryParams ( uriVariables ) ;
return new HierarchicalUriComponents (
sc hemeT o, fragmen tTo, userInfoTo , hostTo , portTo , pathTo , paramsTo , false , false ) ;
return new HierarchicalUriComponents ( schemeTo , fragmentTo , userInfoTo ,
hos tTo , portTo , pathTo , paramsTo , this . encodeState , this . variableEncoder ) ;
}
private MultiValueMap < String , String > expandQueryParams ( UriTemplateVariables variables ) {
@ -404,10 +440,10 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -404,10 +440,10 @@ final class HierarchicalUriComponents extends UriComponents {
MultiValueMap < String , String > result = new LinkedMultiValueMap < > ( size ) ;
UriTemplateVariables queryVariables = new QueryUriTemplateVariables ( variables ) ;
this . queryParams . forEach ( ( key , values ) - > {
String name = expandUriComponent ( key , queryVariables ) ;
String name = expandUriComponent ( key , queryVariables , this . variableEncoder ) ;
List < String > expandedValues = new ArrayList < > ( values . size ( ) ) ;
for ( String value : values ) {
expandedValues . add ( expandUriComponent ( value , queryVariables ) ) ;
expandedValues . add ( expandUriComponent ( value , queryVariables , this . variableEncoder ) ) ;
}
result . put ( name , expandedValues ) ;
} ) ;
@ -417,8 +453,9 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -417,8 +453,9 @@ final class HierarchicalUriComponents extends UriComponents {
@Override
public UriComponents normalize ( ) {
String normalizedPath = StringUtils . cleanPath ( getPath ( ) ) ;
FullPathComponent path = new FullPathComponent ( normalizedPath ) ;
return new HierarchicalUriComponents ( getScheme ( ) , getFragment ( ) , this . userInfo , this . host , this . port ,
new FullPathComponent ( normalizedPa th) , this . queryParams , this . encoded , false ) ;
pa th, this . queryParams , this . encodeState , this . variableEncoder ) ;
}
@ -462,7 +499,7 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -462,7 +499,7 @@ final class HierarchicalUriComponents extends UriComponents {
@Override
public URI toUri ( ) {
try {
if ( this . encoded ) {
if ( this . encodeState . isEnco ded ( ) ) {
return new URI ( toUriString ( ) ) ;
}
else {
@ -689,6 +726,102 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -689,6 +726,102 @@ final class HierarchicalUriComponents extends UriComponents {
}
private enum EncodeState {
/ * *
* Not encoded .
* /
RAW ,
/ * *
* URI vars expanded first and then each URI component encoded by
* quoting only illegal characters within that URI component .
* /
FULLY_ENCODED ,
/ * *
* URI template encoded first by quoting illegal characters only , and
* then URI vars encoded more strictly when expanded , by quoting both
* illegal chars and chars with reserved meaning .
* /
TEMPLATE_ENCODED ;
public boolean isEncoded ( ) {
return this . equals ( FULLY_ENCODED ) | | this . equals ( TEMPLATE_ENCODED ) ;
}
}
private static class UriTemplateEncoder implements BiFunction < String , Type , String > {
private final Charset charset ;
private final StringBuilder currentLiteral = new StringBuilder ( ) ;
private final StringBuilder output = new StringBuilder ( ) ;
public UriTemplateEncoder ( Charset charset ) {
this . charset = charset ;
}
@Override
public String apply ( String source , Type type ) {
// Only URI variable, nothing to encode..
if ( source . length ( ) > 1 & & source . charAt ( 0 ) = = '{' & & source . charAt ( source . length ( ) - 1 ) = = '}' ) {
return source ;
}
// Only literal, encode all..
if ( source . indexOf ( '{' ) = = - 1 ) {
return encodeUriComponent ( source , this . charset , type ) ;
}
// Mixed, encode all except for URI variables..
int level = 0 ;
clear ( this . currentLiteral ) ;
clear ( this . output ) ;
for ( char c : source . toCharArray ( ) ) {
if ( c = = '{' ) {
level + + ;
if ( level = = 1 ) {
encodeAndAppendCurrentLiteral ( type ) ;
}
}
if ( c = = '}' ) {
level - - ;
Assert . isTrue ( level > = 0 , "Mismatched open and close braces" ) ;
}
if ( level > 0 | | ( level = = 0 & & c = = '}' ) ) {
this . output . append ( c ) ;
}
else {
this . currentLiteral . append ( c ) ;
}
}
Assert . isTrue ( level = = 0 , "Mismatched open and close braces" ) ;
encodeAndAppendCurrentLiteral ( type ) ;
return this . output . toString ( ) ;
}
private void encodeAndAppendCurrentLiteral ( Type type ) {
this . output . append ( encodeUriComponent ( this . currentLiteral . toString ( ) , this . charset , type ) ) ;
clear ( this . currentLiteral ) ;
}
private void clear ( StringBuilder sb ) {
sb . delete ( 0 , sb . length ( ) ) ;
}
}
/ * *
* Defines the contract for path ( segments ) .
* /
@ -698,11 +831,11 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -698,11 +831,11 @@ final class HierarchicalUriComponents extends UriComponents {
List < String > getPathSegments ( ) ;
PathComponent encode ( Charset charset ) ;
PathComponent encode ( BiFunction < String , Type , String > encoder ) ;
void verify ( ) ;
PathComponent expand ( UriTemplateVariables uriVariables ) ;
PathComponent expand ( UriTemplateVariables uriVariables , @Nullable UnaryOperator < String > encoder ) ;
void copyToUriComponentsBuilder ( UriComponentsBuilder builder ) ;
}
@ -731,8 +864,8 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -731,8 +864,8 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent encode ( Charset charset ) {
String encodedPath = encodeUriComponent ( getPath ( ) , charset , Type . PATH ) ;
public PathComponent encode ( BiFunction < String , Type , String > encoder ) {
String encodedPath = encoder . apply ( getPath ( ) , Type . PATH ) ;
return new FullPathComponent ( encodedPath ) ;
}
@ -742,8 +875,8 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -742,8 +875,8 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent expand ( UriTemplateVariables uriVariables ) {
String expandedPath = expandUriComponent ( getPath ( ) , uriVariables ) ;
public PathComponent expand ( UriTemplateVariables uriVariables , @Nullable UnaryOperator < String > encoder ) {
String expandedPath = expandUriComponent ( getPath ( ) , uriVariables , encoder ) ;
return new FullPathComponent ( expandedPath ) ;
}
@ -797,11 +930,11 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -797,11 +930,11 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent encode ( Charset charset ) {
public PathComponent encode ( BiFunction < String , Type , String > encoder ) {
List < String > pathSegments = getPathSegments ( ) ;
List < String > encodedPathSegments = new ArrayList < > ( pathSegments . size ( ) ) ;
for ( String pathSegment : pathSegments ) {
String encodedPathSegment = encodeUriComponent ( pathSegment , charse t , Type . PATH_SEGMENT ) ;
String encodedPathSegment = encoder . apply ( pathSegmen t , Type . PATH_SEGMENT ) ;
encodedPathSegments . add ( encodedPathSegment ) ;
}
return new PathSegmentComponent ( encodedPathSegments ) ;
@ -815,11 +948,11 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -815,11 +948,11 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent expand ( UriTemplateVariables uriVariables ) {
public PathComponent expand ( UriTemplateVariables uriVariables , @Nullable UnaryOperator < String > encoder ) {
List < String > pathSegments = getPathSegments ( ) ;
List < String > expandedPathSegments = new ArrayList < > ( pathSegments . size ( ) ) ;
for ( String pathSegment : pathSegments ) {
String expandedPathSegment = expandUriComponent ( pathSegment , uriVariables ) ;
String expandedPathSegment = expandUriComponent ( pathSegment , uriVariables , encoder ) ;
expandedPathSegments . add ( expandedPathSegment ) ;
}
return new PathSegmentComponent ( expandedPathSegments ) ;
@ -874,10 +1007,10 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -874,10 +1007,10 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent encode ( Charset charset ) {
public PathComponent encode ( BiFunction < String , Type , String > encoder ) {
List < PathComponent > encodedComponents = new ArrayList < > ( this . pathComponents . size ( ) ) ;
for ( PathComponent pathComponent : this . pathComponents ) {
encodedComponents . add ( pathComponent . encode ( charset ) ) ;
encodedComponents . add ( pathComponent . encode ( encoder ) ) ;
}
return new PathComponentComposite ( encodedComponents ) ;
}
@ -890,10 +1023,10 @@ final class HierarchicalUriComponents extends UriComponents {
@@ -890,10 +1023,10 @@ final class HierarchicalUriComponents extends UriComponents {
}
@Override
public PathComponent expand ( UriTemplateVariables uriVariables ) {
public PathComponent expand ( UriTemplateVariables uriVariables , @Nullable UnaryOperator < String > encoder ) {
List < PathComponent > expandedComponents = new ArrayList < > ( this . pathComponents . size ( ) ) ;
for ( PathComponent pathComponent : this . pathComponents ) {
expandedComponents . add ( pathComponent . expand ( uriVariables ) ) ;
expandedComponents . add ( pathComponent . expand ( uriVariables , encoder ) ) ;
}
return new PathComponentComposite ( expandedComponents ) ;
}