SPR-5973: UriComponents now encapsulates a PathCompont, switching between string paths and path segment lists automatically.
This commit is contained in:
parent
782c2a4657
commit
3f2ea7f50e
|
|
@ -16,19 +16,20 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.support;
|
package org.springframework.web.servlet.support;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockServletContext;
|
import org.springframework.mock.web.MockServletContext;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
|
|
@ -56,7 +57,6 @@ public class RequestContextTests {
|
||||||
assertEquals("foo/bar", context.getContextUrl("bar"));
|
assertEquals("foo/bar", context.getContextUrl("bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.Ignore // TODO: Arjen to address in SPR-5973
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContextUrlStringMap() throws Exception {
|
public void testGetContextUrlStringMap() throws Exception {
|
||||||
request.setContextPath("foo/");
|
request.setContextPath("foo/");
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ public final class UriComponents {
|
||||||
|
|
||||||
private final int port;
|
private final int port;
|
||||||
|
|
||||||
private final List<String> pathSegments;
|
private final PathComponent path;
|
||||||
|
|
||||||
private final MultiValueMap<String, String> queryParams;
|
private final MultiValueMap<String, String> queryParams;
|
||||||
|
|
||||||
|
|
@ -73,11 +73,11 @@ public final class UriComponents {
|
||||||
|
|
||||||
private final boolean encoded;
|
private final boolean encoded;
|
||||||
|
|
||||||
public UriComponents(String scheme,
|
UriComponents(String scheme,
|
||||||
String userInfo,
|
String userInfo,
|
||||||
String host,
|
String host,
|
||||||
int port,
|
int port,
|
||||||
List<String> pathSegments,
|
PathComponent path,
|
||||||
MultiValueMap<String, String> queryParams,
|
MultiValueMap<String, String> queryParams,
|
||||||
String fragment,
|
String fragment,
|
||||||
boolean encoded) {
|
boolean encoded) {
|
||||||
|
|
@ -85,10 +85,7 @@ public final class UriComponents {
|
||||||
this.userInfo = userInfo;
|
this.userInfo = userInfo;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
if (pathSegments == null) {
|
this.path = path != null ? path : NULL_PATH_COMPONENT;
|
||||||
pathSegments = Collections.emptyList();
|
|
||||||
}
|
|
||||||
this.pathSegments = Collections.unmodifiableList(pathSegments);
|
|
||||||
if (queryParams == null) {
|
if (queryParams == null) {
|
||||||
queryParams = new LinkedMultiValueMap<String, String>(0);
|
queryParams = new LinkedMultiValueMap<String, String>(0);
|
||||||
}
|
}
|
||||||
|
|
@ -141,28 +138,7 @@ public final class UriComponents {
|
||||||
* @return the path. Can be {@code null}.
|
* @return the path. Can be {@code null}.
|
||||||
*/
|
*/
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
if (!pathSegments.isEmpty()) {
|
return path.getPath();
|
||||||
StringBuilder pathBuilder = new StringBuilder();
|
|
||||||
for (String pathSegment : pathSegments) {
|
|
||||||
if (StringUtils.hasLength(pathSegment)) {
|
|
||||||
boolean startsWithSlash = pathSegment.charAt(0) == PATH_DELIMITER;
|
|
||||||
boolean endsWithSlash =
|
|
||||||
pathBuilder.length() > 0 && pathBuilder.charAt(pathBuilder.length() - 1) == PATH_DELIMITER;
|
|
||||||
|
|
||||||
if (!endsWithSlash && !startsWithSlash) {
|
|
||||||
pathBuilder.append('/');
|
|
||||||
}
|
|
||||||
else if (endsWithSlash && startsWithSlash) {
|
|
||||||
pathSegment = pathSegment.substring(1);
|
|
||||||
}
|
|
||||||
pathBuilder.append(pathSegment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pathBuilder.toString();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -171,7 +147,7 @@ public final class UriComponents {
|
||||||
* @return the path segments. Empty if no path has been set.
|
* @return the path segments. Empty if no path has been set.
|
||||||
*/
|
*/
|
||||||
public List<String> getPathSegments() {
|
public List<String> getPathSegments() {
|
||||||
return pathSegments;
|
return path.getPathSegments();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -265,11 +241,7 @@ public final class UriComponents {
|
||||||
String encodedScheme = encodeUriComponent(this.scheme, encoding, Type.SCHEME);
|
String encodedScheme = encodeUriComponent(this.scheme, encoding, Type.SCHEME);
|
||||||
String encodedUserInfo = encodeUriComponent(this.userInfo, encoding, Type.USER_INFO);
|
String encodedUserInfo = encodeUriComponent(this.userInfo, encoding, Type.USER_INFO);
|
||||||
String encodedHost = encodeUriComponent(this.host, encoding, Type.HOST);
|
String encodedHost = encodeUriComponent(this.host, encoding, Type.HOST);
|
||||||
List<String> encodedPathSegments = new ArrayList<String>(this.pathSegments.size());
|
PathComponent encodedPath = path.encode(encoding);
|
||||||
for (String pathSegment : this.pathSegments) {
|
|
||||||
String encodedPathSegment = encodeUriComponent(pathSegment, encoding, Type.PATH_SEGMENT);
|
|
||||||
encodedPathSegments.add(encodedPathSegment);
|
|
||||||
}
|
|
||||||
MultiValueMap<String, String> encodedQueryParams =
|
MultiValueMap<String, String> encodedQueryParams =
|
||||||
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
||||||
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
|
@ -283,7 +255,7 @@ public final class UriComponents {
|
||||||
}
|
}
|
||||||
String encodedFragment = encodeUriComponent(this.fragment, encoding, Type.FRAGMENT);
|
String encodedFragment = encodeUriComponent(this.fragment, encoding, Type.FRAGMENT);
|
||||||
|
|
||||||
return new UriComponents(encodedScheme, encodedUserInfo, encodedHost, this.port, encodedPathSegments,
|
return new UriComponents(encodedScheme, encodedUserInfo, encodedHost, this.port, encodedPath,
|
||||||
encodedQueryParams, encodedFragment, true);
|
encodedQueryParams, encodedFragment, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,11 +322,7 @@ public final class UriComponents {
|
||||||
String expandedScheme = expandUriComponent(this.scheme, uriVariables);
|
String expandedScheme = expandUriComponent(this.scheme, uriVariables);
|
||||||
String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables);
|
String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables);
|
||||||
String expandedHost = expandUriComponent(this.host, uriVariables);
|
String expandedHost = expandUriComponent(this.host, uriVariables);
|
||||||
List<String> expandedPathSegments = new ArrayList<String>(this.pathSegments.size());
|
PathComponent expandedPath = path.expand(uriVariables);
|
||||||
for (String pathSegment : this.pathSegments) {
|
|
||||||
String expandedPathSegment = expandUriComponent(pathSegment, uriVariables);
|
|
||||||
expandedPathSegments.add(expandedPathSegment);
|
|
||||||
}
|
|
||||||
MultiValueMap<String, String> expandedQueryParams =
|
MultiValueMap<String, String> expandedQueryParams =
|
||||||
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
||||||
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
|
@ -368,11 +336,11 @@ public final class UriComponents {
|
||||||
}
|
}
|
||||||
String expandedFragment = expandUriComponent(this.fragment, uriVariables);
|
String expandedFragment = expandUriComponent(this.fragment, uriVariables);
|
||||||
|
|
||||||
return new UriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPathSegments,
|
return new UriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPath,
|
||||||
expandedQueryParams, expandedFragment, false);
|
expandedQueryParams, expandedFragment, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String expandUriComponent(String source, Map<String, ?> uriVariables) {
|
private static String expandUriComponent(String source, Map<String, ?> uriVariables) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -408,11 +376,7 @@ public final class UriComponents {
|
||||||
String expandedScheme = expandUriComponent(this.scheme, valueIterator);
|
String expandedScheme = expandUriComponent(this.scheme, valueIterator);
|
||||||
String expandedUserInfo = expandUriComponent(this.userInfo, valueIterator);
|
String expandedUserInfo = expandUriComponent(this.userInfo, valueIterator);
|
||||||
String expandedHost = expandUriComponent(this.host, valueIterator);
|
String expandedHost = expandUriComponent(this.host, valueIterator);
|
||||||
List<String> expandedPathSegments = new ArrayList<String>(this.pathSegments.size());
|
PathComponent expandedPath = path.expand(valueIterator);
|
||||||
for (String pathSegment : this.pathSegments) {
|
|
||||||
String expandedPathSegment = expandUriComponent(pathSegment, valueIterator);
|
|
||||||
expandedPathSegments.add(expandedPathSegment);
|
|
||||||
}
|
|
||||||
MultiValueMap<String, String> expandedQueryParams =
|
MultiValueMap<String, String> expandedQueryParams =
|
||||||
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
new LinkedMultiValueMap<String, String>(this.queryParams.size());
|
||||||
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
for (Map.Entry<String, List<String>> entry : this.queryParams.entrySet()) {
|
||||||
|
|
@ -426,11 +390,11 @@ public final class UriComponents {
|
||||||
}
|
}
|
||||||
String expandedFragment = expandUriComponent(this.fragment, valueIterator);
|
String expandedFragment = expandUriComponent(this.fragment, valueIterator);
|
||||||
|
|
||||||
return new UriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPathSegments,
|
return new UriComponents(expandedScheme, expandedUserInfo, expandedHost, this.port, expandedPath,
|
||||||
expandedQueryParams, expandedFragment, false);
|
expandedQueryParams, expandedFragment, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String expandUriComponent(String source, Iterator<Object> valueIterator) {
|
private static String expandUriComponent(String source, Iterator<Object> valueIterator) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -453,12 +417,12 @@ public final class UriComponents {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getVariableName(String match) {
|
private static String getVariableName(String match) {
|
||||||
int colonIdx = match.indexOf(':');
|
int colonIdx = match.indexOf(':');
|
||||||
return colonIdx == -1 ? match : match.substring(0, colonIdx);
|
return colonIdx == -1 ? match : match.substring(0, colonIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getVariableValueAsString(Object variableValue) {
|
private static String getVariableValueAsString(Object variableValue) {
|
||||||
return variableValue != null ? variableValue.toString() : "";
|
return variableValue != null ? variableValue.toString() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +461,10 @@ public final class UriComponents {
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = getPath();
|
String path = getPath();
|
||||||
if (path != null) {
|
if (StringUtils.hasLength(path)) {
|
||||||
|
if (uriBuilder.length() != 0 && path.charAt(0) != PATH_DELIMITER) {
|
||||||
|
uriBuilder.append(PATH_DELIMITER);
|
||||||
|
}
|
||||||
uriBuilder.append(path);
|
uriBuilder.append(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,7 +493,11 @@ public final class UriComponents {
|
||||||
return new URI(toUriString());
|
return new URI(toUriString());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new URI(getScheme(), getUserInfo(), getHost(), getPort(), getPath(), getQuery(),
|
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());
|
getFragment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +526,7 @@ public final class UriComponents {
|
||||||
if (port != other.port) {
|
if (port != other.port) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!pathSegments.equals(other.pathSegments)) {
|
if (!path.equals(other.path)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!queryParams.equals(other.queryParams)) {
|
if (!queryParams.equals(other.queryParams)) {
|
||||||
|
|
@ -577,7 +548,7 @@ public final class UriComponents {
|
||||||
result = 31 * result + (userInfo != null ? userInfo.hashCode() : 0);
|
result = 31 * result + (userInfo != null ? userInfo.hashCode() : 0);
|
||||||
result = 31 * result + (host != null ? host.hashCode() : 0);
|
result = 31 * result + (host != null ? host.hashCode() : 0);
|
||||||
result = 31 * result + port;
|
result = 31 * result + port;
|
||||||
result = 31 * result + pathSegments.hashCode();
|
result = 31 * result + path.hashCode();
|
||||||
result = 31 * result + queryParams.hashCode();
|
result = 31 * result + queryParams.hashCode();
|
||||||
result = 31 * result + (fragment != null ? fragment.hashCode() : 0);
|
result = 31 * result + (fragment != null ? fragment.hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -740,4 +711,186 @@ public final class UriComponents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the contract for path (segments).
|
||||||
|
*/
|
||||||
|
interface PathComponent {
|
||||||
|
|
||||||
|
String getPath();
|
||||||
|
|
||||||
|
List<String> getPathSegments();
|
||||||
|
|
||||||
|
PathComponent encode(String encoding) throws UnsupportedEncodingException;
|
||||||
|
|
||||||
|
PathComponent expand(Map<String, ?> uriVariables);
|
||||||
|
|
||||||
|
PathComponent expand(Iterator<Object> valueIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 PathComponent expand(Map<String, ?> uriVariables) {
|
||||||
|
String expandedPath = expandUriComponent(getPath(), uriVariables);
|
||||||
|
return new FullPathComponent(expandedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(Iterator<Object> valueIterator) {
|
||||||
|
String expandedPath = expandUriComponent(getPath(), valueIterator);
|
||||||
|
return new FullPathComponent(expandedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
} else if (o instanceof FullPathComponent) {
|
||||||
|
FullPathComponent other = (FullPathComponent) o;
|
||||||
|
return this.getPath().equals(other.getPath());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 = 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 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 PathComponent expand(Map<String, ?> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(Iterator<Object> valueIterator) {
|
||||||
|
List<String> pathSegments = getPathSegments();
|
||||||
|
List<String> expandedPathSegments = new ArrayList<String>(pathSegments.size());
|
||||||
|
for (String pathSegment : pathSegments) {
|
||||||
|
String expandedPathSegment = expandUriComponent(pathSegment, valueIterator);
|
||||||
|
expandedPathSegments.add(expandedPathSegment);
|
||||||
|
}
|
||||||
|
return new PathSegmentComponent(expandedPathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
} else if (o instanceof PathSegmentComponent) {
|
||||||
|
PathSegmentComponent other = (PathSegmentComponent) o;
|
||||||
|
return this.getPathSegments().equals(other.getPathSegments());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getPathSegments().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 PathComponent expand(Map<String, ?> uriVariables) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponent expand(Iterator<Object> valueIterator) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class UriComponentsBuilder {
|
public class UriComponentsBuilder {
|
||||||
|
|
||||||
private static final char PATH_DELIMITER = '/';
|
|
||||||
|
|
||||||
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)=?([^&=]+)?");
|
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)=?([^&=]+)?");
|
||||||
|
|
||||||
private static final String SCHEME_PATTERN = "([^:/?#]+):";
|
private static final String SCHEME_PATTERN = "([^:/?#]+):";
|
||||||
|
|
@ -89,7 +87,7 @@ public class UriComponentsBuilder {
|
||||||
|
|
||||||
private int port = -1;
|
private int port = -1;
|
||||||
|
|
||||||
private final List<String> pathSegments = new ArrayList<String>();
|
private PathComponentBuilder pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
||||||
|
|
||||||
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
|
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
|
||||||
|
|
||||||
|
|
@ -219,7 +217,7 @@ public class UriComponentsBuilder {
|
||||||
* @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, pathSegments, queryParams, fragment, encoded);
|
return new UriComponents(scheme, userInfo, host, port, pathBuilder.build(), queryParams, fragment, encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// URI components methods
|
// URI components methods
|
||||||
|
|
@ -246,8 +244,7 @@ public class UriComponentsBuilder {
|
||||||
this.port = uri.getPort();
|
this.port = uri.getPort();
|
||||||
}
|
}
|
||||||
if (StringUtils.hasLength(uri.getPath())) {
|
if (StringUtils.hasLength(uri.getPath())) {
|
||||||
this.pathSegments.clear();
|
this.pathBuilder = new FullPathComponentBuilder(uri.getPath());
|
||||||
path(uri.getPath());
|
|
||||||
}
|
}
|
||||||
if (StringUtils.hasLength(uri.getQuery())) {
|
if (StringUtils.hasLength(uri.getQuery())) {
|
||||||
this.queryParams.clear();
|
this.queryParams.clear();
|
||||||
|
|
@ -307,10 +304,6 @@ public class UriComponentsBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String portAsString() {
|
|
||||||
return this.port != -1 ? Integer.toString(this.port) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
|
|
@ -319,10 +312,9 @@ public class UriComponentsBuilder {
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder path(String path) {
|
public UriComponentsBuilder path(String path) {
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
String[] pathSegments = StringUtils.tokenizeToStringArray(path, "/");
|
this.pathBuilder = this.pathBuilder.appendPath(path);
|
||||||
pathSegment(pathSegments);
|
|
||||||
} else {
|
} else {
|
||||||
pathSegments.clear();
|
this.pathBuilder = NULL_PATH_COMPONENT_BUILDER;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -331,13 +323,12 @@ public class UriComponentsBuilder {
|
||||||
* Appends the given path segments to the existing path of this builder. Each given path segments may contain URI
|
* Appends the given path segments to the existing path of this builder. Each given path segments may contain URI
|
||||||
* template variables.
|
* template variables.
|
||||||
*
|
*
|
||||||
* @param segments the URI path segments
|
* @param pathSegments the URI path segments
|
||||||
* @return this UriComponentsBuilder
|
* @return this UriComponentsBuilder
|
||||||
*/
|
*/
|
||||||
public UriComponentsBuilder pathSegment(String... segments) throws IllegalArgumentException {
|
public UriComponentsBuilder pathSegment(String... pathSegments) throws IllegalArgumentException {
|
||||||
Assert.notNull(segments, "'segments' must not be null");
|
Assert.notNull(pathSegments, "'segments' must not be null");
|
||||||
Collections.addAll(this.pathSegments, segments);
|
this.pathBuilder = this.pathBuilder.appendPathSegments(pathSegments);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,4 +395,100 @@ public class UriComponentsBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a builder for {@link org.springframework.web.util.UriComponents.PathComponent}
|
||||||
|
*/
|
||||||
|
private interface PathComponentBuilder {
|
||||||
|
|
||||||
|
UriComponents.PathComponent build();
|
||||||
|
|
||||||
|
PathComponentBuilder appendPath(String path);
|
||||||
|
|
||||||
|
PathComponentBuilder appendPathSegments(String... pathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a builder for full string paths.
|
||||||
|
*/
|
||||||
|
private static class FullPathComponentBuilder implements PathComponentBuilder {
|
||||||
|
|
||||||
|
private final StringBuilder path;
|
||||||
|
|
||||||
|
private FullPathComponentBuilder(String path) {
|
||||||
|
this.path = new StringBuilder(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UriComponents.PathComponent build() {
|
||||||
|
return new UriComponents.FullPathComponent(path.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPath(String path) {
|
||||||
|
this.path.append(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPathSegments(String... pathSegments) {
|
||||||
|
for (String pathSegment : pathSegments) {
|
||||||
|
final boolean pathEndsInSlash = path.length() > 0 && path.charAt(path.length() - 1) == '/';
|
||||||
|
final boolean segmentStartsWithSlash = pathSegment.charAt(0) == '/';
|
||||||
|
|
||||||
|
if (path.length() > 0 && !pathEndsInSlash && !segmentStartsWithSlash) {
|
||||||
|
path.append('/');
|
||||||
|
} else if (pathEndsInSlash && segmentStartsWithSlash) {
|
||||||
|
pathSegment = pathSegment.substring(1);
|
||||||
|
if (pathSegment.length() == 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
path.append(pathSegment);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a builder for paths segment paths.
|
||||||
|
*/
|
||||||
|
private static class PathSegmentComponentBuilder implements PathComponentBuilder {
|
||||||
|
|
||||||
|
private final List<String> pathSegments = new ArrayList<String>();
|
||||||
|
|
||||||
|
private PathSegmentComponentBuilder(String... pathSegments) {
|
||||||
|
Collections.addAll(this.pathSegments, pathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UriComponents.PathComponent build() {
|
||||||
|
return new UriComponents.PathSegmentComponent(pathSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPath(String path) {
|
||||||
|
String[] pathSegments = StringUtils.tokenizeToStringArray(path, "/");
|
||||||
|
Collections.addAll(this.pathSegments, pathSegments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPathSegments(String... pathSegments) {
|
||||||
|
Collections.addAll(this.pathSegments, pathSegments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a builder for an empty path.
|
||||||
|
*/
|
||||||
|
private static PathComponentBuilder NULL_PATH_COMPONENT_BUILDER = new PathComponentBuilder() {
|
||||||
|
|
||||||
|
public UriComponents.PathComponent build() {
|
||||||
|
return UriComponents.NULL_PATH_COMPONENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPath(String path) {
|
||||||
|
return new FullPathComponentBuilder(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathComponentBuilder appendPathSegments(String... pathSegments) {
|
||||||
|
return new PathSegmentComponentBuilder(pathSegments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ public class UriComponentsBuilderTests {
|
||||||
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());
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ 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());
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ public class UriComponentsBuilderTests {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import static org.junit.Assert.*;
|
||||||
/** @author Arjen Poutsma */
|
/** @author Arjen Poutsma */
|
||||||
public class UriComponentsTests {
|
public class UriComponentsTests {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encode() {
|
public void encode() {
|
||||||
UriComponents uriComponents = UriComponentsBuilder.fromPath("/hotel list").build();
|
UriComponents uriComponents = UriComponentsBuilder.fromPath("/hotel list").build();
|
||||||
|
|
|
||||||
|
|
@ -111,19 +111,19 @@ public class UriUtilsTests {
|
||||||
UriUtils.encodeUri("http://www.ietf.org/rfc/rfc3986.txt", ENC));
|
UriUtils.encodeUri("http://www.ietf.org/rfc/rfc3986.txt", ENC));
|
||||||
assertEquals("Invalid encoded URI", "https://www.ietf.org/rfc/rfc3986.txt",
|
assertEquals("Invalid encoded URI", "https://www.ietf.org/rfc/rfc3986.txt",
|
||||||
UriUtils.encodeUri("https://www.ietf.org/rfc/rfc3986.txt", ENC));
|
UriUtils.encodeUri("https://www.ietf.org/rfc/rfc3986.txt", ENC));
|
||||||
assertEquals("Invalid encoded URI", "http://www.google.com?q=Z%C3%BCrich",
|
assertEquals("Invalid encoded URI", "http://www.google.com/?q=Z%C3%BCrich",
|
||||||
UriUtils.encodeUri("http://www.google.com?q=Z\u00fcrich", ENC));
|
UriUtils.encodeUri("http://www.google.com/?q=Z\u00fcrich", ENC));
|
||||||
assertEquals("Invalid encoded URI",
|
assertEquals("Invalid encoded URI",
|
||||||
"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)",
|
||||||
UriUtils.encodeUri(
|
UriUtils.encodeUri(
|
||||||
"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)",
|
||||||
ENC));
|
ENC));
|
||||||
assertEquals("Invalid encoded URI", "http://java.sun.com/j2se/1.3",
|
assertEquals("Invalid encoded URI", "http://java.sun.com/j2se/1.3/",
|
||||||
UriUtils.encodeUri("http://java.sun.com/j2se/1.3", ENC));
|
UriUtils.encodeUri("http://java.sun.com/j2se/1.3/", ENC));
|
||||||
assertEquals("Invalid encoded URI", "/docs/guide/collections/designfaq.html#28",
|
assertEquals("Invalid encoded URI", "docs/guide/collections/designfaq.html#28",
|
||||||
UriUtils.encodeUri("/docs/guide/collections/designfaq.html#28", ENC));
|
UriUtils.encodeUri("docs/guide/collections/designfaq.html#28", ENC));
|
||||||
assertEquals("Invalid encoded URI", "/../../../demo/jfc/SwingSet2/src/SwingSet2.java",
|
assertEquals("Invalid encoded URI", "../../../demo/jfc/SwingSet2/src/SwingSet2.java",
|
||||||
UriUtils.encodeUri("/../../../demo/jfc/SwingSet2/src/SwingSet2.java", ENC));
|
UriUtils.encodeUri("../../../demo/jfc/SwingSet2/src/SwingSet2.java", ENC));
|
||||||
assertEquals("Invalid encoded URI", "file:///~/calendar", UriUtils.encodeUri("file:///~/calendar", ENC));
|
assertEquals("Invalid encoded URI", "file:///~/calendar", UriUtils.encodeUri("file:///~/calendar", ENC));
|
||||||
assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar",
|
assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar",
|
||||||
UriUtils.encodeUri("http://example.com/query=foo@bar", ENC));
|
UriUtils.encodeUri("http://example.com/query=foo@bar", ENC));
|
||||||
|
|
@ -136,8 +136,8 @@ public class UriUtilsTests {
|
||||||
UriUtils.encodeHttpUrl("http://www.ietf.org/rfc/rfc3986.txt", ENC));
|
UriUtils.encodeHttpUrl("http://www.ietf.org/rfc/rfc3986.txt", ENC));
|
||||||
assertEquals("Invalid encoded URI", "https://www.ietf.org/rfc/rfc3986.txt",
|
assertEquals("Invalid encoded URI", "https://www.ietf.org/rfc/rfc3986.txt",
|
||||||
UriUtils.encodeHttpUrl("https://www.ietf.org/rfc/rfc3986.txt", ENC));
|
UriUtils.encodeHttpUrl("https://www.ietf.org/rfc/rfc3986.txt", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL", "http://www.google.com?q=Z%C3%BCrich",
|
assertEquals("Invalid encoded HTTP URL", "http://www.google.com/?q=Z%C3%BCrich",
|
||||||
UriUtils.encodeHttpUrl("http://www.google.com?q=Z\u00fcrich", ENC));
|
UriUtils.encodeHttpUrl("http://www.google.com/?q=Z\u00fcrich", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL", "http://ws.geonames.org/searchJSON?q=T%C5%8Dky%C5%8D&style=FULL&maxRows=300",
|
assertEquals("Invalid encoded HTTP URL", "http://ws.geonames.org/searchJSON?q=T%C5%8Dky%C5%8D&style=FULL&maxRows=300",
|
||||||
UriUtils.encodeHttpUrl("http://ws.geonames.org/searchJSON?q=T\u014dky\u014d&style=FULL&maxRows=300", ENC));
|
UriUtils.encodeHttpUrl("http://ws.geonames.org/searchJSON?q=T\u014dky\u014d&style=FULL&maxRows=300", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL",
|
assertEquals("Invalid encoded HTTP URL",
|
||||||
|
|
@ -146,8 +146,8 @@ public class UriUtilsTests {
|
||||||
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar", ENC));
|
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL", "http://search.twitter.com/search.atom?q=%23avatar",
|
assertEquals("Invalid encoded HTTP URL", "http://search.twitter.com/search.atom?q=%23avatar",
|
||||||
UriUtils.encodeHttpUrl("http://search.twitter.com/search.atom?q=#avatar", ENC));
|
UriUtils.encodeHttpUrl("http://search.twitter.com/search.atom?q=#avatar", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL", "http://java.sun.com/j2se/1.3",
|
assertEquals("Invalid encoded HTTP URL", "http://java.sun.com/j2se/1.3/",
|
||||||
UriUtils.encodeHttpUrl("http://java.sun.com/j2se/1.3", ENC));
|
UriUtils.encodeHttpUrl("http://java.sun.com/j2se/1.3/", ENC));
|
||||||
assertEquals("Invalid encoded HTTP URL", "http://example.com/query=foo@bar",
|
assertEquals("Invalid encoded HTTP URL", "http://example.com/query=foo@bar",
|
||||||
UriUtils.encodeHttpUrl("http://example.com/query=foo@bar", ENC));
|
UriUtils.encodeHttpUrl("http://example.com/query=foo@bar", ENC));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue