Backported "Support opaque URIs in UriComponentsBuilder"
Issue: SPR-9798 Issue: SPR-9804
This commit is contained in:
@@ -0,0 +1,836 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link UriComponents} for hierarchical URIs.
|
||||||
|
*
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @since 3.2
|
||||||
|
* @see <a href="http://tools.ietf.org/html/rfc3986#section-1.2.3">Hierarchical URIs</a>
|
||||||
|
*/
|
||||||
|
final class HierarchicalUriComponents extends UriComponents {
|
||||||
|
|
||||||
|
private static final char PATH_DELIMITER = '/';
|
||||||
|
|
||||||
|
private final String userInfo;
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
private final PathComponent path;
|
||||||
|
|
||||||
|
private final MultiValueMap<String, String> queryParams;
|
||||||
|
|
||||||
|
private final boolean encoded;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package-private constructor. All arguments are optional, and can be {@code null}.
|
||||||
|
* @param scheme the scheme
|
||||||
|
* @param userInfo the user info
|
||||||
|
* @param host the host
|
||||||
|
* @param port the port
|
||||||
|
* @param path the path
|
||||||
|
* @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(String scheme, String userInfo, String host, int port, PathComponent path,
|
||||||
|
MultiValueMap<String, String> queryParams, String fragment, boolean encoded, boolean verify) {
|
||||||
|
|
||||||
|
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<String, String>(0));
|
||||||
|
this.encoded = encoded;
|
||||||
|
if (verify) {
|
||||||
|
verify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// component getters
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchemeSpecificPart() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserInfo() {
|
||||||
|
return this.userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHost() {
|
||||||
|
return this.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPort() {
|
||||||
|
return this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return this.path.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
return this.path.getPathSegments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
if (!this.queryParams.isEmpty()) {
|
||||||
|
StringBuilder queryBuilder = new StringBuilder();
|
||||||
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
List<String> values = entry.getValue();
|
||||||
|
if (CollectionUtils.isEmpty(values)) {
|
||||||
|
if (queryBuilder.length() != 0) {
|
||||||
|
queryBuilder.append('&');
|
||||||
|
}
|
||||||
|
queryBuilder.append(name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (Object value : values) {
|
||||||
|
if (queryBuilder.length() != 0) {
|
||||||
|
queryBuilder.append('&');
|
||||||
|
}
|
||||||
|
queryBuilder.append(name);
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
queryBuilder.append('=');
|
||||||
|
queryBuilder.append(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queryBuilder.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map of query parameters. Empty if no query has been set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MultiValueMap<String, String> getQueryParams() {
|
||||||
|
return this.queryParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// encoding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes all URI components using their specific encoding rules, and returns the result as a new
|
||||||
|
* {@code UriComponents} instance.
|
||||||
|
* @param encoding the encoding of the values contained in this map
|
||||||
|
* @return the encoded uri components
|
||||||
|
* @throws UnsupportedEncodingException if the given encoding is not supported
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HierarchicalUriComponents encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||||
|
|
||||||
|
if (this.encoded) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String encodedScheme = encodeUriComponent(this.getScheme(), encoding, Type.SCHEME);
|
||||||
|
String encodedUserInfo = encodeUriComponent(this.userInfo, encoding, Type.USER_INFO);
|
||||||
|
String encodedHost = encodeUriComponent(this.host, encoding, Type.HOST);
|
||||||
|
PathComponent encodedPath = this.path.encode(encoding);
|
||||||
|
MultiValueMap<String, String> encodedQueryParams =
|
||||||
|
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
||||||
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
String encodedName = encodeUriComponent(entry.getKey(), encoding, Type.QUERY_PARAM);
|
||||||
|
List<String> encodedValues = new ArrayList<String>(entry.getValue().size());
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
String encodedValue = encodeUriComponent(value, encoding, Type.QUERY_PARAM);
|
||||||
|
encodedValues.add(encodedValue);
|
||||||
|
}
|
||||||
|
encodedQueryParams.put(encodedName, encodedValues);
|
||||||
|
}
|
||||||
|
String encodedFragment = encodeUriComponent(this.getFragment(), encoding, Type.FRAGMENT);
|
||||||
|
|
||||||
|
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
|
||||||
|
* by the given component and with the given options.
|
||||||
|
* @param source the source string
|
||||||
|
* @param encoding the encoding of the source string
|
||||||
|
* @param type the URI component for the source
|
||||||
|
* @return the encoded URI
|
||||||
|
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||||
|
*/
|
||||||
|
static String encodeUriComponent(String source, String encoding, Type type)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||||
|
|
||||||
|
byte[] bytes = encodeBytes(source.getBytes(encoding), type);
|
||||||
|
return new String(bytes, "US-ASCII");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encodeBytes(byte[] source, Type type) {
|
||||||
|
Assert.notNull(source, "'source' must not be null");
|
||||||
|
Assert.notNull(type, "'type' must not be null");
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(source.length);
|
||||||
|
for (int i = 0; i < source.length; i++) {
|
||||||
|
int b = source[i];
|
||||||
|
if (b < 0) {
|
||||||
|
b += 256;
|
||||||
|
}
|
||||||
|
if (type.isAllowed(b)) {
|
||||||
|
bos.write(b);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bos.write('%');
|
||||||
|
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
|
||||||
|
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
|
||||||
|
bos.write(hex1);
|
||||||
|
bos.write(hex2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// verifying
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies all URI components to determine whether they contain any illegal
|
||||||
|
* characters, throwing an {@code IllegalArgumentException} if so.
|
||||||
|
* @throws IllegalArgumentException if any component has illegal characters
|
||||||
|
*/
|
||||||
|
private void verify() {
|
||||||
|
if (!this.encoded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verifyUriComponent(getScheme(), Type.SCHEME);
|
||||||
|
verifyUriComponent(userInfo, Type.USER_INFO);
|
||||||
|
verifyUriComponent(host, Type.HOST);
|
||||||
|
this.path.verify();
|
||||||
|
for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
|
||||||
|
verifyUriComponent(entry.getKey(), Type.QUERY_PARAM);
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
verifyUriComponent(value, Type.QUERY_PARAM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verifyUriComponent(getFragment(), Type.FRAGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verifyUriComponent(String source, Type type) {
|
||||||
|
if (source == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = source.length();
|
||||||
|
|
||||||
|
for (int i=0; i < length; i++) {
|
||||||
|
char ch = source.charAt(i);
|
||||||
|
if (ch == '%') {
|
||||||
|
if ((i + 2) < length) {
|
||||||
|
char hex1 = source.charAt(i + 1);
|
||||||
|
char hex2 = source.charAt(i + 2);
|
||||||
|
int u = Character.digit(hex1, 16);
|
||||||
|
int l = Character.digit(hex2, 16);
|
||||||
|
if (u == -1 || l == -1) {
|
||||||
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\"");
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!type.isAllowed(ch)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid character '" + ch + "' for " + type.name() + " in \"" + source + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// expanding
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) {
|
||||||
|
Assert.state(!encoded, "Cannot expand an already encoded UriComponents object");
|
||||||
|
|
||||||
|
String expandedScheme = expandUriComponent(this.getScheme(), uriVariables);
|
||||||
|
String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables);
|
||||||
|
String expandedHost = expandUriComponent(this.host, uriVariables);
|
||||||
|
PathComponent expandedPath = this.path.expand(uriVariables);
|
||||||
|
MultiValueMap<String, String> expandedQueryParams =
|
||||||
|
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
||||||
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
String expandedName = expandUriComponent(entry.getKey(), uriVariables);
|
||||||
|
List<String> expandedValues = new ArrayList<String>(entry.getValue().size());
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
String expandedValue = expandUriComponent(value, uriVariables);
|
||||||
|
expandedValues.add(expandedValue);
|
||||||
|
}
|
||||||
|
expandedQueryParams.put(expandedName, expandedValues);
|
||||||
|
}
|
||||||
|
String expandedFragment = expandUriComponent(this.getFragment(), uriVariables);
|
||||||
|
|
||||||
|
return new HierarchicalUriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPath,
|
||||||
|
expandedQueryParams, expandedFragment, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize the path removing sequences like "path/..".
|
||||||
|
* @see StringUtils#cleanPath(String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UriComponents normalize() {
|
||||||
|
String normalizedPath = StringUtils.cleanPath(getPath());
|
||||||
|
return new HierarchicalUriComponents(getScheme(), this.userInfo, this.host,
|
||||||
|
this.port, new FullPathComponent(normalizedPath), this.queryParams,
|
||||||
|
getFragment(), this.encoded, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// other functionality
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a URI string from this {@code UriComponents} instance.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toUriString() {
|
||||||
|
StringBuilder uriBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
if (getScheme() != null) {
|
||||||
|
uriBuilder.append(getScheme());
|
||||||
|
uriBuilder.append(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.userInfo != null || this.host != null) {
|
||||||
|
uriBuilder.append("//");
|
||||||
|
if (this.userInfo != null) {
|
||||||
|
uriBuilder.append(this.userInfo);
|
||||||
|
uriBuilder.append('@');
|
||||||
|
}
|
||||||
|
if (this.host != null) {
|
||||||
|
uriBuilder.append(host);
|
||||||
|
}
|
||||||
|
if (this.port != -1) {
|
||||||
|
uriBuilder.append(':');
|
||||||
|
uriBuilder.append(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = getPath();
|
||||||
|
if (StringUtils.hasLength(path)) {
|
||||||
|
if (uriBuilder.length() != 0 && path.charAt(0) != PATH_DELIMITER) {
|
||||||
|
uriBuilder.append(PATH_DELIMITER);
|
||||||
|
}
|
||||||
|
uriBuilder.append(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
String query = getQuery();
|
||||||
|
if (query != null) {
|
||||||
|
uriBuilder.append('?');
|
||||||
|
uriBuilder.append(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFragment() != null) {
|
||||||
|
uriBuilder.append('#');
|
||||||
|
uriBuilder.append(getFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
return uriBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code URI} from this {@code UriComponents} instance.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URI toUri() {
|
||||||
|
try {
|
||||||
|
if (this.encoded) {
|
||||||
|
return new URI(toString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String path = getPath();
|
||||||
|
if (StringUtils.hasLength(path) && path.charAt(0) != PATH_DELIMITER) {
|
||||||
|
path = PATH_DELIMITER + path;
|
||||||
|
}
|
||||||
|
return new URI(getScheme(), getUserInfo(), getHost(), getPort(), path, getQuery(),
|
||||||
|
getFragment());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof OpaqueUriComponents)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HierarchicalUriComponents other = (HierarchicalUriComponents) obj;
|
||||||
|
|
||||||
|
if (ObjectUtils.nullSafeEquals(getScheme(), other.getScheme())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ObjectUtils.nullSafeEquals(getUserInfo(), other.getUserInfo())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ObjectUtils.nullSafeEquals(getHost(), other.getHost())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.port != other.port) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.path.equals(other.path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.queryParams.equals(other.queryParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ObjectUtils.nullSafeEquals(getFragment(), other.getFragment())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = ObjectUtils.nullSafeHashCode(getScheme());
|
||||||
|
result = 31 * result + ObjectUtils.nullSafeHashCode(this.userInfo);
|
||||||
|
result = 31 * result + ObjectUtils.nullSafeHashCode(this.host);
|
||||||
|
result = 31 * result + this.port;
|
||||||
|
result = 31 * result + this.path.hashCode();
|
||||||
|
result = 31 * result + this.queryParams.hashCode();
|
||||||
|
result = 31 * result + ObjectUtils.nullSafeHashCode(getFragment());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// inner types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration used to identify the parts of a URI.
|
||||||
|
* <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>
|
||||||
|
*/
|
||||||
|
static enum Type {
|
||||||
|
|
||||||
|
SCHEME {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isAlpha(c) || isDigit(c) || '+' == c || '-' == c || '.' == c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AUTHORITY {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isUnreserved(c) || isSubDelimiter(c) || ':' == c || '@' == c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
USER_INFO {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isUnreserved(c) || isSubDelimiter(c) || ':' == c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HOST {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isUnreserved(c) || isSubDelimiter(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PORT {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isDigit(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PATH {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isPchar(c) || '/' == c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PATH_SEGMENT {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isPchar(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
QUERY {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isPchar(c) || '/' == c || '?' == c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
QUERY_PARAM {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
if ('=' == c || '+' == c || '&' == c) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return isPchar(c) || '/' == c || '?' == c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FRAGMENT {
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(int c) {
|
||||||
|
return isPchar(c) || '/' == c || '?' == c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is allowed in this URI component.
|
||||||
|
*
|
||||||
|
* @param c the character
|
||||||
|
* @return {@code true} if the character is allowed; {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public abstract boolean isAllowed(int c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code ALPHA} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isAlpha(int c) {
|
||||||
|
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code DIGIT} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isDigit(int c) {
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code gen-delims} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isGenericDelimiter(int c) {
|
||||||
|
return ':' == c || '/' == c || '?' == c || '#' == c || '[' == c || ']' == c || '@' == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code sub-delims} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isSubDelimiter(int c) {
|
||||||
|
return '!' == c || '$' == c || '&' == c || '\'' == c || '(' == c || ')' == c || '*' == c || '+' == c ||
|
||||||
|
',' == c || ';' == c || '=' == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code reserved} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isReserved(char c) {
|
||||||
|
return isGenericDelimiter(c) || isReserved(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code unreserved} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isUnreserved(int c) {
|
||||||
|
return isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the given character is in the {@code pchar} set.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
|
||||||
|
*/
|
||||||
|
protected boolean isPchar(int c) {
|
||||||
|
return isUnreserved(c) || isSubDelimiter(c) || ':' == c || '@' == c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the contract for path (segments).
|
||||||
|
*/
|
||||||
|
interface PathComponent {
|
||||||
|
|
||||||
|
String getPath();
|
||||||
|
|
||||||
|
List<String> getPathSegments();
|
||||||
|
|
||||||
|
PathComponent encode(String encoding) throws UnsupportedEncodingException;
|
||||||
|
|
||||||
|
void verify();
|
||||||
|
|
||||||
|
PathComponent expand(UriTemplateVariables uriVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a path backed by a string.
|
||||||
|
*/
|
||||||
|
final static class FullPathComponent implements PathComponent {
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
FullPathComponent(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
String delimiter = new String(new char[]{PATH_DELIMITER});
|
||||||
|
String[] pathSegments = StringUtils.tokenizeToStringArray(path, delimiter);
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(pathSegments));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
String encodedPath = encodeUriComponent(getPath(),encoding, Type.PATH);
|
||||||
|
return new FullPathComponent(encodedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verify() {
|
||||||
|
verifyUriComponent(this.path, Type.PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(UriTemplateVariables uriVariables) {
|
||||||
|
String expandedPath = expandUriComponent(getPath(), uriVariables);
|
||||||
|
return new FullPathComponent(expandedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return (this == obj || (obj instanceof FullPathComponent &&
|
||||||
|
getPath().equals(((FullPathComponent) obj).getPath())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getPath().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a path backed by a string list (i.e. path segments).
|
||||||
|
*/
|
||||||
|
final static class PathSegmentComponent implements PathComponent {
|
||||||
|
|
||||||
|
private final List<String> pathSegments;
|
||||||
|
|
||||||
|
PathSegmentComponent(List<String> pathSegments) {
|
||||||
|
this.pathSegments = Collections.unmodifiableList(pathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
StringBuilder pathBuilder = new StringBuilder();
|
||||||
|
pathBuilder.append(PATH_DELIMITER);
|
||||||
|
for (Iterator<String> iterator = this.pathSegments.iterator(); iterator.hasNext(); ) {
|
||||||
|
String pathSegment = iterator.next();
|
||||||
|
pathBuilder.append(pathSegment);
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
pathBuilder.append(PATH_DELIMITER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
return this.pathSegments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
List<String> pathSegments = getPathSegments();
|
||||||
|
List<String> encodedPathSegments = new ArrayList<String>(pathSegments.size());
|
||||||
|
for (String pathSegment : pathSegments) {
|
||||||
|
String encodedPathSegment = encodeUriComponent(pathSegment, encoding, Type.PATH_SEGMENT);
|
||||||
|
encodedPathSegments.add(encodedPathSegment);
|
||||||
|
}
|
||||||
|
return new PathSegmentComponent(encodedPathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verify() {
|
||||||
|
for (String pathSegment : getPathSegments()) {
|
||||||
|
verifyUriComponent(pathSegment, Type.PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(UriTemplateVariables uriVariables) {
|
||||||
|
List<String> pathSegments = getPathSegments();
|
||||||
|
List<String> expandedPathSegments = new ArrayList<String>(pathSegments.size());
|
||||||
|
for (String pathSegment : pathSegments) {
|
||||||
|
String expandedPathSegment = expandUriComponent(pathSegment, uriVariables);
|
||||||
|
expandedPathSegments.add(expandedPathSegment);
|
||||||
|
}
|
||||||
|
return new PathSegmentComponent(expandedPathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return (this == obj || (obj instanceof PathSegmentComponent &&
|
||||||
|
getPathSegments().equals(((PathSegmentComponent) obj).getPathSegments())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getPathSegments().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a collection of PathComponents.
|
||||||
|
*/
|
||||||
|
final static class PathComponentComposite implements PathComponent {
|
||||||
|
|
||||||
|
private final List<PathComponent> pathComponents;
|
||||||
|
|
||||||
|
PathComponentComposite(List<PathComponent> pathComponents) {
|
||||||
|
this.pathComponents = pathComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
StringBuilder pathBuilder = new StringBuilder();
|
||||||
|
for (PathComponent pathComponent : this.pathComponents) {
|
||||||
|
pathBuilder.append(pathComponent.getPath());
|
||||||
|
}
|
||||||
|
return pathBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
for (PathComponent pathComponent : this.pathComponents) {
|
||||||
|
result.addAll(pathComponent.getPathSegments());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
List<PathComponent> encodedComponents = new ArrayList<PathComponent>(pathComponents.size());
|
||||||
|
for (PathComponent pathComponent : pathComponents) {
|
||||||
|
encodedComponents.add(pathComponent.encode(encoding));
|
||||||
|
}
|
||||||
|
return new PathComponentComposite(encodedComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verify() {
|
||||||
|
for (PathComponent pathComponent : pathComponents) {
|
||||||
|
pathComponent.verify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(UriTemplateVariables uriVariables) {
|
||||||
|
List<PathComponent> expandedComponents = new ArrayList<PathComponent>(this.pathComponents.size());
|
||||||
|
for (PathComponent pathComponent : this.pathComponents) {
|
||||||
|
expandedComponents.add(pathComponent.expand(uriVariables));
|
||||||
|
}
|
||||||
|
return new PathComponentComposite(expandedComponents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an empty path.
|
||||||
|
*/
|
||||||
|
final static PathComponent NULL_PATH_COMPONENT = new PathComponent() {
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verify() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(UriTemplateVariables uriVariables) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return (this == obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link UriComponents} for opaque URIs.
|
||||||
|
*
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @since 3.2
|
||||||
|
* @see <a href="http://tools.ietf.org/html/rfc3986#section-1.2.3">Hierarchical vs Opaque URIs</a>
|
||||||
|
*/
|
||||||
|
final class OpaqueUriComponents extends UriComponents {
|
||||||
|
|
||||||
|
private static final MultiValueMap<String, String> QUERY_PARAMS_NONE = new LinkedMultiValueMap<String, String>(0);
|
||||||
|
|
||||||
|
private final String ssp;
|
||||||
|
|
||||||
|
|
||||||
|
OpaqueUriComponents(String scheme, String schemeSpecificPart, String fragment) {
|
||||||
|
super(scheme, fragment);
|
||||||
|
this.ssp = schemeSpecificPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchemeSpecificPart() {
|
||||||
|
return this.ssp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHost() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPort() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getPathSegments() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiValueMap<String, String> getQueryParams() {
|
||||||
|
return QUERY_PARAMS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UriComponents encode(String encoding) throws UnsupportedEncodingException {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UriComponents expandInternal(UriTemplateVariables uriVariables) {
|
||||||
|
String expandedScheme = expandUriComponent(this.getScheme(), uriVariables);
|
||||||
|
String expandedSSp = expandUriComponent(this.ssp, uriVariables);
|
||||||
|
String expandedFragment = expandUriComponent(this.getFragment(), uriVariables);
|
||||||
|
|
||||||
|
return new OpaqueUriComponents(expandedScheme, expandedSSp, expandedFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toUriString() {
|
||||||
|
StringBuilder uriBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
if (getScheme() != null) {
|
||||||
|
uriBuilder.append(getScheme());
|
||||||
|
uriBuilder.append(':');
|
||||||
|
}
|
||||||
|
if (this.ssp != null) {
|
||||||
|
uriBuilder.append(this.ssp);
|
||||||
|
}
|
||||||
|
if (getFragment() != null) {
|
||||||
|
uriBuilder.append('#');
|
||||||
|
uriBuilder.append(getFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
return uriBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI toUri() {
|
||||||
|
try {
|
||||||
|
return new URI(getScheme(), this.ssp, getFragment());
|
||||||
|
}
|
||||||
|
catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UriComponents normalize() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof OpaqueUriComponents)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpaqueUriComponents other = (OpaqueUriComponents) obj;
|
||||||
|
|
||||||
|
if (ObjectUtils.nullSafeEquals(getScheme(), other.getScheme())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ObjectUtils.nullSafeEquals(this.ssp, other.ssp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ObjectUtils.nullSafeEquals(getFragment(), other.getFragment())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = ObjectUtils.nullSafeHashCode(getScheme());
|
||||||
|
result = 31 * result + ObjectUtils.nullSafeHashCode(this.ssp);
|
||||||
|
result = 31 * result + ObjectUtils.nullSafeHashCode(getFragment());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,8 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for {@link UriComponents}.
|
* Builder for {@link UriComponents}.
|
||||||
* <p/>
|
*
|
||||||
* Typical usage involves:
|
* <p></p>Typical usage involves:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Create a {@code UriComponentsBuilder} with one of the static factory methods (such as
|
* <li>Create a {@code UriComponentsBuilder} with one of the static factory methods (such as
|
||||||
* {@link #fromPath(String)} or {@link #fromUri(URI)})</li>
|
* {@link #fromPath(String)} or {@link #fromUri(URI)})</li>
|
||||||
@@ -46,10 +46,10 @@ import org.springframework.util.StringUtils;
|
|||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
* @see #newInstance()
|
* @see #newInstance()
|
||||||
* @see #fromPath(String)
|
* @see #fromPath(String)
|
||||||
* @see #fromUri(URI)
|
* @see #fromUri(URI)
|
||||||
* @since 3.1
|
|
||||||
*/
|
*/
|
||||||
public class UriComponentsBuilder {
|
public class UriComponentsBuilder {
|
||||||
|
|
||||||
@@ -83,6 +83,8 @@ public class UriComponentsBuilder {
|
|||||||
|
|
||||||
private String scheme;
|
private String scheme;
|
||||||
|
|
||||||
|
private String ssp;
|
||||||
|
|
||||||
private String userInfo;
|
private String userInfo;
|
||||||
|
|
||||||
private String host;
|
private String host;
|
||||||
@@ -105,7 +107,7 @@ public class UriComponentsBuilder {
|
|||||||
protected UriComponentsBuilder() {
|
protected UriComponentsBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory methods
|
// Factory methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new, empty builder.
|
* Returns a new, empty builder.
|
||||||
@@ -143,7 +145,19 @@ public class UriComponentsBuilder {
|
|||||||
/**
|
/**
|
||||||
* Returns a builder that is initialized with the given URI string.
|
* Returns a builder that is initialized with the given URI string.
|
||||||
*
|
*
|
||||||
* @param uri the URI string to initialize with
|
* <p><strong>Note:</strong> The presence of reserved characters can prevent
|
||||||
|
* correct parsing of the URI string. For example if a query parameter
|
||||||
|
* contains {@code '='} or {@code '&'} characters, the query string cannot
|
||||||
|
* be parsed unambiguously. Such values should be substituted for URI
|
||||||
|
* variables to enable correct parsing:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* String uriString = "/hotels/42?filter={value}";
|
||||||
|
* UriComponentsBuilder.fromUriString(uriString).buildAndExpand("hot&cold");
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the URI string to initialize with
|
||||||
* @return the new {@code UriComponentsBuilder}
|
* @return the new {@code UriComponentsBuilder}
|
||||||
*/
|
*/
|
||||||
public static UriComponentsBuilder fromUriString(String uri) {
|
public static UriComponentsBuilder fromUriString(String uri) {
|
||||||
@@ -152,16 +166,43 @@ public class UriComponentsBuilder {
|
|||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
UriComponentsBuilder builder = new UriComponentsBuilder();
|
UriComponentsBuilder builder = new UriComponentsBuilder();
|
||||||
|
|
||||||
builder.scheme(m.group(2));
|
String scheme = m.group(2);
|
||||||
builder.userInfo(m.group(5));
|
String userInfo = m.group(5);
|
||||||
builder.host(m.group(6));
|
String host = m.group(6);
|
||||||
String port = m.group(8);
|
String port = m.group(8);
|
||||||
if (StringUtils.hasLength(port)) {
|
String path = m.group(9);
|
||||||
builder.port(Integer.parseInt(port));
|
String query = m.group(11);
|
||||||
|
String fragment = m.group(13);
|
||||||
|
|
||||||
|
boolean opaque = false;
|
||||||
|
|
||||||
|
if (StringUtils.hasLength(scheme)) {
|
||||||
|
String s = uri.substring(scheme.length());
|
||||||
|
if (!s.startsWith(":/")) {
|
||||||
|
opaque = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
builder.path(m.group(9));
|
|
||||||
builder.query(m.group(11));
|
builder.scheme(scheme);
|
||||||
builder.fragment(m.group(13));
|
|
||||||
|
|
||||||
|
if (opaque) {
|
||||||
|
String ssp = uri.substring(scheme.length()).substring(1);
|
||||||
|
if (StringUtils.hasLength(fragment)) {
|
||||||
|
ssp = ssp.substring(0, ssp.length() - (fragment.length() + 1));
|
||||||
|
}
|
||||||
|
builder.schemeSpecificPart(ssp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.userInfo(userInfo);
|
||||||
|
builder.host(host);
|
||||||
|
if (StringUtils.hasLength(port)) {
|
||||||
|
builder.port(Integer.parseInt(port));
|
||||||
|
}
|
||||||
|
builder.path(path);
|
||||||
|
builder.query(query);
|
||||||
|
}
|
||||||
|
builder.fragment(fragment);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@@ -173,6 +214,17 @@ public class UriComponentsBuilder {
|
|||||||
/**
|
/**
|
||||||
* Creates a new {@code UriComponents} object from the string HTTP URL.
|
* Creates a new {@code UriComponents} object from the string HTTP URL.
|
||||||
*
|
*
|
||||||
|
* <p><strong>Note:</strong> The presence of reserved characters can prevent
|
||||||
|
* correct parsing of the URI string. For example if a query parameter
|
||||||
|
* contains {@code '='} or {@code '&'} characters, the query string cannot
|
||||||
|
* be parsed unambiguously. Such values should be substituted for URI
|
||||||
|
* variables to enable correct parsing:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* String uriString = "/hotels/42?filter={value}";
|
||||||
|
* UriComponentsBuilder.fromUriString(uriString).buildAndExpand("hot&cold");
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* @param httpUrl the source URI
|
* @param httpUrl the source URI
|
||||||
* @return the URI components of the URI
|
* @return the URI components of the URI
|
||||||
*/
|
*/
|
||||||
@@ -201,7 +253,7 @@ public class UriComponentsBuilder {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// build methods
|
// build methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@code UriComponents} instance from the various components contained in this builder.
|
* Builds a {@code UriComponents} instance from the various components contained in this builder.
|
||||||
@@ -213,13 +265,21 @@ public class UriComponentsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@code UriComponents} instance from the various components contained in this builder.
|
* Builds a {@code UriComponents} instance from the various components
|
||||||
|
* contained in this builder.
|
||||||
*
|
*
|
||||||
* @param encoded whether all the components set in this builder are encoded ({@code true}) or not ({@code false}).
|
* @param encoded whether all the components set in this builder are
|
||||||
|
* encoded ({@code true}) or not ({@code false}).
|
||||||
* @return the URI components
|
* @return the URI components
|
||||||
*/
|
*/
|
||||||
public UriComponents build(boolean encoded) {
|
public UriComponents build(boolean encoded) {
|
||||||
return new UriComponents(scheme, userInfo, host, port, pathBuilder.build(), queryParams, fragment, encoded, true);
|
if (ssp != null) {
|
||||||
|
return new OpaqueUriComponents(scheme, ssp, fragment);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new HierarchicalUriComponents(
|
||||||
|
scheme, userInfo, host, port, pathBuilder.build(), queryParams, fragment, encoded, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,7 +306,7 @@ public class UriComponentsBuilder {
|
|||||||
return build(false).expand(uriVariableValues);
|
return build(false).expand(uriVariableValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
// URI components methods
|
// URI components methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all components of this URI builder with the components of the given URI.
|
* Initializes all components of this URI builder with the components of the given URI.
|
||||||
@@ -256,25 +316,31 @@ public class UriComponentsBuilder {
|
|||||||
*/
|
*/
|
||||||
public UriComponentsBuilder uri(URI uri) {
|
public UriComponentsBuilder uri(URI uri) {
|
||||||
Assert.notNull(uri, "'uri' must not be null");
|
Assert.notNull(uri, "'uri' must not be null");
|
||||||
Assert.isTrue(!uri.isOpaque(), "Opaque URI [" + uri + "] not supported");
|
|
||||||
|
|
||||||
this.scheme = uri.getScheme();
|
this.scheme = uri.getScheme();
|
||||||
|
|
||||||
if (uri.getRawUserInfo() != null) {
|
if (uri.isOpaque()) {
|
||||||
this.userInfo = uri.getRawUserInfo();
|
this.ssp = uri.getRawSchemeSpecificPart();
|
||||||
|
resetHierarchicalComponents();
|
||||||
}
|
}
|
||||||
if (uri.getHost() != null) {
|
else {
|
||||||
this.host = uri.getHost();
|
if (uri.getRawUserInfo() != null) {
|
||||||
}
|
this.userInfo = uri.getRawUserInfo();
|
||||||
if (uri.getPort() != -1) {
|
}
|
||||||
this.port = uri.getPort();
|
if (uri.getHost() != null) {
|
||||||
}
|
this.host = uri.getHost();
|
||||||
if (StringUtils.hasLength(uri.getRawPath())) {
|
}
|
||||||
this.pathBuilder = new FullPathComponentBuilder(uri.getRawPath());
|
if (uri.getPort() != -1) {
|
||||||
}
|
this.port = uri.getPort();
|
||||||
if (StringUtils.hasLength(uri.getRawQuery())) {
|
}
|
||||||
this.queryParams.clear();
|
if (StringUtils.hasLength(uri.getRawPath())) {
|
||||||
query(uri.getRawQuery());
|
this.pathBuilder = new FullPathComponentBuilder(uri.getRawPath());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(uri.getRawQuery())) {
|
||||||
|
this.queryParams.clear();
|
||||||
|
query(uri.getRawQuery());
|
||||||
|
}
|
||||||
|
resetSchemeSpecificPart();
|
||||||
}
|
}
|
||||||
if (uri.getRawFragment() != null) {
|
if (uri.getRawFragment() != null) {
|
||||||
this.fragment = uri.getRawFragment();
|
this.fragment = uri.getRawFragment();
|
||||||
@@ -282,11 +348,24 @@ public class UriComponentsBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetHierarchicalComponents() {
|
||||||
|
this.userInfo = null;
|
||||||
|
this.host = null;
|
||||||
|
this.port = -1;
|
||||||
|
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
||||||
|
this.queryParams.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetSchemeSpecificPart() {
|
||||||
|
this.ssp = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the URI scheme. The given scheme may contain URI template variables, and may also be {@code null} to clear the
|
* Sets the URI scheme. The given scheme may contain URI template variables,
|
||||||
* scheme of this builder.
|
* and may also be {@code null} to clear the scheme of this builder.
|
||||||
*
|
*
|
||||||
* @param scheme the URI scheme
|
* @param scheme
|
||||||
|
* the URI scheme
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder scheme(String scheme) {
|
public UriComponentsBuilder scheme(String scheme) {
|
||||||
@@ -295,26 +374,44 @@ public class UriComponentsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the URI user info. The given user info may contain URI template variables, and may also be {@code null} to
|
* Set the URI scheme-specific-part. When invoked, this method overwrites
|
||||||
* clear the user info of this builder.
|
* {@linkplain #userInfo(String) user-info}, {@linkplain #host(String) host},
|
||||||
|
* {@linkplain #port(int) port}, {@linkplain #path(String) path}, and
|
||||||
|
* {@link #query(String) query}.
|
||||||
|
*
|
||||||
|
* @param ssp the URI scheme-specific-part, may contain URI template parameters
|
||||||
|
* @return this UriComponentsBuilder
|
||||||
|
*/
|
||||||
|
public UriComponentsBuilder schemeSpecificPart(String ssp) {
|
||||||
|
this.ssp = ssp;
|
||||||
|
resetHierarchicalComponents();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the URI user info. The given user info may contain URI template
|
||||||
|
* variables, and may also be {@code null} to clear the user info of this
|
||||||
|
* builder.
|
||||||
*
|
*
|
||||||
* @param userInfo the URI user info
|
* @param userInfo the URI user info
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder userInfo(String userInfo) {
|
public UriComponentsBuilder userInfo(String userInfo) {
|
||||||
this.userInfo = userInfo;
|
this.userInfo = userInfo;
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the URI host. The given host may contain URI template variables, and may also be {@code null} to clear the host
|
* Sets the URI host. The given host may contain URI template variables, and
|
||||||
* of this builder.
|
* may also be {@code null} to clear the host of this builder.
|
||||||
*
|
*
|
||||||
* @param host the URI host
|
* @param host the URI host
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder host(String host) {
|
public UriComponentsBuilder host(String host) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,11 +424,13 @@ public class UriComponentsBuilder {
|
|||||||
public UriComponentsBuilder port(int port) {
|
public UriComponentsBuilder port(int port) {
|
||||||
Assert.isTrue(port >= -1, "'port' must not be < -1");
|
Assert.isTrue(port >= -1, "'port' must not be < -1");
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given path to the existing path of this builder. The given path may contain URI template variables.
|
* Appends the given path to the existing path of this builder. The given
|
||||||
|
* path may contain URI template variables.
|
||||||
*
|
*
|
||||||
* @param path the URI path
|
* @param path the URI path
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
@@ -343,6 +442,7 @@ public class UriComponentsBuilder {
|
|||||||
else {
|
else {
|
||||||
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
||||||
}
|
}
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,6 +455,7 @@ public class UriComponentsBuilder {
|
|||||||
public UriComponentsBuilder replacePath(String path) {
|
public UriComponentsBuilder replacePath(String path) {
|
||||||
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
||||||
path(path);
|
path(path);
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,11 +469,24 @@ public class UriComponentsBuilder {
|
|||||||
public UriComponentsBuilder pathSegment(String... pathSegments) throws IllegalArgumentException {
|
public UriComponentsBuilder pathSegment(String... pathSegments) throws IllegalArgumentException {
|
||||||
Assert.notNull(pathSegments, "'segments' must not be null");
|
Assert.notNull(pathSegments, "'segments' must not be null");
|
||||||
this.pathBuilder = this.pathBuilder.appendPathSegments(pathSegments);
|
this.pathBuilder = this.pathBuilder.appendPathSegments(pathSegments);
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given query to the existing query of this builder. The given query may contain URI template variables.
|
* Appends the given query to the existing query of this builder.
|
||||||
|
* The given query may contain URI template variables.
|
||||||
|
*
|
||||||
|
* <p><strong>Note:</strong> The presence of reserved characters can prevent
|
||||||
|
* correct parsing of the URI string. For example if a query parameter
|
||||||
|
* contains {@code '='} or {@code '&'} characters, the query string cannot
|
||||||
|
* be parsed unambiguously. Such values should be substituted for URI
|
||||||
|
* variables to enable correct parsing:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* String uriString = "/hotels/42?filter={value}";
|
||||||
|
* UriComponentsBuilder.fromUriString(uriString).buildAndExpand("hot&cold");
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param query the query string
|
* @param query the query string
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
@@ -389,6 +503,7 @@ public class UriComponentsBuilder {
|
|||||||
else {
|
else {
|
||||||
this.queryParams.clear();
|
this.queryParams.clear();
|
||||||
}
|
}
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,16 +516,20 @@ public class UriComponentsBuilder {
|
|||||||
public UriComponentsBuilder replaceQuery(String query) {
|
public UriComponentsBuilder replaceQuery(String query) {
|
||||||
this.queryParams.clear();
|
this.queryParams.clear();
|
||||||
query(query);
|
query(query);
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given query parameter to the existing query parameters. The given name or any of the values may contain
|
* Appends the given query parameter to the existing query parameters. The
|
||||||
* URI template variables. If no values are given, the resulting URI will contain the query parameter name only (i.e.
|
* given name or any of the values may contain URI template variables. If no
|
||||||
* {@code ?foo} instead of {@code ?foo=bar}.
|
* values are given, the resulting URI will contain the query parameter name
|
||||||
|
* only (i.e. {@code ?foo} instead of {@code ?foo=bar}.
|
||||||
*
|
*
|
||||||
* @param name the query parameter name
|
* @param name
|
||||||
* @param values the query parameter values
|
* the query parameter name
|
||||||
|
* @param values
|
||||||
|
* the query parameter values
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder queryParam(String name, Object... values) {
|
public UriComponentsBuilder queryParam(String name, Object... values) {
|
||||||
@@ -424,15 +543,19 @@ public class UriComponentsBuilder {
|
|||||||
else {
|
else {
|
||||||
this.queryParams.add(name, null);
|
this.queryParams.add(name, null);
|
||||||
}
|
}
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the query parameter values overriding all existing query values for the same parameter.
|
* Sets the query parameter values overriding all existing query values for
|
||||||
* If no values are given, the query parameter is removed.
|
* the same parameter. If no values are given, the query parameter is
|
||||||
|
* removed.
|
||||||
*
|
*
|
||||||
* @param name the query parameter name
|
* @param name
|
||||||
* @param values the query parameter values
|
* the query parameter name
|
||||||
|
* @param values
|
||||||
|
* the query parameter values
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder replaceQueryParam(String name, Object... values) {
|
public UriComponentsBuilder replaceQueryParam(String name, Object... values) {
|
||||||
@@ -441,14 +564,17 @@ public class UriComponentsBuilder {
|
|||||||
if (!ObjectUtils.isEmpty(values)) {
|
if (!ObjectUtils.isEmpty(values)) {
|
||||||
queryParam(name, values);
|
queryParam(name, values);
|
||||||
}
|
}
|
||||||
|
resetSchemeSpecificPart();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the URI fragment. The given fragment may contain URI template variables, and may also be {@code null} to clear
|
* Sets the URI fragment. The given fragment may contain URI template
|
||||||
* the fragment of this builder.
|
* variables, and may also be {@code null} to clear the fragment of this
|
||||||
|
* builder.
|
||||||
*
|
*
|
||||||
* @param fragment the URI fragment
|
* @param fragment
|
||||||
|
* the URI fragment
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder fragment(String fragment) {
|
public UriComponentsBuilder fragment(String fragment) {
|
||||||
@@ -463,11 +589,11 @@ public class UriComponentsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a builder for {@link org.springframework.web.util.UriComponents.PathComponent}
|
* Represents a builder for {@link HierarchicalUriComponents.PathComponent}
|
||||||
*/
|
*/
|
||||||
private interface PathComponentBuilder {
|
private interface PathComponentBuilder {
|
||||||
|
|
||||||
UriComponents.PathComponent build();
|
HierarchicalUriComponents.PathComponent build();
|
||||||
|
|
||||||
PathComponentBuilder appendPath(String path);
|
PathComponentBuilder appendPath(String path);
|
||||||
|
|
||||||
@@ -485,8 +611,8 @@ public class UriComponentsBuilder {
|
|||||||
this.path = new StringBuilder(path);
|
this.path = new StringBuilder(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UriComponents.PathComponent build() {
|
public HierarchicalUriComponents.PathComponent build() {
|
||||||
return new UriComponents.FullPathComponent(path.toString());
|
return new HierarchicalUriComponents.FullPathComponent(path.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathComponentBuilder appendPath(String path) {
|
public PathComponentBuilder appendPath(String path) {
|
||||||
@@ -522,8 +648,8 @@ public class UriComponentsBuilder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UriComponents.PathComponent build() {
|
public HierarchicalUriComponents.PathComponent build() {
|
||||||
return new UriComponents.PathSegmentComponent(pathSegments);
|
return new HierarchicalUriComponents.PathSegmentComponent(pathSegments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathComponentBuilder appendPath(String path) {
|
public PathComponentBuilder appendPath(String path) {
|
||||||
@@ -549,14 +675,14 @@ public class UriComponentsBuilder {
|
|||||||
pathComponentBuilders.add(builder);
|
pathComponentBuilders.add(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UriComponents.PathComponent build() {
|
public HierarchicalUriComponents.PathComponent build() {
|
||||||
List<UriComponents.PathComponent> pathComponents =
|
List<HierarchicalUriComponents.PathComponent> pathComponents =
|
||||||
new ArrayList<UriComponents.PathComponent>(pathComponentBuilders.size());
|
new ArrayList<HierarchicalUriComponents.PathComponent>(pathComponentBuilders.size());
|
||||||
|
|
||||||
for (PathComponentBuilder pathComponentBuilder : pathComponentBuilders) {
|
for (PathComponentBuilder pathComponentBuilder : pathComponentBuilders) {
|
||||||
pathComponents.add(pathComponentBuilder.build());
|
pathComponents.add(pathComponentBuilder.build());
|
||||||
}
|
}
|
||||||
return new UriComponents.PathComponentComposite(pathComponents);
|
return new HierarchicalUriComponents.PathComponentComposite(pathComponents);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathComponentBuilder appendPath(String path) {
|
public PathComponentBuilder appendPath(String path) {
|
||||||
@@ -576,8 +702,8 @@ public class UriComponentsBuilder {
|
|||||||
*/
|
*/
|
||||||
private static PathComponentBuilder NULL_PATH_COMPONENT_BUILDER = new PathComponentBuilder() {
|
private static PathComponentBuilder NULL_PATH_COMPONENT_BUILDER = new PathComponentBuilder() {
|
||||||
|
|
||||||
public UriComponents.PathComponent build() {
|
public HierarchicalUriComponents.PathComponent build() {
|
||||||
return UriComponents.NULL_PATH_COMPONENT;
|
return HierarchicalUriComponents.NULL_PATH_COMPONENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathComponentBuilder appendPath(String path) {
|
public PathComponentBuilder appendPath(String path) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -29,9 +29,9 @@ import org.springframework.util.Assert;
|
|||||||
*
|
*
|
||||||
* <p>All {@code encode*(String, String} methods in this class operate in a similar way:
|
* <p>All {@code encode*(String, String} methods in this class operate in a similar way:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Valid characters for the specific URI component as defined in RFC 3986 stay the same.</li>
|
* <li>Valid characters for the specific URI component as defined in RFC 3986 stay the same.</li>
|
||||||
* <li>All other characters are converted into one or more bytes in the given encoding scheme. Each of the
|
* <li>All other characters are converted into one or more bytes in the given encoding scheme. Each of the
|
||||||
* resulting bytes is written as a hexadecimal string in the "<code>%<i>xy</i></code>" format.</li>
|
* resulting bytes is written as a hexadecimal string in the "<code>%<i>xy</i></code>" format.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
@@ -64,6 +64,7 @@ public abstract class UriUtils {
|
|||||||
private static final Pattern HTTP_URL_PATTERN = Pattern.compile(
|
private static final Pattern HTTP_URL_PATTERN = Pattern.compile(
|
||||||
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
|
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
|
||||||
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
|
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
|
||||||
|
|
||||||
// encoding
|
// encoding
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,8 +74,8 @@ public abstract class UriUtils {
|
|||||||
* characters in query parameter names and query parameter values because they cannot
|
* characters in query parameter names and query parameter values because they cannot
|
||||||
* be parsed in a reliable way. Instead use:
|
* be parsed in a reliable way. Instead use:
|
||||||
* <pre>
|
* <pre>
|
||||||
* UriComponents uriComponents = UriComponentsBuilder.fromUri("/path?name={value}").buildAndExpand("a=b");
|
* UriComponents uriComponents = UriComponentsBuilder.fromUri("/path?name={value}").buildAndExpand("a=b");
|
||||||
* String encodedUri = uriComponents.encode().toUriString();
|
* String encodedUri = uriComponents.encode().toUriString();
|
||||||
* </pre>
|
* </pre>
|
||||||
* @param uri the URI to be encoded
|
* @param uri the URI to be encoded
|
||||||
* @param encoding the character encoding to encode to
|
* @param encoding the character encoding to encode to
|
||||||
@@ -83,6 +84,7 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
|
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException {
|
public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException {
|
||||||
Assert.notNull(uri, "'uri' must not be null");
|
Assert.notNull(uri, "'uri' must not be null");
|
||||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||||
@@ -113,8 +115,8 @@ public abstract class UriUtils {
|
|||||||
* characters in query parameter names and query parameter values because they cannot
|
* characters in query parameter names and query parameter values because they cannot
|
||||||
* be parsed in a reliable way. Instead use:
|
* be parsed in a reliable way. Instead use:
|
||||||
* <pre>
|
* <pre>
|
||||||
* UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("/path?name={value}").buildAndExpand("a=b");
|
* UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("/path?name={value}").buildAndExpand("a=b");
|
||||||
* String encodedUri = uriComponents.encode().toUriString();
|
* String encodedUri = uriComponents.encode().toUriString();
|
||||||
* </pre>
|
* </pre>
|
||||||
* @param httpUrl the HTTP URL to be encoded
|
* @param httpUrl the HTTP URL to be encoded
|
||||||
* @param encoding the character encoding to encode to
|
* @param encoding the character encoding to encode to
|
||||||
@@ -123,6 +125,7 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
|
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException {
|
public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException {
|
||||||
Assert.notNull(httpUrl, "'httpUrl' must not be null");
|
Assert.notNull(httpUrl, "'httpUrl' must not be null");
|
||||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||||
@@ -160,46 +163,47 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
* @deprecated in favor of {@link UriComponentsBuilder}
|
* @deprecated in favor of {@link UriComponentsBuilder}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static String encodeUriComponents(String scheme, String authority, String userInfo,
|
public static String encodeUriComponents(String scheme, String authority, String userInfo,
|
||||||
String host, String port, String path, String query, String fragment, String encoding)
|
String host, String port, String path, String query, String fragment, String encoding)
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
|
|
||||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
if (scheme != null) {
|
if (scheme != null) {
|
||||||
sb.append(encodeScheme(scheme, encoding));
|
sb.append(encodeScheme(scheme, encoding));
|
||||||
sb.append(':');
|
sb.append(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authority != null) {
|
if (authority != null) {
|
||||||
sb.append("//");
|
sb.append("//");
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
sb.append(encodeUserInfo(userInfo, encoding));
|
sb.append(encodeUserInfo(userInfo, encoding));
|
||||||
sb.append('@');
|
sb.append('@');
|
||||||
}
|
}
|
||||||
if (host != null) {
|
if (host != null) {
|
||||||
sb.append(encodeHost(host, encoding));
|
sb.append(encodeHost(host, encoding));
|
||||||
}
|
}
|
||||||
if (port != null) {
|
if (port != null) {
|
||||||
sb.append(':');
|
sb.append(':');
|
||||||
sb.append(encodePort(port, encoding));
|
sb.append(encodePort(port, encoding));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(encodePath(path, encoding));
|
sb.append(encodePath(path, encoding));
|
||||||
|
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
sb.append('?');
|
sb.append('?');
|
||||||
sb.append(encodeQuery(query, encoding));
|
sb.append(encodeQuery(query, encoding));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fragment != null) {
|
if (fragment != null) {
|
||||||
sb.append('#');
|
sb.append('#');
|
||||||
sb.append(encodeFragment(fragment, encoding));
|
sb.append(encodeFragment(fragment, encoding));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -213,7 +217,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeScheme(String scheme, String encoding) throws UnsupportedEncodingException {
|
public static String encodeScheme(String scheme, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(scheme, encoding, UriComponents.Type.SCHEME);
|
return HierarchicalUriComponents.encodeUriComponent(scheme, encoding,
|
||||||
|
HierarchicalUriComponents.Type.SCHEME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,7 +229,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeAuthority(String authority, String encoding) throws UnsupportedEncodingException {
|
public static String encodeAuthority(String authority, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(authority, encoding, UriComponents.Type.AUTHORITY);
|
return HierarchicalUriComponents.encodeUriComponent(authority, encoding,
|
||||||
|
HierarchicalUriComponents.Type.AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -235,7 +241,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeUserInfo(String userInfo, String encoding) throws UnsupportedEncodingException {
|
public static String encodeUserInfo(String userInfo, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(userInfo, encoding, UriComponents.Type.USER_INFO);
|
return HierarchicalUriComponents.encodeUriComponent(userInfo, encoding,
|
||||||
|
HierarchicalUriComponents.Type.USER_INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,7 +253,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeHost(String host, String encoding) throws UnsupportedEncodingException {
|
public static String encodeHost(String host, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(host, encoding, UriComponents.Type.HOST);
|
return HierarchicalUriComponents
|
||||||
|
.encodeUriComponent(host, encoding, HierarchicalUriComponents.Type.HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,7 +265,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodePort(String port, String encoding) throws UnsupportedEncodingException {
|
public static String encodePort(String port, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(port, encoding, UriComponents.Type.PORT);
|
return HierarchicalUriComponents
|
||||||
|
.encodeUriComponent(port, encoding, HierarchicalUriComponents.Type.PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,7 +277,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodePath(String path, String encoding) throws UnsupportedEncodingException {
|
public static String encodePath(String path, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(path, encoding, UriComponents.Type.PATH);
|
return HierarchicalUriComponents
|
||||||
|
.encodeUriComponent(path, encoding, HierarchicalUriComponents.Type.PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,7 +289,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodePathSegment(String segment, String encoding) throws UnsupportedEncodingException {
|
public static String encodePathSegment(String segment, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(segment, encoding, UriComponents.Type.PATH_SEGMENT);
|
return HierarchicalUriComponents.encodeUriComponent(segment, encoding,
|
||||||
|
HierarchicalUriComponents.Type.PATH_SEGMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +301,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeQuery(String query, String encoding) throws UnsupportedEncodingException {
|
public static String encodeQuery(String query, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(query, encoding, UriComponents.Type.QUERY);
|
return HierarchicalUriComponents
|
||||||
|
.encodeUriComponent(query, encoding, HierarchicalUriComponents.Type.QUERY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -301,7 +313,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeQueryParam(String queryParam, String encoding) throws UnsupportedEncodingException {
|
public static String encodeQueryParam(String queryParam, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(queryParam, encoding, UriComponents.Type.QUERY_PARAM);
|
return HierarchicalUriComponents.encodeUriComponent(queryParam, encoding,
|
||||||
|
HierarchicalUriComponents.Type.QUERY_PARAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,7 +325,8 @@ public abstract class UriUtils {
|
|||||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||||
*/
|
*/
|
||||||
public static String encodeFragment(String fragment, String encoding) throws UnsupportedEncodingException {
|
public static String encodeFragment(String fragment, String encoding) throws UnsupportedEncodingException {
|
||||||
return UriComponents.encodeUriComponent(fragment, encoding, UriComponents.Type.FRAGMENT);
|
return HierarchicalUriComponents.encodeUriComponent(fragment, encoding,
|
||||||
|
HierarchicalUriComponents.Type.FRAGMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -321,11 +335,11 @@ public abstract class UriUtils {
|
|||||||
/**
|
/**
|
||||||
* Decodes the given encoded source String into an URI. Based on the following rules:
|
* Decodes the given encoded source String into an URI. Based on the following rules:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Alphanumeric characters {@code "a"} through {@code "z"}, {@code "A"} through {@code "Z"}, and
|
* <li>Alphanumeric characters {@code "a"} through {@code "z"}, {@code "A"} through {@code "Z"}, and
|
||||||
* {@code "0"} through {@code "9"} stay the same.</li>
|
* {@code "0"} through {@code "9"} stay the same.</li>
|
||||||
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
|
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
|
||||||
* <li>A sequence "<code>%<i>xy</i></code>" is interpreted as a hexadecimal representation of the character.</li>
|
* <li>A sequence "<code>%<i>xy</i></code>" is interpreted as a hexadecimal representation of the character.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* @param source the source string
|
* @param source the source string
|
||||||
* @param encoding the encoding
|
* @param encoding the encoding
|
||||||
* @return the decoded URI
|
* @return the decoded URI
|
||||||
|
|||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.web.util;
|
package org.springframework.web.util;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -26,21 +23,26 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
/** @author Arjen Poutsma */
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
*/
|
||||||
public class UriComponentsBuilderTests {
|
public class UriComponentsBuilderTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void plain() throws URISyntaxException {
|
public void plain() throws URISyntaxException {
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||||
UriComponents result = builder.scheme("http").host("example.com").path("foo").queryParam("bar").fragment("baz").build();
|
UriComponents result = builder.scheme("http").host("example.com").path("foo").queryParam("bar").fragment("baz").build();
|
||||||
assertEquals("http", result.getScheme());
|
assertEquals("http", result.getScheme());
|
||||||
assertEquals("example.com", result.getHost());
|
assertEquals("example.com", result.getHost());
|
||||||
assertEquals("foo", result.getPath());
|
assertEquals("foo", result.getPath());
|
||||||
assertEquals("bar", result.getQuery());
|
assertEquals("bar", result.getQuery());
|
||||||
assertEquals("baz", result.getFragment());
|
assertEquals("baz", result.getFragment());
|
||||||
|
|
||||||
URI expected = new URI("http://example.com/foo?bar#baz");
|
URI expected = new URI("http://example.com/foo?bar#baz");
|
||||||
assertEquals("Invalid result URI", expected, result.toUri());
|
assertEquals("Invalid result URI", expected, result.toUri());
|
||||||
@@ -49,43 +51,54 @@ public class UriComponentsBuilderTests {
|
|||||||
@Test
|
@Test
|
||||||
public void fromPath() throws URISyntaxException {
|
public void fromPath() throws URISyntaxException {
|
||||||
UriComponents result = UriComponentsBuilder.fromPath("foo").queryParam("bar").fragment("baz").build();
|
UriComponents result = UriComponentsBuilder.fromPath("foo").queryParam("bar").fragment("baz").build();
|
||||||
assertEquals("foo", result.getPath());
|
assertEquals("foo", result.getPath());
|
||||||
assertEquals("bar", result.getQuery());
|
assertEquals("bar", result.getQuery());
|
||||||
assertEquals("baz", result.getFragment());
|
assertEquals("baz", result.getFragment());
|
||||||
|
|
||||||
URI expected = new URI("/foo?bar#baz");
|
URI expected = new URI("/foo?bar#baz");
|
||||||
assertEquals("Invalid result URI", expected, result.toUri());
|
assertEquals("Invalid result URI", expected, result.toUri());
|
||||||
|
|
||||||
result = UriComponentsBuilder.fromPath("/foo").build();
|
result = UriComponentsBuilder.fromPath("/foo").build();
|
||||||
assertEquals("/foo", result.getPath());
|
assertEquals("/foo", result.getPath());
|
||||||
|
|
||||||
expected = new URI("/foo");
|
expected = new URI("/foo");
|
||||||
assertEquals("Invalid result URI", expected, result.toUri());
|
assertEquals("Invalid result URI", expected, result.toUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fromUri() throws URISyntaxException {
|
public void fromHierarchicalUri() throws URISyntaxException {
|
||||||
URI uri = new URI("http://example.com/foo?bar#baz");
|
URI uri = new URI("http://example.com/foo?bar#baz");
|
||||||
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
||||||
assertEquals("http", result.getScheme());
|
assertEquals("http", result.getScheme());
|
||||||
assertEquals("example.com", result.getHost());
|
assertEquals("example.com", result.getHost());
|
||||||
assertEquals("/foo", result.getPath());
|
assertEquals("/foo", result.getPath());
|
||||||
assertEquals("bar", result.getQuery());
|
assertEquals("bar", result.getQuery());
|
||||||
assertEquals("baz", result.getFragment());
|
assertEquals("baz", result.getFragment());
|
||||||
|
|
||||||
|
assertEquals("Invalid result URI", uri, result.toUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromOpaqueUri() throws URISyntaxException {
|
||||||
|
URI uri = new URI("mailto:foo@bar.com#baz");
|
||||||
|
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
||||||
|
assertEquals("mailto", result.getScheme());
|
||||||
|
assertEquals("foo@bar.com", result.getSchemeSpecificPart());
|
||||||
|
assertEquals("baz", result.getFragment());
|
||||||
|
|
||||||
assertEquals("Invalid result URI", uri, result.toUri());
|
assertEquals("Invalid result URI", uri, result.toUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-9317
|
// SPR-9317
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fromUriEncodedQuery() throws URISyntaxException {
|
public void fromUriEncodedQuery() throws URISyntaxException {
|
||||||
URI uri = new URI("http://www.example.org/?param=aGVsbG9Xb3JsZA%3D%3D");
|
URI uri = new URI("http://www.example.org/?param=aGVsbG9Xb3JsZA%3D%3D");
|
||||||
String fromUri = UriComponentsBuilder.fromUri(uri).build().getQueryParams().get("param").get(0);
|
String fromUri = UriComponentsBuilder.fromUri(uri).build().getQueryParams().get("param").get(0);
|
||||||
String fromUriString = UriComponentsBuilder.fromUriString(uri.toString()).build().getQueryParams().get("param").get(0);
|
String fromUriString = UriComponentsBuilder.fromUriString(uri.toString()).build().getQueryParams().get("param").get(0);
|
||||||
|
|
||||||
assertEquals(fromUri, fromUriString);
|
assertEquals(fromUri, fromUriString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fromUriString() {
|
public void fromUriString() {
|
||||||
@@ -102,34 +115,35 @@ public class UriComponentsBuilderTests {
|
|||||||
result = UriComponentsBuilder.fromUriString(
|
result = UriComponentsBuilder.fromUriString(
|
||||||
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar#and(java.util.BitSet)")
|
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar#and(java.util.BitSet)")
|
||||||
.build();
|
.build();
|
||||||
assertEquals("http", result.getScheme());
|
assertEquals("http", result.getScheme());
|
||||||
assertEquals("arjen:foobar", result.getUserInfo());
|
assertEquals("arjen:foobar", result.getUserInfo());
|
||||||
assertEquals("java.sun.com", result.getHost());
|
assertEquals("java.sun.com", result.getHost());
|
||||||
assertEquals(80, result.getPort());
|
assertEquals(80, result.getPort());
|
||||||
assertEquals("/javase/6/docs/api/java/util/BitSet.html", result.getPath());
|
assertEquals("/javase/6/docs/api/java/util/BitSet.html", result.getPath());
|
||||||
assertEquals("foo=bar", result.getQuery());
|
assertEquals("foo=bar", result.getQuery());
|
||||||
MultiValueMap<String, String> expectedQueryParams = new LinkedMultiValueMap<String, String>(1);
|
MultiValueMap<String, String> expectedQueryParams = new LinkedMultiValueMap<String, String>(1);
|
||||||
expectedQueryParams.add("foo", "bar");
|
expectedQueryParams.add("foo", "bar");
|
||||||
assertEquals(expectedQueryParams, result.getQueryParams());
|
assertEquals(expectedQueryParams, result.getQueryParams());
|
||||||
assertEquals("and(java.util.BitSet)", result.getFragment());
|
assertEquals("and(java.util.BitSet)", result.getFragment());
|
||||||
|
|
||||||
result = UriComponentsBuilder.fromUriString("mailto:java-net@java.sun.com").build();
|
result = UriComponentsBuilder.fromUriString("mailto:java-net@java.sun.com#baz").build();
|
||||||
assertEquals("mailto", result.getScheme());
|
assertEquals("mailto", result.getScheme());
|
||||||
assertNull(result.getUserInfo());
|
assertNull(result.getUserInfo());
|
||||||
assertNull(result.getHost());
|
assertNull(result.getHost());
|
||||||
assertEquals(-1, result.getPort());
|
assertEquals(-1, result.getPort());
|
||||||
assertEquals("java-net@java.sun.com", result.getPathSegments().get(0));
|
assertEquals("java-net@java.sun.com", result.getSchemeSpecificPart());
|
||||||
assertNull(result.getQuery());
|
assertNull(result.getPath());
|
||||||
assertNull(result.getFragment());
|
assertNull(result.getQuery());
|
||||||
|
assertEquals("baz", result.getFragment());
|
||||||
|
|
||||||
result = UriComponentsBuilder.fromUriString("docs/guide/collections/designfaq.html#28").build();
|
result = UriComponentsBuilder.fromUriString("docs/guide/collections/designfaq.html#28").build();
|
||||||
assertNull(result.getScheme());
|
assertNull(result.getScheme());
|
||||||
assertNull(result.getUserInfo());
|
assertNull(result.getUserInfo());
|
||||||
assertNull(result.getHost());
|
assertNull(result.getHost());
|
||||||
assertEquals(-1, result.getPort());
|
assertEquals(-1, result.getPort());
|
||||||
assertEquals("docs/guide/collections/designfaq.html", result.getPath());
|
assertEquals("docs/guide/collections/designfaq.html", result.getPath());
|
||||||
assertNull(result.getQuery());
|
assertNull(result.getQuery());
|
||||||
assertEquals("28", result.getFragment());
|
assertEquals("28", result.getFragment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -265,7 +279,7 @@ public class UriComponentsBuilderTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildAndExpand() {
|
public void buildAndExpandHierarchical() {
|
||||||
UriComponents result = UriComponentsBuilder.fromPath("/{foo}").buildAndExpand("fooValue");
|
UriComponents result = UriComponentsBuilder.fromPath("/{foo}").buildAndExpand("fooValue");
|
||||||
assertEquals("/fooValue", result.toUriString());
|
assertEquals("/fooValue", result.toUriString());
|
||||||
|
|
||||||
@@ -275,4 +289,17 @@ public class UriComponentsBuilderTests {
|
|||||||
result = UriComponentsBuilder.fromPath("/{foo}/{bar}").buildAndExpand(values);
|
result = UriComponentsBuilder.fromPath("/{foo}/{bar}").buildAndExpand(values);
|
||||||
assertEquals("/fooValue/barValue", result.toUriString());
|
assertEquals("/fooValue/barValue", result.toUriString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAndExpandOpaque() {
|
||||||
|
UriComponents result = UriComponentsBuilder.fromUriString("mailto:{user}@{domain}").buildAndExpand("foo", "example.com");
|
||||||
|
assertEquals("mailto:foo@example.com", result.toUriString());
|
||||||
|
|
||||||
|
Map<String, String> values = new HashMap<String, String>();
|
||||||
|
values.put("user", "foo");
|
||||||
|
values.put("domain", "example.com");
|
||||||
|
UriComponentsBuilder.fromUriString("mailto:{user}@{domain}").buildAndExpand(values);
|
||||||
|
assertEquals("mailto:foo@example.com", result.toUriString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user