|
|
|
@ -39,6 +39,7 @@ import org.springframework.util.StringUtils; |
|
|
|
* Extension of {@link UriComponents} for hierarchical URIs. |
|
|
|
* Extension of {@link UriComponents} for hierarchical URIs. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Arjen Poutsma |
|
|
|
|
|
|
|
* @author Rossen Stoyanchev |
|
|
|
* @author Phillip Webb |
|
|
|
* @author Phillip Webb |
|
|
|
* @since 3.1.3 |
|
|
|
* @since 3.1.3 |
|
|
|
* @see <a href="http://tools.ietf.org/html/rfc3986#section-1.2.3">Hierarchical URIs</a> |
|
|
|
* @see <a href="http://tools.ietf.org/html/rfc3986#section-1.2.3">Hierarchical URIs</a> |
|
|
|
@ -73,8 +74,9 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
* @param encoded whether the components are already encoded |
|
|
|
* @param encoded whether the components are already encoded |
|
|
|
* @param verify whether the components need to be checked for illegal characters |
|
|
|
* @param verify whether the components need to be checked for illegal characters |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
HierarchicalUriComponents(String scheme, String userInfo, String host, String port, PathComponent path, |
|
|
|
HierarchicalUriComponents(String scheme, String userInfo, String host, String port, |
|
|
|
MultiValueMap<String, String> queryParams, String fragment, boolean encoded, boolean verify) { |
|
|
|
PathComponent path, MultiValueMap<String, String> queryParams, |
|
|
|
|
|
|
|
String fragment, boolean encoded, boolean verify) { |
|
|
|
|
|
|
|
|
|
|
|
super(scheme, fragment); |
|
|
|
super(scheme, fragment); |
|
|
|
this.userInfo = userInfo; |
|
|
|
this.userInfo = userInfo; |
|
|
|
@ -175,41 +177,44 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
// encoding
|
|
|
|
// encoding
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Encodes all URI components using their specific encoding rules, and returns the result as a new |
|
|
|
* Encode all URI components using their specific encoding rules and return |
|
|
|
* {@code UriComponents} instance. |
|
|
|
* the result as a new {@code UriComponents} instance. |
|
|
|
* @param encoding the encoding of the values contained in this map |
|
|
|
* @param encoding the encoding of the values contained in this map |
|
|
|
* @return the encoded uri components |
|
|
|
* @return the encoded uri components |
|
|
|
* @throws UnsupportedEncodingException if the given encoding is not supported |
|
|
|
* @throws UnsupportedEncodingException if the given encoding is not supported |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public HierarchicalUriComponents encode(String encoding) throws UnsupportedEncodingException { |
|
|
|
public HierarchicalUriComponents encode(String encoding) throws UnsupportedEncodingException { |
|
|
|
Assert.hasLength(encoding, "Encoding must not be empty"); |
|
|
|
|
|
|
|
if (this.encoded) { |
|
|
|
if (this.encoded) { |
|
|
|
return this; |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
String encodedScheme = encodeUriComponent(getScheme(), encoding, Type.SCHEME); |
|
|
|
Assert.hasLength(encoding, "Encoding must not be empty"); |
|
|
|
String encodedUserInfo = encodeUriComponent(this.userInfo, encoding, Type.USER_INFO); |
|
|
|
String schemeTo = encodeUriComponent(getScheme(), encoding, Type.SCHEME); |
|
|
|
String encodedHost = encodeUriComponent(this.host, encoding, getHostType()); |
|
|
|
String userInfoTo = encodeUriComponent(this.userInfo, encoding, Type.USER_INFO); |
|
|
|
|
|
|
|
String hostTo = encodeUriComponent(this.host, encoding, getHostType()); |
|
|
|
|
|
|
|
PathComponent pathTo = this.path.encode(encoding); |
|
|
|
|
|
|
|
MultiValueMap<String, String> paramsTo = encodeQueryParams(encoding); |
|
|
|
|
|
|
|
String fragmentTo = encodeUriComponent(this.getFragment(), encoding, Type.FRAGMENT); |
|
|
|
|
|
|
|
return new HierarchicalUriComponents(schemeTo, userInfoTo, hostTo, this.port, |
|
|
|
|
|
|
|
pathTo, paramsTo, fragmentTo, true, false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PathComponent encodedPath = this.path.encode(encoding); |
|
|
|
private MultiValueMap<String, String> encodeQueryParams(String encoding) throws UnsupportedEncodingException { |
|
|
|
MultiValueMap<String, String> encodedQueryParams = |
|
|
|
int size = this.queryParams.size(); |
|
|
|
new LinkedMultiValueMap<String, String>(this.queryParams.size()); |
|
|
|
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(size); |
|
|
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) { |
|
|
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) { |
|
|
|
String encodedName = encodeUriComponent(entry.getKey(), encoding, Type.QUERY_PARAM); |
|
|
|
String name = encodeUriComponent(entry.getKey(), encoding, Type.QUERY_PARAM); |
|
|
|
List<String> encodedValues = new ArrayList<String>(entry.getValue().size()); |
|
|
|
List<String> values = new ArrayList<String>(entry.getValue().size()); |
|
|
|
for (String value : entry.getValue()) { |
|
|
|
for (String value : entry.getValue()) { |
|
|
|
String encodedValue = encodeUriComponent(value, encoding, Type.QUERY_PARAM); |
|
|
|
values.add(encodeUriComponent(value, encoding, Type.QUERY_PARAM)); |
|
|
|
encodedValues.add(encodedValue); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
encodedQueryParams.put(encodedName, encodedValues); |
|
|
|
result.put(name, values); |
|
|
|
} |
|
|
|
} |
|
|
|
String encodedFragment = encodeUriComponent(this.getFragment(), encoding, Type.FRAGMENT); |
|
|
|
return result; |
|
|
|
return new HierarchicalUriComponents(encodedScheme, encodedUserInfo, encodedHost, this.port, encodedPath, |
|
|
|
|
|
|
|
encodedQueryParams, encodedFragment, true, false); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Encodes the given source into an encoded String using the rules specified |
|
|
|
* Encode the given source into an encoded String using the rules specified |
|
|
|
* by the given component and with the given options. |
|
|
|
* by the given component and with the given options. |
|
|
|
* @param source the source string |
|
|
|
* @param source the source string |
|
|
|
* @param encoding the encoding of the source string |
|
|
|
* @param encoding the encoding of the source string |
|
|
|
@ -217,7 +222,9 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
* @return the encoded URI |
|
|
|
* @return the encoded URI |
|
|
|
* @throws IllegalArgumentException when the given uri parameter is not a valid URI |
|
|
|
* @throws IllegalArgumentException when the given uri parameter is not a valid URI |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
static String encodeUriComponent(String source, String encoding, Type type) throws UnsupportedEncodingException { |
|
|
|
static String encodeUriComponent(String source, String encoding, Type type) |
|
|
|
|
|
|
|
throws UnsupportedEncodingException { |
|
|
|
|
|
|
|
|
|
|
|
if (source == null) { |
|
|
|
if (source == null) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -291,17 +298,19 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
int u = Character.digit(hex1, 16); |
|
|
|
int u = Character.digit(hex1, 16); |
|
|
|
int l = Character.digit(hex2, 16); |
|
|
|
int l = Character.digit(hex2, 16); |
|
|
|
if (u == -1 || l == -1) { |
|
|
|
if (u == -1 || l == -1) { |
|
|
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); |
|
|
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + |
|
|
|
|
|
|
|
source.substring(i) + "\""); |
|
|
|
} |
|
|
|
} |
|
|
|
i += 2; |
|
|
|
i += 2; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); |
|
|
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + |
|
|
|
|
|
|
|
source.substring(i) + "\""); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (!type.isAllowed(ch)) { |
|
|
|
else if (!type.isAllowed(ch)) { |
|
|
|
throw new IllegalArgumentException( |
|
|
|
throw new IllegalArgumentException("Invalid character '" + ch + "' for " + |
|
|
|
"Invalid character '" + ch + "' for " + type.name() + " in \"" + source + "\""); |
|
|
|
type.name() + " in \"" + source + "\""); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -312,25 +321,31 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) { |
|
|
|
protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) { |
|
|
|
Assert.state(!this.encoded, "Cannot expand an already encoded UriComponents object"); |
|
|
|
Assert.state(!this.encoded, "Cannot expand an already encoded UriComponents object"); |
|
|
|
String expandedScheme = expandUriComponent(getScheme(), uriVariables); |
|
|
|
|
|
|
|
String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables); |
|
|
|
String schemeTo = expandUriComponent(getScheme(), uriVariables); |
|
|
|
String expandedHost = expandUriComponent(this.host, uriVariables); |
|
|
|
String userInfoTo = expandUriComponent(this.userInfo, uriVariables); |
|
|
|
String expandedPort = expandUriComponent(this.port, uriVariables); |
|
|
|
String hostTo = expandUriComponent(this.host, uriVariables); |
|
|
|
PathComponent expandedPath = this.path.expand(uriVariables); |
|
|
|
String portTo = expandUriComponent(this.port, uriVariables); |
|
|
|
MultiValueMap<String, String> expandedQueryParams = |
|
|
|
PathComponent pathTo = this.path.expand(uriVariables); |
|
|
|
new LinkedMultiValueMap<String, String>(this.queryParams.size()); |
|
|
|
MultiValueMap<String, String> paramsTo = expandQueryParams(uriVariables); |
|
|
|
|
|
|
|
String fragmentTo = expandUriComponent(this.getFragment(), uriVariables); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return new HierarchicalUriComponents(schemeTo, userInfoTo, hostTo, portTo, |
|
|
|
|
|
|
|
pathTo, paramsTo, fragmentTo, false, false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private MultiValueMap<String, String> expandQueryParams(UriTemplateVariables variables) { |
|
|
|
|
|
|
|
int size = this.queryParams.size(); |
|
|
|
|
|
|
|
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(size); |
|
|
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) { |
|
|
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) { |
|
|
|
String expandedName = expandUriComponent(entry.getKey(), uriVariables); |
|
|
|
String name = expandUriComponent(entry.getKey(), variables); |
|
|
|
List<String> expandedValues = new ArrayList<String>(entry.getValue().size()); |
|
|
|
List<String> values = new ArrayList<String>(entry.getValue().size()); |
|
|
|
for (String value : entry.getValue()) { |
|
|
|
for (String value : entry.getValue()) { |
|
|
|
String expandedValue = expandUriComponent(value, uriVariables); |
|
|
|
values.add(expandUriComponent(value, variables)); |
|
|
|
expandedValues.add(expandedValue); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
expandedQueryParams.put(expandedName, expandedValues); |
|
|
|
result.put(name, values); |
|
|
|
} |
|
|
|
} |
|
|
|
String expandedFragment = expandUriComponent(this.getFragment(), uriVariables); |
|
|
|
return result; |
|
|
|
return new HierarchicalUriComponents(expandedScheme, expandedUserInfo, expandedHost, expandedPort, expandedPath, |
|
|
|
|
|
|
|
expandedQueryParams, expandedFragment, false, false); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -468,7 +483,7 @@ final class HierarchicalUriComponents extends UriComponents { |
|
|
|
* <p>Contains methods to indicate whether a given character is valid in a specific URI component. |
|
|
|
* <p>Contains methods to indicate whether a given character is valid in a specific URI component. |
|
|
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a> |
|
|
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
static enum Type { |
|
|
|
enum Type { |
|
|
|
|
|
|
|
|
|
|
|
SCHEME { |
|
|
|
SCHEME { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
|