SPR-5973: Using UriComponents in more places, replaced UriBuilder by UriComponentsBuilder, UriComponents is now immutable.
This commit is contained in:
parent
ce8bc8e7e4
commit
c8add61a72
|
|
@ -22,13 +22,13 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class ServletUriBuilder extends UriBuilder {
|
||||
public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
|
||||
|
||||
public static ServletUriBuilder fromCurrentServletRequest() {
|
||||
public static ServletUriComponentsBuilder fromCurrentServletRequest() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
Assert.state(requestAttributes != null, "Could not find current RequestAttributes in RequestContextHolder");
|
||||
Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes);
|
||||
|
|
@ -38,10 +38,10 @@ public class ServletUriBuilder extends UriBuilder {
|
|||
return fromServletRequest(servletRequest);
|
||||
}
|
||||
|
||||
public static ServletUriBuilder fromServletRequest(HttpServletRequest request) {
|
||||
public static ServletUriComponentsBuilder fromServletRequest(HttpServletRequest request) {
|
||||
Assert.notNull(request, "'request' must not be null");
|
||||
|
||||
ServletUriBuilder builder = new ServletUriBuilder();
|
||||
ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder();
|
||||
builder.scheme(request.getScheme());
|
||||
builder.host(request.getServerName());
|
||||
builder.port(request.getServerPort());
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link URI} objects.
|
||||
*
|
||||
* <p>Typical usage involves:
|
||||
* <ol>
|
||||
* <li>Create a {@code UriBuilder} with one of the static factory methods (such as {@link #fromPath(String)} or
|
||||
* {@link #fromUri(URI)})</li>
|
||||
* <li>Set the various URI components through the respective methods ({@link #scheme(String)},
|
||||
* {@link #userInfo(String)}, {@link #host(String)}, {@link #port(int)}, {@link #path(String)},
|
||||
* {@link #pathSegment(String...)}, {@link #queryParam(String, Object...)}, and {@link #fragment(String)}.</li>
|
||||
* <li>Build the URI with one of the {@link #build} method variants.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Most of the URI component methods accept URI template variables (i.e. {@code "{foo}"}), which are expanded by
|
||||
* calling {@code build}.
|
||||
*
|
||||
* <p>Inspired by {@link javax.ws.rs.core.UriBuilder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
* @see #newInstance()
|
||||
* @see #fromPath(String)
|
||||
* @see #fromUri(URI)
|
||||
*/
|
||||
public class UriBuilder {
|
||||
|
||||
private String scheme;
|
||||
|
||||
private String userInfo;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port = -1;
|
||||
|
||||
private final List<String> pathSegments = new ArrayList<String>();
|
||||
|
||||
private final StringBuilder queryBuilder = new StringBuilder();
|
||||
|
||||
private String fragment;
|
||||
|
||||
/**
|
||||
* Default constructor. Protected to prevent direct instantiation.
|
||||
*
|
||||
* @see #newInstance()
|
||||
* @see #fromPath(String)
|
||||
* @see #fromUri(URI)
|
||||
*/
|
||||
protected UriBuilder() {
|
||||
}
|
||||
|
||||
// Factory methods
|
||||
|
||||
/**
|
||||
* Returns a new, empty URI builder.
|
||||
*
|
||||
* @return the new {@code UriBuilder}
|
||||
*/
|
||||
public static UriBuilder newInstance() {
|
||||
return new UriBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URI builder that is initialized with the given path.
|
||||
*
|
||||
* @param path the path to initialize with
|
||||
* @return the new {@code UriBuilder}
|
||||
*/
|
||||
public static UriBuilder fromPath(String path) {
|
||||
UriBuilder builder = new UriBuilder();
|
||||
builder.path(path);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URI builder that is initialized with the given {@code URI}.
|
||||
*
|
||||
* @param uri the URI to initialize with
|
||||
* @return the new {@code UriBuilder}
|
||||
*/
|
||||
public static UriBuilder fromUri(URI uri) {
|
||||
UriBuilder builder = new UriBuilder();
|
||||
builder.uri(uri);
|
||||
return builder;
|
||||
}
|
||||
|
||||
// build methods
|
||||
|
||||
/**
|
||||
* Builds a URI with no URI template variables. Any template variable definitions found will be encoded (i.e.
|
||||
* {@code "/{foo}"} will result in {@code "/%7Bfoo%7D"}.
|
||||
* @return the resulting URI
|
||||
*/
|
||||
public URI build() {
|
||||
String port = portAsString();
|
||||
String path = null;
|
||||
if (!pathSegments.isEmpty()) {
|
||||
StringBuilder pathBuilder = new StringBuilder();
|
||||
for (String pathSegment : pathSegments) {
|
||||
boolean startsWithSlash = pathSegment.charAt(0) == '/';
|
||||
boolean endsWithSlash = pathBuilder.length() > 0 && pathBuilder.charAt(pathBuilder.length() - 1) == '/';
|
||||
|
||||
if (!endsWithSlash && !startsWithSlash) {
|
||||
pathBuilder.append('/');
|
||||
}
|
||||
else if (endsWithSlash && startsWithSlash) {
|
||||
pathSegment = pathSegment.substring(1);
|
||||
}
|
||||
pathBuilder.append(pathSegment);
|
||||
}
|
||||
path = pathBuilder.toString();
|
||||
}
|
||||
String query = queryAsString();
|
||||
|
||||
String uri = UriUtils.buildUri(scheme, null, userInfo, host, port, path, query, fragment);
|
||||
|
||||
uri = StringUtils.replace(uri, "{", "%7B");
|
||||
uri = StringUtils.replace(uri, "}", "%7D");
|
||||
|
||||
return URI.create(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI with the given URI template variables. Any template variable definitions found will be expanded with
|
||||
* the given variables map. All variable values will be encoded in accordance with the encoding rules for the URI
|
||||
* component they occur in.
|
||||
*
|
||||
* @param uriVariables the map of URI variables
|
||||
* @return the resulting URI
|
||||
*/
|
||||
public URI build(Map<String, ?> uriVariables) {
|
||||
return buildFromMap(uriVariables, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI with the given URI template variables. Any template variable definitions found will be expanded with the
|
||||
* given variables map. All variable values will not be encoded.
|
||||
*
|
||||
* @param uriVariables the map of URI variables
|
||||
* @return the resulting URI
|
||||
*/
|
||||
public URI buildFromEncoded(Map<String, ?> uriVariables) {
|
||||
return buildFromMap(uriVariables, false);
|
||||
}
|
||||
|
||||
private URI buildFromMap(Map<String, ?> uriVariables, boolean encodeUriVariableValues) {
|
||||
if (CollectionUtils.isEmpty(uriVariables)) {
|
||||
return build();
|
||||
}
|
||||
String scheme = expand(this.scheme, UriComponents.Type.SCHEME, uriVariables, encodeUriVariableValues);
|
||||
String userInfo = expand(this.userInfo, UriComponents.Type.USER_INFO, uriVariables, encodeUriVariableValues);
|
||||
String host = expand(this.host, UriComponents.Type.HOST, uriVariables, encodeUriVariableValues);
|
||||
String port = expand(this.portAsString(), UriComponents.Type.PORT, uriVariables, encodeUriVariableValues);
|
||||
String path = null;
|
||||
if (!this.pathSegments.isEmpty()) {
|
||||
StringBuilder pathBuilder = new StringBuilder();
|
||||
for (String pathSegment : this.pathSegments) {
|
||||
boolean startsWithSlash = pathSegment.charAt(0) == '/';
|
||||
boolean endsWithSlash = pathBuilder.length() > 0 && pathBuilder.charAt(pathBuilder.length() - 1) == '/';
|
||||
|
||||
if (!endsWithSlash && !startsWithSlash) {
|
||||
pathBuilder.append('/');
|
||||
}
|
||||
else if (endsWithSlash && startsWithSlash) {
|
||||
pathSegment = pathSegment.substring(1);
|
||||
}
|
||||
pathSegment = expand(pathSegment, UriComponents.Type.PATH_SEGMENT, uriVariables, encodeUriVariableValues);
|
||||
pathBuilder.append(pathSegment);
|
||||
}
|
||||
path = pathBuilder.toString();
|
||||
}
|
||||
String query = expand(this.queryAsString(), UriComponents.Type.QUERY, uriVariables, encodeUriVariableValues);
|
||||
String fragment = expand(this.fragment, UriComponents.Type.FRAGMENT, uriVariables, encodeUriVariableValues);
|
||||
|
||||
String uri = UriUtils.buildUri(scheme, null, userInfo, host, port, path, query, fragment);
|
||||
return URI.create(uri);
|
||||
}
|
||||
|
||||
private String expand(String source,
|
||||
UriComponents.Type uriComponent,
|
||||
Map<String, ?> uriVariables,
|
||||
boolean encodeUriVariableValues) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (source.indexOf('{') == -1) {
|
||||
return source;
|
||||
}
|
||||
UriTemplate template = new UriComponentTemplate(source, uriComponent, encodeUriVariableValues);
|
||||
return template.expandAsString(uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI with the given URI template variable values. Any template variable definitions found will be expanded
|
||||
* with the given variables. All variable values will be encoded in accordance with the encoding rules for the URI
|
||||
* component they occur in.
|
||||
*
|
||||
* @param uriVariableValues the array of URI variables
|
||||
* @return the resulting URI
|
||||
*/
|
||||
public URI build(Object... uriVariableValues) {
|
||||
return buildFromVarArg(true, uriVariableValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI with the given URI template variable values. Any template variable definitions found will be expanded
|
||||
* with the given variables. All variable values will not be encoded.
|
||||
*
|
||||
* @param uriVariableValues the array of URI variables
|
||||
* @return the resulting URI
|
||||
*/
|
||||
public URI buildFromEncoded(Object... uriVariableValues) {
|
||||
return buildFromVarArg(false, uriVariableValues);
|
||||
}
|
||||
|
||||
private URI buildFromVarArg(boolean encodeUriVariableValues, Object... uriVariableValues) {
|
||||
if (ObjectUtils.isEmpty(uriVariableValues)) {
|
||||
return build();
|
||||
}
|
||||
|
||||
StringBuilder uriBuilder = new StringBuilder();
|
||||
|
||||
UriTemplate template;
|
||||
|
||||
if (scheme != null) {
|
||||
template = new UriComponentTemplate(scheme, UriComponents.Type.SCHEME, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
uriBuilder.append(':');
|
||||
}
|
||||
|
||||
if (userInfo != null || host != null || port != -1) {
|
||||
uriBuilder.append("//");
|
||||
|
||||
if (StringUtils.hasLength(userInfo)) {
|
||||
template = new UriComponentTemplate(userInfo, UriComponents.Type.USER_INFO, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
uriBuilder.append('@');
|
||||
}
|
||||
|
||||
if (host != null) {
|
||||
template = new UriComponentTemplate(host, UriComponents.Type.HOST, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
}
|
||||
|
||||
if (port != -1) {
|
||||
uriBuilder.append(':');
|
||||
uriBuilder.append(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pathSegments.isEmpty()) {
|
||||
for (String pathSegment : pathSegments) {
|
||||
boolean startsWithSlash = pathSegment.charAt(0) == '/';
|
||||
boolean endsWithSlash = uriBuilder.length() > 0 && uriBuilder.charAt(uriBuilder.length() - 1) == '/';
|
||||
|
||||
if (!endsWithSlash && !startsWithSlash) {
|
||||
uriBuilder.append('/');
|
||||
}
|
||||
else if (endsWithSlash && startsWithSlash) {
|
||||
pathSegment = pathSegment.substring(1);
|
||||
}
|
||||
template = new UriComponentTemplate(pathSegment, UriComponents.Type.PATH_SEGMENT, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
}
|
||||
}
|
||||
|
||||
if (queryBuilder.length() > 0) {
|
||||
uriBuilder.append('?');
|
||||
template = new UriComponentTemplate(queryBuilder.toString(), UriComponents.Type.QUERY, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
}
|
||||
|
||||
if (StringUtils.hasLength(fragment)) {
|
||||
uriBuilder.append('#');
|
||||
template = new UriComponentTemplate(fragment, UriComponents.Type.FRAGMENT, encodeUriVariableValues);
|
||||
uriBuilder.append(template.expandAsString(uriVariableValues));
|
||||
}
|
||||
|
||||
return URI.create(uriBuilder.toString());
|
||||
}
|
||||
|
||||
// URI components methods
|
||||
|
||||
/**
|
||||
* Initializes all components of this URI builder with the components of the given URI.
|
||||
*
|
||||
* @param uri the URI
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder uri(URI uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
Assert.isTrue(!uri.isOpaque(), "Opaque URI [" + uri + "] not supported");
|
||||
|
||||
this.scheme = uri.getScheme();
|
||||
|
||||
if (uri.getRawUserInfo() != null) {
|
||||
this.userInfo = uri.getRawUserInfo();
|
||||
}
|
||||
if (uri.getHost() != null) {
|
||||
this.host = uri.getHost();
|
||||
}
|
||||
if (uri.getPort() != -1) {
|
||||
this.port = uri.getPort();
|
||||
}
|
||||
if (StringUtils.hasLength(uri.getRawPath())) {
|
||||
String[] pathSegments = StringUtils.tokenizeToStringArray(uri.getRawPath(), "/");
|
||||
|
||||
this.pathSegments.clear();
|
||||
Collections.addAll(this.pathSegments, pathSegments);
|
||||
}
|
||||
if (StringUtils.hasLength(uri.getRawQuery())) {
|
||||
this.queryBuilder.setLength(0);
|
||||
this.queryBuilder.append(uri.getRawQuery());
|
||||
}
|
||||
if (uri.getRawFragment() != null) {
|
||||
this.fragment = uri.getRawFragment();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI scheme. The given scheme may contain URI template variables, and may also be {@code null} to clear the
|
||||
* scheme of this builder.
|
||||
*
|
||||
* @param scheme the URI scheme
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder scheme(String scheme) {
|
||||
if (scheme != null) {
|
||||
Assert.hasLength(scheme, "'scheme' must not be empty");
|
||||
this.scheme = encodeUriComponent(scheme, UriComponents.Type.SCHEME);
|
||||
}
|
||||
else {
|
||||
this.scheme = null;
|
||||
}
|
||||
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
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder userInfo(String userInfo) {
|
||||
if (userInfo != null) {
|
||||
Assert.hasLength(userInfo, "'userInfo' must not be empty");
|
||||
this.userInfo = encodeUriComponent(userInfo, UriComponents.Type.USER_INFO);
|
||||
}
|
||||
else {
|
||||
this.userInfo = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI host. The given host may contain URI template variables, and may also be {@code null} to clear the host
|
||||
* of this builder.
|
||||
*
|
||||
* @param host the URI host
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder host(String host) {
|
||||
if (host != null) {
|
||||
Assert.hasLength(host, "'host' must not be empty");
|
||||
this.host = encodeUriComponent(host, UriComponents.Type.HOST);
|
||||
}
|
||||
else {
|
||||
this.host = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI port. Passing {@code -1} will clear the port of this builder.
|
||||
*
|
||||
* @param port the URI port
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder port(int port) {
|
||||
Assert.isTrue(port >= -1, "'port' must not be < -1");
|
||||
this.port = port;
|
||||
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.
|
||||
*
|
||||
* @param path the URI path
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder path(String path) {
|
||||
Assert.notNull(path, "path must not be null");
|
||||
|
||||
String[] pathSegments = StringUtils.tokenizeToStringArray(path, "/");
|
||||
return pathSegment(pathSegments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given path segments to the existing path of this builder. Each given path segments may contain URI
|
||||
* template variables.
|
||||
*
|
||||
* @param segments the URI path segments
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder pathSegment(String... segments) throws IllegalArgumentException {
|
||||
Assert.notNull(segments, "'segments' must not be null");
|
||||
for (String segment : segments) {
|
||||
this.pathSegments.add(encodeUriComponent(segment, UriComponents.Type.PATH_SEGMENT));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given query parameter to the existing query parameters. The given name or any of the values may contain
|
||||
* URI template variables. If no 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 values the query parameter values
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder queryParam(String name, Object... values) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
|
||||
String encodedName = encodeUriComponent(name, UriComponents.Type.QUERY_PARAM);
|
||||
|
||||
if (ObjectUtils.isEmpty(values)) {
|
||||
if (queryBuilder.length() != 0) {
|
||||
queryBuilder.append('&');
|
||||
}
|
||||
queryBuilder.append(encodedName);
|
||||
}
|
||||
else {
|
||||
for (Object value : values) {
|
||||
if (queryBuilder.length() != 0) {
|
||||
queryBuilder.append('&');
|
||||
}
|
||||
queryBuilder.append(encodedName);
|
||||
|
||||
String valueAsString = value != null ? value.toString() : "";
|
||||
if (valueAsString.length() != 0) {
|
||||
queryBuilder.append('=');
|
||||
queryBuilder.append(encodeUriComponent(valueAsString, UriComponents.Type.QUERY_PARAM));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private String queryAsString() {
|
||||
return queryBuilder.length() != 0 ? queryBuilder.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI fragment. The given fragment may contain URI template variables, and may also be {@code null} to clear
|
||||
* the fragment of this builder.
|
||||
*
|
||||
* @param fragment the URI fragment
|
||||
* @return this UriBuilder
|
||||
*/
|
||||
public UriBuilder fragment(String fragment) {
|
||||
if (fragment != null) {
|
||||
Assert.hasLength(fragment, "'fragment' must not be empty");
|
||||
this.fragment = encodeUriComponent(fragment, UriComponents.Type.FRAGMENT);
|
||||
}
|
||||
else {
|
||||
this.fragment = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private String encodeUriComponent(String source, UriComponents.Type uriComponent) {
|
||||
return UriUtils.encodeUriComponent(source, uriComponent, EnumSet.of(UriUtils.EncodingOption.ALLOW_TEMPLATE_VARS));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Subclass of {@link UriTemplate} that operates on URI components, rather than full URIs.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
class UriComponentTemplate extends UriTemplate {
|
||||
|
||||
private final UriComponents.Type uriComponent;
|
||||
|
||||
private boolean encodeUriVariableValues;
|
||||
|
||||
UriComponentTemplate(String uriTemplate, UriComponents.Type uriComponent, boolean encodeUriVariableValues) {
|
||||
super(uriTemplate);
|
||||
Assert.notNull(uriComponent, "'uriComponent' must not be null");
|
||||
this.uriComponent = uriComponent;
|
||||
this.encodeUriVariableValues = encodeUriVariableValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVariableValueAsString(Object variableValue) {
|
||||
String variableValueString = super.getVariableValueAsString(variableValue);
|
||||
return encodeUriVariableValues ? UriUtils.encodeUriComponent(variableValueString, uriComponent) :
|
||||
variableValueString;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,14 @@
|
|||
|
||||
package org.springframework.web.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
@ -28,23 +31,55 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents the components that make up a URI, mapping component type to string values. Contains convenience getters
|
||||
* and setters for all components, as well as the regular {@link Map} implementation.
|
||||
*
|
||||
* <p>This mapping does not contain mappings for {@link org.springframework.web.util.UriComponents.Type#PATH_SEGMENT} or nor {@link
|
||||
* org.springframework.web.util.UriComponents.Type#QUERY_PARAM}, since those components can occur multiple times in the URI. Instead, one can use {@link
|
||||
* #getPathSegments()} or {@link #getQueryParams()} respectively.
|
||||
* Represents an immutable collection of URI components, mapping component type to string values. Contains convenience
|
||||
* getters for all components, as well as the regular {@link Map} implementation. Effectively similar to {@link URI},
|
||||
* but with more powerful encoding options.
|
||||
* <p/>
|
||||
* <strong>Note</strong> that this {@code Map} does not contain entries for {@link Type#PATH_SEGMENT}
|
||||
* nor {@link Type#QUERY_PARAM}, since those components can occur multiple
|
||||
* times in the URI. Instead, one can use {@link #getPathSegments()} or {@link #getQueryParams()} respectively.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
* @see UriComponentsBuilder
|
||||
*/
|
||||
public class UriComponents implements Map<UriComponents.Type, String> {
|
||||
public final class UriComponents implements Map<UriComponents.Type, String> {
|
||||
|
||||
/**
|
||||
* The default encoding used for various encode methods.
|
||||
*/
|
||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
private static final String SCHEME_PATTERN = "([^:/?#]+):";
|
||||
|
||||
private static final String HTTP_PATTERN = "(http|https):";
|
||||
|
||||
private static final String USERINFO_PATTERN = "([^@/]*)";
|
||||
|
||||
private static final String HOST_PATTERN = "([^/?#:]*)";
|
||||
|
||||
private static final String PORT_PATTERN = "(\\d*)";
|
||||
|
||||
private static final String PATH_PATTERN = "([^?#]*)";
|
||||
|
||||
private static final String QUERY_PATTERN = "([^#]*)";
|
||||
|
||||
private static final String LAST_PATTERN = "(.*)";
|
||||
|
||||
// Regex patterns that matches URIs. See RFC 3986, appendix B
|
||||
private static final Pattern URI_PATTERN = Pattern.compile(
|
||||
"^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
|
||||
")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");
|
||||
|
||||
private static final Pattern HTTP_URL_PATTERN = Pattern.compile(
|
||||
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
|
||||
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
|
||||
|
||||
|
||||
private static final String PATH_DELIMITER = "/";
|
||||
|
||||
|
|
@ -52,22 +87,192 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
|
||||
private final Map<Type, String> uriComponents;
|
||||
|
||||
/** Constructs a new, empty instance of the {@code UriComponents} object. */
|
||||
public UriComponents() {
|
||||
this.uriComponents = new EnumMap<Type, String>(Type.class);
|
||||
private final boolean encoded;
|
||||
|
||||
private UriComponents(Map<Type, String> uriComponents, boolean encoded) {
|
||||
Assert.notEmpty(uriComponents, "'uriComponents' must not be empty");
|
||||
this.uriComponents = Collections.unmodifiableMap(uriComponents);
|
||||
this.encoded = encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the {@code UriComponents} object that contains the given component.
|
||||
* Creates a new {@code UriComponents} object from the given string URI.
|
||||
*
|
||||
* @param uri the source URI
|
||||
* @return the URI components of the URI
|
||||
*/
|
||||
public static UriComponents fromUriString(String uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
Matcher m = URI_PATTERN.matcher(uri);
|
||||
if (m.matches()) {
|
||||
Map<UriComponents.Type, String> result = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
|
||||
result.put(UriComponents.Type.SCHEME, m.group(2));
|
||||
result.put(UriComponents.Type.AUTHORITY, m.group(3));
|
||||
result.put(UriComponents.Type.USER_INFO, m.group(5));
|
||||
result.put(UriComponents.Type.HOST, m.group(6));
|
||||
result.put(UriComponents.Type.PORT, m.group(8));
|
||||
result.put(UriComponents.Type.PATH, m.group(9));
|
||||
result.put(UriComponents.Type.QUERY, m.group(11));
|
||||
result.put(UriComponents.Type.FRAGMENT, m.group(13));
|
||||
|
||||
return new UriComponents(result, false);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code UriComponents} object from the string HTTP URL.
|
||||
*
|
||||
* @param httpUrl the source URI
|
||||
* @return the URI components of the URI
|
||||
*/
|
||||
public static UriComponents fromHttpUrl(String httpUrl) {
|
||||
Assert.notNull(httpUrl, "'httpUrl' must not be null");
|
||||
Matcher m = HTTP_URL_PATTERN.matcher(httpUrl);
|
||||
if (m.matches()) {
|
||||
Map<UriComponents.Type, String> result = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
|
||||
result.put(UriComponents.Type.SCHEME, m.group(1));
|
||||
result.put(UriComponents.Type.AUTHORITY, m.group(2));
|
||||
result.put(UriComponents.Type.USER_INFO, m.group(4));
|
||||
result.put(UriComponents.Type.HOST, m.group(5));
|
||||
result.put(UriComponents.Type.PORT, m.group(7));
|
||||
result.put(UriComponents.Type.PATH, m.group(8));
|
||||
result.put(UriComponents.Type.QUERY, m.group(10));
|
||||
|
||||
return new UriComponents(result, false);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code UriComponents} object from the given {@code URI}.
|
||||
*
|
||||
* @param uri the URI
|
||||
* @return the URI components of the URI
|
||||
*/
|
||||
public static UriComponents fromUri(URI uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
|
||||
Map<Type, String> uriComponents = new EnumMap<Type, String>(Type.class);
|
||||
if (uri.getScheme() != null) {
|
||||
uriComponents.put(Type.SCHEME, uri.getScheme());
|
||||
}
|
||||
if (uri.getRawAuthority() != null) {
|
||||
uriComponents.put(Type.AUTHORITY, uri.getRawAuthority());
|
||||
}
|
||||
if (uri.getRawUserInfo() != null) {
|
||||
uriComponents.put(Type.USER_INFO, uri.getRawUserInfo());
|
||||
}
|
||||
if (uri.getHost() != null) {
|
||||
uriComponents.put(Type.HOST, uri.getHost());
|
||||
}
|
||||
if (uri.getPort() != -1) {
|
||||
uriComponents.put(Type.PORT, Integer.toString(uri.getPort()));
|
||||
}
|
||||
if (uri.getRawPath() != null) {
|
||||
uriComponents.put(Type.PATH, uri.getRawPath());
|
||||
}
|
||||
if (uri.getRawQuery() != null) {
|
||||
uriComponents.put(Type.QUERY, uri.getRawQuery());
|
||||
}
|
||||
if (uri.getRawFragment() != null) {
|
||||
uriComponents.put(Type.FRAGMENT, uri.getRawFragment());
|
||||
}
|
||||
return new UriComponents(uriComponents, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance of the {@code UriComponents} object from the given components. All the given arguments
|
||||
* can be {@code null} and are considered to be unencoded.
|
||||
*/
|
||||
public static UriComponents fromUriComponents(String scheme,
|
||||
String authority,
|
||||
String userInfo,
|
||||
String host,
|
||||
String port,
|
||||
String path,
|
||||
String query,
|
||||
String fragment) {
|
||||
return fromUriComponents(scheme, authority, userInfo, host, port, path, query, fragment, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the {@code UriComponents} object from the given components. All the given arguments
|
||||
* can be {@code null}.
|
||||
*
|
||||
* @param encoded {@code true} if the arguments are encoded; {@code false} otherwise
|
||||
*/
|
||||
public static UriComponents fromUriComponents(String scheme,
|
||||
String authority,
|
||||
String userInfo,
|
||||
String host,
|
||||
String port,
|
||||
String path,
|
||||
String query,
|
||||
String fragment,
|
||||
boolean encoded) {
|
||||
Map<Type, String> uriComponents = new EnumMap<Type, String>(Type.class);
|
||||
if (scheme != null) {
|
||||
uriComponents.put(Type.SCHEME, scheme);
|
||||
}
|
||||
if (authority != null) {
|
||||
uriComponents.put(Type.AUTHORITY, authority);
|
||||
}
|
||||
if (userInfo != null) {
|
||||
uriComponents.put(Type.USER_INFO, userInfo);
|
||||
}
|
||||
if (host != null) {
|
||||
uriComponents.put(Type.HOST, host);
|
||||
}
|
||||
if (port != null) {
|
||||
uriComponents.put(Type.PORT, port);
|
||||
}
|
||||
if (path != null) {
|
||||
uriComponents.put(Type.PATH, path);
|
||||
}
|
||||
if (query != null) {
|
||||
uriComponents.put(Type.QUERY, query);
|
||||
}
|
||||
if (fragment != null) {
|
||||
uriComponents.put(Type.FRAGMENT, fragment);
|
||||
}
|
||||
return new UriComponents(uriComponents, encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the {@code UriComponents} object that contains the given components map.
|
||||
*
|
||||
* @param uriComponents the component to initialize with
|
||||
*/
|
||||
public UriComponents(Map<Type, String> uriComponents) {
|
||||
Assert.notNull(uriComponents, "'uriComponents' must not be null");
|
||||
this.uriComponents = new EnumMap<Type, String>(uriComponents);
|
||||
public static UriComponents fromUriComponentMap(Map<Type, String> uriComponents) {
|
||||
boolean encoded;
|
||||
if (uriComponents instanceof UriComponents) {
|
||||
encoded = ((UriComponents) uriComponents).encoded;
|
||||
}
|
||||
else {
|
||||
encoded = false;
|
||||
}
|
||||
return new UriComponents(uriComponents, encoded);
|
||||
}
|
||||
|
||||
// convenience properties
|
||||
/**
|
||||
* Creates an instance of the {@code UriComponents} object that contains the given components map.
|
||||
*
|
||||
* @param uriComponents the component to initialize with
|
||||
* @param encoded whether the components are encpded
|
||||
*/
|
||||
public static UriComponents fromUriComponentMap(Map<Type, String> uriComponents, boolean encoded) {
|
||||
return new UriComponents(uriComponents, encoded);
|
||||
}
|
||||
|
||||
// component getters
|
||||
|
||||
/**
|
||||
* Returns the scheme.
|
||||
|
|
@ -78,15 +283,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.SCHEME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scheme.
|
||||
*
|
||||
* @param scheme the scheme. Can be {@code null}.
|
||||
*/
|
||||
public void setScheme(String scheme) {
|
||||
put(Type.SCHEME, scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authority.
|
||||
*
|
||||
|
|
@ -96,15 +292,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authority.
|
||||
*
|
||||
* @param authority the authority. Can be {@code null}.
|
||||
*/
|
||||
public void setAuthority(String authority) {
|
||||
put(Type.AUTHORITY, authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user info.
|
||||
*
|
||||
|
|
@ -114,15 +301,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.USER_INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user info
|
||||
*
|
||||
* @param userInfo the user info. Can be {@code null}
|
||||
*/
|
||||
public void setUserInfo(String userInfo) {
|
||||
put(Type.USER_INFO, userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host.
|
||||
*
|
||||
|
|
@ -132,15 +310,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host.
|
||||
*
|
||||
* @param host the host. Can be {@code null}.
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
put(Type.HOST, host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port as string.
|
||||
*
|
||||
|
|
@ -150,15 +319,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port as string.
|
||||
*
|
||||
* @param port the port as string. Can be {@code null}.
|
||||
*/
|
||||
public void setPort(String port) {
|
||||
put(Type.PORT, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port as integer. Returns {@code -1} if no port has been set.
|
||||
*
|
||||
|
|
@ -169,16 +329,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return port != null ? Integer.parseInt(port) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port as integer. A value < 0 resets the port to an empty value.
|
||||
*
|
||||
* @param port the port as integer
|
||||
*/
|
||||
public void setPortAsInteger(int port) {
|
||||
String portString = port > -1 ? Integer.toString(port) : null;
|
||||
put(Type.PORT, portString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path.
|
||||
*
|
||||
|
|
@ -188,15 +338,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path.
|
||||
*
|
||||
* @param path the path. Can be {@code null}.
|
||||
*/
|
||||
public void setPath(String path) {
|
||||
put(Type.PATH, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of path segments.
|
||||
*
|
||||
|
|
@ -212,28 +353,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path segments. An empty or {@code null} value resets the path to an empty value.
|
||||
*
|
||||
* @param pathSegments the path segments
|
||||
*/
|
||||
public void setPathSegments(List<String> pathSegments) {
|
||||
if (!CollectionUtils.isEmpty(pathSegments)) {
|
||||
StringBuilder pathBuilder = new StringBuilder("/");
|
||||
for (Iterator<String> iterator = pathSegments.iterator(); iterator.hasNext(); ) {
|
||||
String pathSegment = iterator.next();
|
||||
pathBuilder.append(pathSegment);
|
||||
if (iterator.hasNext()) {
|
||||
pathBuilder.append('/');
|
||||
}
|
||||
}
|
||||
setPath(pathBuilder.toString());
|
||||
}
|
||||
else {
|
||||
setPath(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query.
|
||||
*
|
||||
|
|
@ -243,15 +362,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.QUERY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query.
|
||||
*
|
||||
* @param query the query. Can be {@code null}.
|
||||
*/
|
||||
public void setQuery(String query) {
|
||||
put(Type.QUERY, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of query parameters.
|
||||
*
|
||||
|
|
@ -271,44 +381,6 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query parameters. An empty or {@code null} value resets the query to an empty value.
|
||||
*
|
||||
* @param queryParams the query parameters
|
||||
*/
|
||||
public void setQueryParams(MultiValueMap<String, String> queryParams) {
|
||||
if (!CollectionUtils.isEmpty(queryParams)) {
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
for (Iterator<Entry<String, List<String>>> entryIterator = queryParams.entrySet().iterator();
|
||||
entryIterator.hasNext(); ) {
|
||||
Entry<String, List<String>> entry = entryIterator.next();
|
||||
String name = entry.getKey();
|
||||
List<String> values = entry.getValue();
|
||||
if (CollectionUtils.isEmpty(values) || (values.size() == 1 && values.get(0) == null)) {
|
||||
queryBuilder.append(name);
|
||||
}
|
||||
else {
|
||||
for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext(); ) {
|
||||
String value = valueIterator.next();
|
||||
queryBuilder.append(name);
|
||||
queryBuilder.append('=');
|
||||
queryBuilder.append(value);
|
||||
if (valueIterator.hasNext()) {
|
||||
queryBuilder.append('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entryIterator.hasNext()) {
|
||||
queryBuilder.append('&');
|
||||
}
|
||||
}
|
||||
setQuery(queryBuilder.toString());
|
||||
}
|
||||
else {
|
||||
setQuery(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fragment.
|
||||
*
|
||||
|
|
@ -318,13 +390,165 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
return get(Type.FRAGMENT);
|
||||
}
|
||||
|
||||
// other functionality
|
||||
|
||||
public UriComponents encode() {
|
||||
try {
|
||||
return encode(DEFAULT_ENCODING);
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError("\"" + DEFAULT_ENCODING + "\" not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fragment.
|
||||
* Encodes all URI components using their specific encoding rules, and returns the result as a new
|
||||
* {@code UriComponents} instance.
|
||||
*
|
||||
* @param fragment the fragment. Can be {@code null}.
|
||||
* @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
|
||||
*/
|
||||
public void setFragment(String fragment) {
|
||||
put(Type.FRAGMENT, fragment);
|
||||
public UriComponents encode(String encoding) throws UnsupportedEncodingException {
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
|
||||
if (encoded) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final Map<Type, String> encoded = new EnumMap<Type, String>(Type.class);
|
||||
for (Entry<Type, String> entry : uriComponents.entrySet()) {
|
||||
Type key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (value != null) {
|
||||
value = encodeUriComponent(value, encoding, key);
|
||||
}
|
||||
encoded.put(key, value);
|
||||
}
|
||||
return new UriComponents(encoded, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 uriComponent the URI component for the source
|
||||
* @param encodingOptions the options used when encoding. May be {@code null}.
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
* @see EncodingOption
|
||||
*/
|
||||
static String encodeUriComponent(String source,
|
||||
String encoding,
|
||||
UriComponents.Type uriComponent) throws UnsupportedEncodingException {
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
|
||||
byte[] bytes = encodeInternal(source.getBytes(encoding), uriComponent);
|
||||
return new String(bytes, "US-ASCII");
|
||||
}
|
||||
|
||||
private static byte[] encodeInternal(byte[] source,
|
||||
UriComponents.Type uriComponent) {
|
||||
Assert.notNull(source, "'source' must not be null");
|
||||
Assert.notNull(uriComponent, "'uriComponent' 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 (uriComponent.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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a URI string from this {@code UriComponents} instance.
|
||||
*
|
||||
* @return the URI created from the given components
|
||||
*/
|
||||
public String toUriString() {
|
||||
StringBuilder uriBuilder = new StringBuilder();
|
||||
|
||||
if (getScheme() != null) {
|
||||
uriBuilder.append(getScheme());
|
||||
uriBuilder.append(':');
|
||||
}
|
||||
|
||||
if (getUserInfo() != null || getHost() != null || getPort() != null) {
|
||||
uriBuilder.append("//");
|
||||
if (getUserInfo() != null) {
|
||||
uriBuilder.append(getUserInfo());
|
||||
uriBuilder.append('@');
|
||||
}
|
||||
if (getHost() != null) {
|
||||
uriBuilder.append(getHost());
|
||||
}
|
||||
if (getPort() != null) {
|
||||
uriBuilder.append(':');
|
||||
uriBuilder.append(getPort());
|
||||
}
|
||||
}
|
||||
else if (getAuthority() != null) {
|
||||
uriBuilder.append("//");
|
||||
uriBuilder.append(getAuthority());
|
||||
}
|
||||
|
||||
if (getPath() != null) {
|
||||
uriBuilder.append(getPath());
|
||||
}
|
||||
|
||||
if (getQuery() != null) {
|
||||
uriBuilder.append('?');
|
||||
uriBuilder.append(getQuery());
|
||||
}
|
||||
|
||||
if (getFragment() != null) {
|
||||
uriBuilder.append('#');
|
||||
uriBuilder.append(getFragment());
|
||||
}
|
||||
|
||||
return uriBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code URI} from this {@code UriComponents} instance.
|
||||
*
|
||||
* @return the URI created from the given components
|
||||
*/
|
||||
public URI toUri() {
|
||||
try {
|
||||
if (encoded) {
|
||||
return new URI(toUriString());
|
||||
}
|
||||
else {
|
||||
if (getUserInfo() != null || getHost() != null || getPort() != null) {
|
||||
return new URI(getScheme(), getUserInfo(), getHost(), getPortAsInteger(), getPath(), getQuery(),
|
||||
getFragment());
|
||||
}
|
||||
else {
|
||||
return new URI(getScheme(), getAuthority(), getPath(), getQuery(), getFragment());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Map implementation
|
||||
|
|
@ -403,7 +627,7 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
|
||||
/**
|
||||
* Enumeration used to identify the parts of a URI.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Contains methods to indicate whether a given character is valid in a specific URI component.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
|
|
@ -550,4 +774,5 @@ public class UriComponents implements Map<UriComponents.Type, String> {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link UriComponents}.
|
||||
* <p/>
|
||||
* Typical usage involves:
|
||||
* <ol>
|
||||
* <li>Create a {@code UriComponentsBuilder} with one of the static factory methods (such as
|
||||
* {@link #fromPath(String)} or {@link #fromUri(URI)})</li>
|
||||
* <li>Set the various URI components through the respective methods ({@link #scheme(String)},
|
||||
* {@link #userInfo(String)}, {@link #host(String)}, {@link #port(int)}, {@link #path(String)},
|
||||
* {@link #pathSegment(String...)}, {@link #queryParam(String, Object...)}, and
|
||||
* {@link #fragment(String)}.</li>
|
||||
* <li>Build the {@link UriComponents} instance with the {@link #build()} method.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #newInstance()
|
||||
* @see #fromPath(String)
|
||||
* @see #fromUri(URI)
|
||||
* @since 3.1
|
||||
*/
|
||||
public class UriComponentsBuilder {
|
||||
|
||||
private static final char PATH_DELIMITER = '/';
|
||||
|
||||
private String scheme;
|
||||
|
||||
private String userInfo;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port = -1;
|
||||
|
||||
private final List<String> pathSegments = new ArrayList<String>();
|
||||
|
||||
private final StringBuilder queryBuilder = new StringBuilder();
|
||||
|
||||
private String fragment;
|
||||
|
||||
/**
|
||||
* Default constructor. Protected to prevent direct instantiation.
|
||||
*
|
||||
* @see #newInstance()
|
||||
* @see #fromPath(String)
|
||||
* @see #fromUri(URI)
|
||||
*/
|
||||
protected UriComponentsBuilder() {
|
||||
}
|
||||
|
||||
// Factory methods
|
||||
|
||||
/**
|
||||
* Returns a new, empty URI builder.
|
||||
*
|
||||
* @return the new {@code UriComponentsBuilder}
|
||||
*/
|
||||
public static UriComponentsBuilder newInstance() {
|
||||
return new UriComponentsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URI builder that is initialized with the given path.
|
||||
*
|
||||
* @param path the path to initialize with
|
||||
* @return the new {@code UriComponentsBuilder}
|
||||
*/
|
||||
public static UriComponentsBuilder fromPath(String path) {
|
||||
UriComponentsBuilder builder = new UriComponentsBuilder();
|
||||
builder.path(path);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URI builder that is initialized with the given {@code URI}.
|
||||
*
|
||||
* @param uri the URI to initialize with
|
||||
* @return the new {@code UriComponentsBuilder}
|
||||
*/
|
||||
public static UriComponentsBuilder fromUri(URI uri) {
|
||||
UriComponentsBuilder builder = new UriComponentsBuilder();
|
||||
builder.uri(uri);
|
||||
return builder;
|
||||
}
|
||||
|
||||
// build methods
|
||||
|
||||
/**
|
||||
* Builds a {@code UriComponents} instance from the various components contained in this builder.
|
||||
*
|
||||
* @return the URI components
|
||||
*/
|
||||
public UriComponents build() {
|
||||
String port = portAsString();
|
||||
String path = pathAsString();
|
||||
String query = queryAsString();
|
||||
return UriComponents.fromUriComponents(scheme, null, userInfo, host, port, path, query, fragment, false);
|
||||
}
|
||||
|
||||
// URI components methods
|
||||
|
||||
/**
|
||||
* Initializes all components of this URI builder with the components of the given URI.
|
||||
*
|
||||
* @param uri the URI
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder uri(URI uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
Assert.isTrue(!uri.isOpaque(), "Opaque URI [" + uri + "] not supported");
|
||||
|
||||
this.scheme = uri.getScheme();
|
||||
|
||||
if (uri.getUserInfo() != null) {
|
||||
this.userInfo = uri.getUserInfo();
|
||||
}
|
||||
if (uri.getHost() != null) {
|
||||
this.host = uri.getHost();
|
||||
}
|
||||
if (uri.getPort() != -1) {
|
||||
this.port = uri.getPort();
|
||||
}
|
||||
if (StringUtils.hasLength(uri.getPath())) {
|
||||
String[] pathSegments = StringUtils.tokenizeToStringArray(uri.getPath(), "/");
|
||||
|
||||
this.pathSegments.clear();
|
||||
Collections.addAll(this.pathSegments, pathSegments);
|
||||
}
|
||||
if (StringUtils.hasLength(uri.getQuery())) {
|
||||
this.queryBuilder.setLength(0);
|
||||
this.queryBuilder.append(uri.getQuery());
|
||||
}
|
||||
if (uri.getFragment() != null) {
|
||||
this.fragment = uri.getFragment();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI scheme. The given scheme may contain URI template variables, and may also be {@code null} to clear the
|
||||
* scheme of this builder.
|
||||
*
|
||||
* @param scheme the URI scheme
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder scheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
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
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder userInfo(String userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI host. The given host may contain URI template variables, and may also be {@code null} to clear the host
|
||||
* of this builder.
|
||||
*
|
||||
* @param host the URI host
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder host(String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI port. Passing {@code -1} will clear the port of this builder.
|
||||
*
|
||||
* @param port the URI port
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder port(int port) {
|
||||
Assert.isTrue(port >= -1, "'port' must not be < -1");
|
||||
this.port = port;
|
||||
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.
|
||||
*
|
||||
* @param path the URI path
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder path(String path) {
|
||||
Assert.notNull(path, "path must not be null");
|
||||
|
||||
String[] pathSegments = StringUtils.tokenizeToStringArray(path, "/");
|
||||
return pathSegment(pathSegments);
|
||||
}
|
||||
|
||||
private String pathAsString() {
|
||||
if (!pathSegments.isEmpty()) {
|
||||
StringBuilder pathBuilder = new StringBuilder();
|
||||
for (String pathSegment : pathSegments) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends the given path segments to the existing path of this builder. Each given path segments may contain URI
|
||||
* template variables.
|
||||
*
|
||||
* @param segments the URI path segments
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder pathSegment(String... segments) throws IllegalArgumentException {
|
||||
Assert.notNull(segments, "'segments' must not be null");
|
||||
Collections.addAll(this.pathSegments, segments);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given query parameter to the existing query parameters. The given name or any of the values may contain
|
||||
* URI template variables. If no 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 values the query parameter values
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder queryParam(String name, Object... values) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
|
||||
if (ObjectUtils.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 this;
|
||||
}
|
||||
|
||||
private String queryAsString() {
|
||||
return queryBuilder.length() != 0 ? queryBuilder.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI fragment. The given fragment may contain URI template variables, and may also be {@code null} to clear
|
||||
* the fragment of this builder.
|
||||
*
|
||||
* @param fragment the URI fragment
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
public UriComponentsBuilder fragment(String fragment) {
|
||||
if (fragment != null) {
|
||||
Assert.hasLength(fragment, "'fragment' must not be empty");
|
||||
this.fragment = fragment;
|
||||
}
|
||||
else {
|
||||
this.fragment = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,10 +21,13 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
@ -49,16 +52,17 @@ public class UriTemplate implements Serializable {
|
|||
/** Replaces template variables in the URI template. */
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
|
||||
|
||||
|
||||
private final List<String> variableNames;
|
||||
|
||||
private final Pattern matchPattern;
|
||||
|
||||
private final String uriTemplate;
|
||||
|
||||
private final UriComponents uriComponents;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@link UriTemplate} with the given URI String.
|
||||
* Construct a new {@code UriTemplate} with the given URI String.
|
||||
* @param uriTemplate the URI template string
|
||||
*/
|
||||
public UriTemplate(String uriTemplate) {
|
||||
|
|
@ -66,6 +70,16 @@ public class UriTemplate implements Serializable {
|
|||
this.uriTemplate = uriTemplate;
|
||||
this.variableNames = parser.getVariableNames();
|
||||
this.matchPattern = parser.getMatchPattern();
|
||||
this.uriComponents = UriComponents.fromUriString(uriTemplate);
|
||||
}
|
||||
|
||||
public UriTemplate(Map<UriComponents.Type, String> uriComponents) {
|
||||
this.uriComponents = UriComponents.fromUriComponentMap(uriComponents);
|
||||
String uriTemplate = this.uriComponents.toUriString();
|
||||
Parser parser = new Parser(uriTemplate);
|
||||
this.uriTemplate = uriTemplate;
|
||||
this.variableNames = parser.getVariableNames();
|
||||
this.matchPattern = parser.getMatchPattern();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +90,7 @@ public class UriTemplate implements Serializable {
|
|||
return this.variableNames;
|
||||
}
|
||||
|
||||
// expanding
|
||||
|
||||
/**
|
||||
* Given the Map of variables, expands this template into a URI. The Map keys represent variable names,
|
||||
|
|
@ -95,7 +110,8 @@ public class UriTemplate implements Serializable {
|
|||
* or if it does not contain values for all the variable names
|
||||
*/
|
||||
public URI expand(Map<String, ?> uriVariables) {
|
||||
return encodeUri(expandAsString(uriVariables));
|
||||
UriComponents expandedComponents = expandAsUriComponents(uriVariables, true);
|
||||
return expandedComponents.toUri();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -116,17 +132,69 @@ public class UriTemplate implements Serializable {
|
|||
* @throws IllegalArgumentException if <code>uriVariables</code> is <code>null</code>;
|
||||
* or if it does not contain values for all the variable names
|
||||
*/
|
||||
public String expandAsString(Map<String, ?> uriVariables) {
|
||||
public String expandAsString(final Map<String, ?> uriVariables, boolean encode) {
|
||||
UriComponents expandedComponents = expandAsUriComponents(uriVariables, encode);
|
||||
return expandedComponents.toUriString();
|
||||
}
|
||||
|
||||
public UriComponents expandAsUriComponents(final Map<String, ?> uriVariables, boolean encode) {
|
||||
Assert.notNull(uriVariables, "'uriVariables' must not be null");
|
||||
Object[] values = new Object[this.variableNames.size()];
|
||||
for (int i = 0; i < this.variableNames.size(); i++) {
|
||||
String name = this.variableNames.get(i);
|
||||
if (!uriVariables.containsKey(name)) {
|
||||
throw new IllegalArgumentException("'uriVariables' Map has no value for '" + name + "'");
|
||||
Set<String> variablesSet = new HashSet<String>(this.variableNames);
|
||||
variablesSet.removeAll(uriVariables.keySet());
|
||||
Assert.isTrue(variablesSet.isEmpty(),
|
||||
"'uriVariables' does not contain keys for all variables: " + variablesSet);
|
||||
|
||||
Map<UriComponents.Type, String> expandedComponents = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
|
||||
for (Map.Entry<UriComponents.Type, String> entry : this.uriComponents.entrySet()) {
|
||||
UriComponents.Type key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
String expandedValue = expandUriComponent(key, value, uriVariables);
|
||||
expandedComponents.put(key, expandedValue);
|
||||
}
|
||||
values[i] = uriVariables.get(name);
|
||||
UriComponents result = UriComponents.fromUriComponentMap(expandedComponents);
|
||||
if (encode) {
|
||||
result = result.encode();
|
||||
}
|
||||
return expandAsString(values);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String expandUriComponent(UriComponents.Type componentType, String value, Map<String, ?> uriVariables) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value.indexOf('{') == -1) {
|
||||
return value;
|
||||
}
|
||||
Matcher matcher = NAMES_PATTERN.matcher(value);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
String match = matcher.group(1);
|
||||
String variableName = getVariableName(match);
|
||||
Object variableValue = uriVariables.get(variableName);
|
||||
String uriVariableValueString = getVariableValueAsString(variableValue);
|
||||
String replacement = Matcher.quoteReplacement(uriVariableValueString);
|
||||
matcher.appendReplacement(sb, replacement);
|
||||
}
|
||||
matcher.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getVariableName(String match) {
|
||||
int colonIdx = match.indexOf(':');
|
||||
return colonIdx == -1 ? match : match.substring(0, colonIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that returns the string representation of the given URI template value.
|
||||
*
|
||||
* <p>Defaults implementation simply calls {@link Object#toString()}, or returns an empty string for {@code null}.
|
||||
*
|
||||
* @param variableValue the URI template variable value
|
||||
* @return the variable value as string
|
||||
*/
|
||||
protected String getVariableValueAsString(Object variableValue) {
|
||||
return variableValue != null ? variableValue.toString() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -144,7 +212,8 @@ public class UriTemplate implements Serializable {
|
|||
* or if it does not contain sufficient variables
|
||||
*/
|
||||
public URI expand(Object... uriVariableValues) {
|
||||
return encodeUri(expandAsString(uriVariableValues));
|
||||
UriComponents expandedComponents = expandAsUriComponents(uriVariableValues, true);
|
||||
return expandedComponents.toUri();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -157,43 +226,38 @@ public class UriTemplate implements Serializable {
|
|||
* </pre>
|
||||
* will print: <blockquote><code>http://example.com/hotels/1/bookings/42</code></blockquote>
|
||||
*
|
||||
*
|
||||
* @param uriVariableValues the array of URI variables
|
||||
* @return the expanded URI
|
||||
* @throws IllegalArgumentException if <code>uriVariables</code> is <code>null</code>
|
||||
* or if it does not contain sufficient variables
|
||||
*/
|
||||
public String expandAsString(Object... uriVariableValues) {
|
||||
public String expandAsString(boolean encode, Object[] uriVariableValues) {
|
||||
UriComponents expandedComponents = expandAsUriComponents(uriVariableValues, encode);
|
||||
return expandedComponents.toUriString();
|
||||
}
|
||||
|
||||
public UriComponents expandAsUriComponents(Object[] uriVariableValues, boolean encode) {
|
||||
Assert.notNull(uriVariableValues, "'uriVariableValues' must not be null");
|
||||
if (uriVariableValues.length < this.variableNames.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Not enough of variables values in [" + this.uriTemplate + "]: expected at least " +
|
||||
this.variableNames.size() + "; got " + uriVariableValues.length);
|
||||
}
|
||||
Matcher matcher = NAMES_PATTERN.matcher(this.uriTemplate);
|
||||
StringBuffer uriBuffer = new StringBuffer();
|
||||
int i = 0;
|
||||
while (matcher.find()) {
|
||||
Object uriVariableValue = uriVariableValues[i++];
|
||||
String uriVariableValueString = getVariableValueAsString(uriVariableValue);
|
||||
String replacement = Matcher.quoteReplacement(uriVariableValueString);
|
||||
matcher.appendReplacement(uriBuffer, replacement);
|
||||
}
|
||||
matcher.appendTail(uriBuffer);
|
||||
return uriBuffer.toString();
|
||||
Map<String, Object> uriVariables = new LinkedHashMap<String, Object>(this.variableNames.size());
|
||||
|
||||
for (int i = 0, size = variableNames.size(); i < size; i++) {
|
||||
String variableName = variableNames.get(i);
|
||||
Object variableValue = uriVariableValues[i];
|
||||
uriVariables.put(variableName, variableValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that returns the string representation of the given URI template value.
|
||||
*
|
||||
* <p>Defaults implementation simply calls {@link Object#toString()}, or returns an empty string for {@code null}.
|
||||
*
|
||||
* @param variableValue the URI template variable value
|
||||
* @return the variable value as string
|
||||
*/
|
||||
protected String getVariableValueAsString(Object variableValue) {
|
||||
return variableValue != null ? variableValue.toString() : "";
|
||||
return expandAsUriComponents(uriVariables, encode);
|
||||
}
|
||||
|
||||
|
||||
// matching
|
||||
|
||||
/**
|
||||
* Indicate whether the given URI matches this template.
|
||||
* @param uri the URI to match to
|
||||
|
|
@ -316,4 +380,5 @@ public class UriTemplate implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@ package org.springframework.web.util;
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
@ -44,181 +38,6 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public abstract class UriUtils {
|
||||
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
private static final String SCHEME_PATTERN = "([^:/?#]+):";
|
||||
|
||||
private static final String HTTP_PATTERN = "(http|https):";
|
||||
|
||||
private static final String USERINFO_PATTERN = "([^@/]*)";
|
||||
|
||||
private static final String HOST_PATTERN = "([^/?#:]*)";
|
||||
|
||||
private static final String PORT_PATTERN = "(\\d*)";
|
||||
|
||||
private static final String PATH_PATTERN = "([^?#]*)";
|
||||
|
||||
private static final String QUERY_PATTERN = "([^#]*)";
|
||||
|
||||
private static final String LAST_PATTERN = "(.*)";
|
||||
|
||||
// Regex patterns that matches URIs. See RFC 3986, appendix B
|
||||
private static final Pattern URI_PATTERN = Pattern.compile(
|
||||
"^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
|
||||
")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");
|
||||
|
||||
private static final Pattern HTTP_URL_PATTERN = Pattern.compile(
|
||||
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
|
||||
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
|
||||
|
||||
|
||||
// Parsing
|
||||
|
||||
/**
|
||||
* Parses the given source URI into a mapping of URI components to string values.
|
||||
*
|
||||
* @param uri the source URI
|
||||
* @return the URI components of the URI
|
||||
*/
|
||||
public static UriComponents parseUriComponents(String uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
Matcher m = URI_PATTERN.matcher(uri);
|
||||
if (m.matches()) {
|
||||
Map<UriComponents.Type, String> result = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
|
||||
result.put(UriComponents.Type.SCHEME, m.group(2));
|
||||
result.put(UriComponents.Type.AUTHORITY, m.group(3));
|
||||
result.put(UriComponents.Type.USER_INFO, m.group(5));
|
||||
result.put(UriComponents.Type.HOST, m.group(6));
|
||||
result.put(UriComponents.Type.PORT, m.group(8));
|
||||
result.put(UriComponents.Type.PATH, m.group(9));
|
||||
result.put(UriComponents.Type.QUERY, m.group(11));
|
||||
result.put(UriComponents.Type.FRAGMENT, m.group(13));
|
||||
|
||||
return new UriComponents(result);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given source HTTP URL into a mapping of URI components to string values.
|
||||
|
||||
* <p><strong>Note</strong> that the returned map will contain a mapping for
|
||||
* {@link org.springframework.web.util.UriComponents.Type#FRAGMENT}, as fragments are not supposed to be sent to the
|
||||
* server, but retained by the client.
|
||||
*
|
||||
* @param httpUrl the source URI
|
||||
* @return the URI components of the URI
|
||||
*/
|
||||
public static UriComponents parseHttpUrlComponents(String httpUrl) {
|
||||
Assert.notNull(httpUrl, "'httpUrl' must not be null");
|
||||
Matcher m = HTTP_URL_PATTERN.matcher(httpUrl);
|
||||
if (m.matches()) {
|
||||
Map<UriComponents.Type, String> result = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
|
||||
result.put(UriComponents.Type.SCHEME, m.group(1));
|
||||
result.put(UriComponents.Type.AUTHORITY, m.group(2));
|
||||
result.put(UriComponents.Type.USER_INFO, m.group(4));
|
||||
result.put(UriComponents.Type.HOST, m.group(5));
|
||||
result.put(UriComponents.Type.PORT, m.group(7));
|
||||
result.put(UriComponents.Type.PATH, m.group(8));
|
||||
result.put(UriComponents.Type.QUERY, m.group(10));
|
||||
|
||||
return new UriComponents(result);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL");
|
||||
}
|
||||
}
|
||||
|
||||
// building
|
||||
|
||||
/**
|
||||
* Builds a URI from the given URI components. The given map should contain at least one entry.
|
||||
*
|
||||
* <p><strong>Note</strong> that {@link org.springframework.web.util.UriComponents.Type#PATH_SEGMENT} and {@link org.springframework.web.util.UriComponents.Type#QUERY_PARAM} keys (if any)
|
||||
* will not be used to build the URI, in favor of {@link org.springframework.web.util.UriComponents.Type#PATH} and {@link org.springframework.web.util.UriComponents.Type#QUERY}
|
||||
* respectively.
|
||||
*
|
||||
* @param uriComponents the components to build the URI out of
|
||||
* @return the URI created from the given components
|
||||
*/
|
||||
public static String buildUri(Map<UriComponents.Type, String> uriComponents) {
|
||||
Assert.notEmpty(uriComponents, "'uriComponents' must not be empty");
|
||||
|
||||
return buildUri(uriComponents.get(UriComponents.Type.SCHEME), uriComponents.get(
|
||||
UriComponents.Type.AUTHORITY),
|
||||
uriComponents.get(UriComponents.Type.USER_INFO), uriComponents.get(UriComponents.Type.HOST),
|
||||
uriComponents.get(UriComponents.Type.PORT), uriComponents.get(UriComponents.Type.PATH),
|
||||
uriComponents.get(UriComponents.Type.QUERY), uriComponents.get(UriComponents.Type.FRAGMENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI from the given URI component parameters. All parameters can be {@code null}.
|
||||
*
|
||||
* @param scheme the scheme
|
||||
* @param authority the authority
|
||||
* @param userinfo the user info
|
||||
* @param host the host
|
||||
* @param port the port
|
||||
* @param path the path
|
||||
* @param query the query
|
||||
* @param fragment the fragment
|
||||
* @return the URI created from the given components
|
||||
*/
|
||||
public static String buildUri(String scheme,
|
||||
String authority,
|
||||
String userinfo,
|
||||
String host,
|
||||
String port,
|
||||
String path,
|
||||
String query,
|
||||
String fragment) {
|
||||
StringBuilder uriBuilder = new StringBuilder();
|
||||
|
||||
if (scheme != null) {
|
||||
uriBuilder.append(scheme);
|
||||
uriBuilder.append(':');
|
||||
}
|
||||
|
||||
if (userinfo != null || host != null || port != null) {
|
||||
uriBuilder.append("//");
|
||||
if (userinfo != null) {
|
||||
uriBuilder.append(userinfo);
|
||||
uriBuilder.append('@');
|
||||
}
|
||||
if (host != null) {
|
||||
uriBuilder.append(host);
|
||||
}
|
||||
if (port != null) {
|
||||
uriBuilder.append(':');
|
||||
uriBuilder.append(port);
|
||||
}
|
||||
}
|
||||
else if (authority != null) {
|
||||
uriBuilder.append("//");
|
||||
uriBuilder.append(authority);
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
uriBuilder.append(path);
|
||||
}
|
||||
|
||||
if (query != null) {
|
||||
uriBuilder.append('?');
|
||||
uriBuilder.append(query);
|
||||
}
|
||||
|
||||
if (fragment != null) {
|
||||
uriBuilder.append('#');
|
||||
uriBuilder.append(fragment);
|
||||
}
|
||||
|
||||
return uriBuilder.toString();
|
||||
}
|
||||
|
||||
// encoding
|
||||
|
||||
/**
|
||||
|
|
@ -232,8 +51,9 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException {
|
||||
Map<UriComponents.Type, String> uriComponents = parseUriComponents(uri);
|
||||
return encodeUriComponents(uriComponents, encoding);
|
||||
UriComponents uriComponents = UriComponents.fromUriString(uri);
|
||||
UriComponents encoded = uriComponents.encode(encoding);
|
||||
return encoded.toUriString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -248,33 +68,9 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException {
|
||||
Map<UriComponents.Type, String> uriComponents = parseHttpUrlComponents(httpUrl);
|
||||
return encodeUriComponents(uriComponents, encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given source URI components into an encoded String. All various URI components are optional, but encoded
|
||||
* according to their respective valid character sets.
|
||||
*
|
||||
* @param uriComponents the URI components
|
||||
* @param encoding the character encoding to encode to
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeUriComponents(Map<UriComponents.Type, String> uriComponents,
|
||||
String encoding) throws UnsupportedEncodingException {
|
||||
Assert.notEmpty(uriComponents, "'uriComponents' must not be empty");
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
|
||||
Map<UriComponents.Type, String> encodedUriComponents = new EnumMap<UriComponents.Type, String>(UriComponents.Type.class);
|
||||
for (Map.Entry<UriComponents.Type, String> entry : uriComponents.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
String encodedValue = encodeUriComponent(entry.getValue(), encoding, entry.getKey(), null);
|
||||
encodedUriComponents.put(entry.getKey(), encodedValue);
|
||||
}
|
||||
}
|
||||
return buildUri(encodedUriComponents);
|
||||
UriComponents uriComponents = UriComponents.fromHttpUrl(httpUrl);
|
||||
UriComponents encoded = uriComponents.encode(encoding);
|
||||
return encoded.toUriString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -303,141 +99,10 @@ public abstract class UriUtils {
|
|||
String query,
|
||||
String fragment,
|
||||
String encoding) throws UnsupportedEncodingException {
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
UriComponents uriComponents = UriComponents.fromUriComponents(scheme, authority, userInfo, host, port, path, query, fragment);
|
||||
UriComponents encoded = uriComponents.encode(encoding);
|
||||
|
||||
if (scheme != null) {
|
||||
scheme = encodeScheme(scheme, encoding);
|
||||
}
|
||||
if (authority != null) {
|
||||
authority = encodeAuthority(authority, encoding);
|
||||
}
|
||||
if (userInfo != null) {
|
||||
userInfo = encodeUserInfo(userInfo, encoding);
|
||||
}
|
||||
if (host != null) {
|
||||
host = encodeHost(host, encoding);
|
||||
}
|
||||
if (port != null) {
|
||||
port = encodePort(port, encoding);
|
||||
}
|
||||
if (path != null) {
|
||||
path = encodePath(path, encoding);
|
||||
}
|
||||
if (query != null) {
|
||||
query = encodeQuery(query, encoding);
|
||||
}
|
||||
if (fragment != null) {
|
||||
fragment = encodeFragment(fragment, encoding);
|
||||
}
|
||||
return buildUri(scheme, authority, userInfo, host, port, path, query, fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given source into an encoded String using the rules specified by the given component.
|
||||
*
|
||||
* @param source the source string
|
||||
* @param uriComponent the URI component for the source
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
*/
|
||||
public static String encodeUriComponent(String source, UriComponents.Type uriComponent) {
|
||||
return encodeUriComponent(source, uriComponent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 uriComponent the URI component for the source
|
||||
* @param encodingOptions the options used when encoding. May be {@code null}.
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
* @see EncodingOption
|
||||
*/
|
||||
public static String encodeUriComponent(String source,
|
||||
UriComponents.Type uriComponent,
|
||||
Set<EncodingOption> encodingOptions) {
|
||||
try {
|
||||
return encodeUriComponent(source, DEFAULT_ENCODING, uriComponent, encodingOptions);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new InternalError("\"" + DEFAULT_ENCODING + "\" not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes the given source into an encoded String using the rules specified by the given component.
|
||||
*
|
||||
* @param source the source string
|
||||
* @param encoding the encoding of the source string
|
||||
* @param uriComponent the URI component for the source
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
*/
|
||||
public static String encodeUriComponent(String source,
|
||||
String encoding,
|
||||
UriComponents.Type uriComponent) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(source, encoding, uriComponent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 uriComponent the URI component for the source
|
||||
* @param encodingOptions the options used when encoding. May be {@code null}.
|
||||
* @return the encoded URI
|
||||
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
|
||||
* @see EncodingOption
|
||||
*/
|
||||
public static String encodeUriComponent(String source,
|
||||
String encoding,
|
||||
UriComponents.Type uriComponent,
|
||||
Set<EncodingOption> encodingOptions) throws UnsupportedEncodingException {
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
|
||||
byte[] bytes = encodeInternal(source.getBytes(encoding), uriComponent, encodingOptions);
|
||||
return new String(bytes, "US-ASCII");
|
||||
}
|
||||
|
||||
private static byte[] encodeInternal(byte[] source,
|
||||
UriComponents.Type uriComponent,
|
||||
Set<EncodingOption> encodingOptions) {
|
||||
Assert.notNull(source, "'source' must not be null");
|
||||
Assert.notNull(uriComponent, "'uriComponent' must not be null");
|
||||
|
||||
if (encodingOptions == null) {
|
||||
encodingOptions = Collections.emptySet();
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(source.length);
|
||||
for (int i = 0; i < source.length; i++) {
|
||||
int b = source[i];
|
||||
if (b < 0) {
|
||||
b += 256;
|
||||
}
|
||||
if (uriComponent.isAllowed(b)) {
|
||||
bos.write(b);
|
||||
}
|
||||
else if (encodingOptions.contains(EncodingOption.ALLOW_TEMPLATE_VARS) && (b == '{' || 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();
|
||||
return encoded.toUriString();
|
||||
}
|
||||
|
||||
// encoding convenience methods
|
||||
|
|
@ -451,7 +116,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeScheme(String scheme, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(scheme, encoding, UriComponents.Type.SCHEME, null);
|
||||
return UriComponents.encodeUriComponent(scheme, encoding, UriComponents.Type.SCHEME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -463,7 +128,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeAuthority(String authority, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(authority, encoding, UriComponents.Type.AUTHORITY, null);
|
||||
return UriComponents.encodeUriComponent(authority, encoding, UriComponents.Type.AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -475,7 +140,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeUserInfo(String userInfo, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(userInfo, encoding, UriComponents.Type.USER_INFO, null);
|
||||
return UriComponents.encodeUriComponent(userInfo, encoding, UriComponents.Type.USER_INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -487,7 +152,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeHost(String host, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(host, encoding, UriComponents.Type.HOST, null);
|
||||
return UriComponents.encodeUriComponent(host, encoding, UriComponents.Type.HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -499,7 +164,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodePort(String port, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(port, encoding, UriComponents.Type.PORT, null);
|
||||
return UriComponents.encodeUriComponent(port, encoding, UriComponents.Type.PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -511,7 +176,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodePath(String path, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(path, encoding, UriComponents.Type.PATH, null);
|
||||
return UriComponents.encodeUriComponent(path, encoding, UriComponents.Type.PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -523,7 +188,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodePathSegment(String segment, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(segment, encoding, UriComponents.Type.PATH_SEGMENT, null);
|
||||
return UriComponents.encodeUriComponent(segment, encoding, UriComponents.Type.PATH_SEGMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -535,7 +200,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeQuery(String query, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(query, encoding, UriComponents.Type.QUERY, null);
|
||||
return UriComponents.encodeUriComponent(query, encoding, UriComponents.Type.QUERY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -547,7 +212,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeQueryParam(String queryParam, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(queryParam, encoding, UriComponents.Type.QUERY_PARAM, null);
|
||||
return UriComponents.encodeUriComponent(queryParam, encoding, UriComponents.Type.QUERY_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -559,7 +224,7 @@ public abstract class UriUtils {
|
|||
* @throws UnsupportedEncodingException when the given encoding parameter is not supported
|
||||
*/
|
||||
public static String encodeFragment(String fragment, String encoding) throws UnsupportedEncodingException {
|
||||
return encodeUriComponent(fragment, encoding, UriComponents.Type.FRAGMENT, null);
|
||||
return UriComponents.encodeUriComponent(fragment, encoding, UriComponents.Type.FRAGMENT);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -609,16 +274,4 @@ public abstract class UriUtils {
|
|||
return changed ? new String(bos.toByteArray(), encoding) : source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration used to control how URIs are encoded.
|
||||
*/
|
||||
public enum EncodingOption {
|
||||
|
||||
/**
|
||||
* Allow for URI template variables to occur in the URI component (i.e. '{foo}')
|
||||
*/
|
||||
ALLOW_TEMPLATE_VARS
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class UriBuilderTests {
|
||||
|
||||
@Test
|
||||
public void plain() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.scheme("http").host("example.com").path("foo").queryParam("bar").fragment("baz").build();
|
||||
|
||||
URI expected = new URI("http://example.com/foo?bar#baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromPath() throws URISyntaxException {
|
||||
URI result = UriBuilder.fromPath("foo").queryParam("bar").fragment("baz").build();
|
||||
|
||||
URI expected = new URI("/foo?bar#baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromUri() throws URISyntaxException {
|
||||
URI uri = new URI("http://example.com/foo?bar#baz");
|
||||
|
||||
URI result = UriBuilder.fromUri(uri).build();
|
||||
assertEquals("Invalid result URI", uri, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Working on it")
|
||||
public void templateVarsVarArgs() throws URISyntaxException {
|
||||
URI result = UriBuilder.fromPath("/{foo}/{bar}").build("baz", "qux");
|
||||
|
||||
URI expected = new URI("http://example.com/baz/qux");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateVarsEncoded() throws URISyntaxException, UnsupportedEncodingException {
|
||||
URI result = UriBuilder.fromPath("{foo}").build("bar baz");
|
||||
|
||||
URI expected = new URI("/bar%20baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateVarsNotEncoded() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.scheme("http").host("example.com").path("{foo}").buildFromEncoded("bar%20baz");
|
||||
|
||||
URI expected = new URI("http://example.com/bar%20baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void templateVarsMap() throws URISyntaxException {
|
||||
Map<String, String> vars = new HashMap<String, String>(2);
|
||||
vars.put("bar", "qux");
|
||||
vars.put("foo", "baz");
|
||||
URI result = UriBuilder.fromPath("/{foo}/{bar}").build(vars);
|
||||
URI expected = new URI("/baz/qux");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unusedTemplateVars() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.scheme("http").host("example.com").path("{foo}").build();
|
||||
|
||||
URI expected = new URI("http://example.com/%7Bfoo%7D");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathSegments() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.pathSegment("foo").pathSegment("bar").build();
|
||||
|
||||
URI expected = new URI("/foo/bar");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParam() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.queryParam("baz", "qux", 42).build();
|
||||
|
||||
URI expected = new URI("?baz=qux&baz=42");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyQueryParam() throws URISyntaxException {
|
||||
UriBuilder builder = UriBuilder.newInstance();
|
||||
URI result = builder.queryParam("baz").build();
|
||||
|
||||
URI expected = new URI("?baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class UriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void plain() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
UriComponents result = builder.scheme("http").host("example.com").path("foo").queryParam("bar").fragment("baz").build();
|
||||
assertEquals("http", result.getScheme());
|
||||
assertEquals("example.com", result.getHost());
|
||||
assertEquals("/foo", result.getPath());
|
||||
assertEquals("bar", result.getQuery());
|
||||
assertEquals("baz", result.getFragment());
|
||||
|
||||
URI expected = new URI("http://example.com/foo?bar#baz");
|
||||
assertEquals("Invalid result URI", expected, result.toUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromPath() throws URISyntaxException {
|
||||
UriComponents result = UriComponentsBuilder.fromPath("foo").queryParam("bar").fragment("baz").build();
|
||||
assertEquals("/foo", result.getPath());
|
||||
assertEquals("bar", result.getQuery());
|
||||
assertEquals("baz", result.getFragment());
|
||||
|
||||
URI expected = new URI("/foo?bar#baz");
|
||||
assertEquals("Invalid result URI", expected, result.toUri());
|
||||
|
||||
result = UriComponentsBuilder.fromPath("/foo").build();
|
||||
assertEquals("/foo", result.getPath());
|
||||
|
||||
expected = new URI("/foo");
|
||||
assertEquals("Invalid result URI", expected, result.toUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromUri() throws URISyntaxException {
|
||||
URI uri = new URI("http://example.com/foo?bar#baz");
|
||||
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
||||
assertEquals("http", result.getScheme());
|
||||
assertEquals("example.com", result.getHost());
|
||||
assertEquals("/foo", result.getPath());
|
||||
assertEquals("bar", result.getQuery());
|
||||
assertEquals("baz", result.getFragment());
|
||||
|
||||
assertEquals("Invalid result URI", uri, result.toUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathSegments() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
URI result = builder.pathSegment("foo").pathSegment("bar").build().toUri();
|
||||
|
||||
URI expected = new URI("/foo/bar");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParam() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
URI result = builder.queryParam("baz", "qux", 42).build().toUri();
|
||||
|
||||
URI expected = new URI("?baz=qux&baz=42");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyQueryParam() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
URI result = builder.queryParam("baz").build().toUri();
|
||||
|
||||
URI expected = new URI("?baz");
|
||||
assertEquals("Invalid result URI", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineWithUriTemplate() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromPath("/{foo}");
|
||||
UriComponents components = builder.build();
|
||||
UriTemplate template = new UriTemplate(components);
|
||||
URI uri = template.expand("bar baz");
|
||||
assertEquals(new URI("/bar%20baz"), uri);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -16,58 +16,105 @@
|
|||
|
||||
package org.springframework.web.util;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class UriComponentsTests {
|
||||
|
||||
private UriComponents components;
|
||||
@Test
|
||||
public void fromUri() {
|
||||
Map<UriComponents.Type, String> result = UriComponents.fromUriString("http://www.ietf.org/rfc/rfc3986.txt");
|
||||
assertEquals("http", result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertEquals("www.ietf.org", result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("/rfc/rfc3986.txt", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertNull(result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
@Before
|
||||
public void createComponents() {
|
||||
components = new UriComponents();
|
||||
result = UriComponents.fromUriString(
|
||||
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar#and(java.util.BitSet)");
|
||||
assertEquals("http", result.get(UriComponents.Type.SCHEME));
|
||||
assertEquals("arjen:foobar", result.get(UriComponents.Type.USER_INFO));
|
||||
assertEquals("java.sun.com", result.get(UriComponents.Type.HOST));
|
||||
assertEquals("80", result.get(UriComponents.Type.PORT));
|
||||
assertEquals("/javase/6/docs/api/java/util/BitSet.html", result.get(UriComponents.Type.PATH));
|
||||
assertEquals("foo=bar", result.get(UriComponents.Type.QUERY));
|
||||
assertEquals("and(java.util.BitSet)", result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
result = UriComponents.fromUriString("mailto:java-net@java.sun.com");
|
||||
assertEquals("mailto", result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertNull(result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("java-net@java.sun.com", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertNull(result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
result = UriComponents.fromUriString("docs/guide/collections/designfaq.html#28");
|
||||
assertNull(result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertNull(result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("docs/guide/collections/designfaq.html", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertEquals("28", result.get(UriComponents.Type.FRAGMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathSegments() {
|
||||
String path = "/foo/bar";
|
||||
components.setPath(path);
|
||||
UriComponents components = UriComponents.fromUriComponentMap(Collections.singletonMap(UriComponents.Type.PATH, path));
|
||||
List<String> expected = Arrays.asList("foo", "bar");
|
||||
|
||||
List<String> pathSegments = components.getPathSegments();
|
||||
assertEquals(expected, pathSegments);
|
||||
|
||||
components.setPath(null);
|
||||
|
||||
components.setPathSegments(expected);
|
||||
assertEquals(path, components.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParams() {
|
||||
String query = "foo=bar&foo=baz&qux";
|
||||
components.setQuery(query);
|
||||
UriComponents components = UriComponents.fromUriComponentMap(
|
||||
Collections.singletonMap(UriComponents.Type.QUERY, query));
|
||||
MultiValueMap<String, String> expected = new LinkedMultiValueMap<String, String>(1);
|
||||
expected.put("foo", Arrays.asList("bar", "baz"));
|
||||
expected.set("qux", null);
|
||||
|
||||
MultiValueMap<String, String> result = components.getQueryParams();
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
components.setQuery(null);
|
||||
@Test
|
||||
public void encode() {
|
||||
UriComponents uriComponents = UriComponents.fromUriString("http://example.com/hotel list");
|
||||
UriComponents encoded = uriComponents.encode();
|
||||
assertEquals("/hotel%20list", encoded.getPath());
|
||||
}
|
||||
|
||||
components.setQueryParams(expected);
|
||||
assertEquals(query, components.getQuery());
|
||||
@Test
|
||||
public void toUriEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponents.fromUriString("http://example.com/hotel list/Z\u00fcrich");
|
||||
UriComponents encoded = uriComponents.encode();
|
||||
assertEquals(new URI("http://example.com/hotel%20list/Z%C3%BCrich"), encoded.toUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUriNotEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponents.fromUriString("http://example.com/hotel list/Z\u00fcrich");
|
||||
assertEquals(new URI("http://example.com/hotel%20list/Z\u00fcrich"), uriComponents.toUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,14 +53,6 @@ public class UriTemplateTests {
|
|||
template.expand("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandMapDuplicateVariables() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/order/{c}/{c}/{c}");
|
||||
assertEquals("Invalid variable names", Arrays.asList("c", "c", "c"), template.getVariableNames());
|
||||
URI result = template.expand(Collections.singletonMap("c", "cheeseburger"));
|
||||
assertEquals("Invalid expanded template", new URI("/order/cheeseburger/cheeseburger/cheeseburger"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandMap() throws Exception {
|
||||
Map<String, String> uriVariables = new HashMap<String, String>(2);
|
||||
|
|
@ -71,6 +63,14 @@ public class UriTemplateTests {
|
|||
assertEquals("Invalid expanded template", new URI("http://example.com/hotels/1/bookings/42"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandMapDuplicateVariables() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/order/{c}/{c}/{c}");
|
||||
assertEquals("Invalid variable names", Arrays.asList("c", "c", "c"), template.getVariableNames());
|
||||
URI result = template.expand(Collections.singletonMap("c", "cheeseburger"));
|
||||
assertEquals("Invalid expanded template", new URI("/order/cheeseburger/cheeseburger/cheeseburger"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandMapNonString() throws Exception {
|
||||
Map<String, Integer> uriVariables = new HashMap<String, Integer>(2);
|
||||
|
|
@ -81,6 +81,15 @@ public class UriTemplateTests {
|
|||
assertEquals("Invalid expanded template", new URI("http://example.com/hotels/1/bookings/42"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandMapEncoded() throws Exception {
|
||||
Map<String, String> uriVariables = Collections.singletonMap("hotel", "Z\u00fcrich");
|
||||
UriTemplate template = new UriTemplate("http://example.com/hotel list/{hotel}");
|
||||
URI result = template.expand(uriVariables);
|
||||
assertEquals("Invalid expanded template", new URI("http://example.com/hotel%20list/Z%C3%BCrich"), result);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void expandMapInvalidAmountVariables() throws Exception {
|
||||
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
|
||||
|
|
|
|||
|
|
@ -17,11 +17,10 @@
|
|||
package org.springframework.web.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
|
|
@ -31,45 +30,6 @@ public class UriUtilsTests {
|
|||
private static final String ENC = "UTF-8";
|
||||
|
||||
|
||||
@Test
|
||||
public void parseUriComponents() {
|
||||
Map<UriComponents.Type, String> result = UriUtils.parseUriComponents("http://www.ietf.org/rfc/rfc3986.txt");
|
||||
assertEquals("http", result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertEquals("www.ietf.org", result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("/rfc/rfc3986.txt", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertNull(result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
result = UriUtils.parseUriComponents(
|
||||
"http://arjen:foobar@java.sun.com:80/javase/6/docs/api/java/util/BitSet.html?foo=bar#and(java.util.BitSet)");
|
||||
assertEquals("http", result.get(UriComponents.Type.SCHEME));
|
||||
assertEquals("arjen:foobar", result.get(UriComponents.Type.USER_INFO));
|
||||
assertEquals("java.sun.com", result.get(UriComponents.Type.HOST));
|
||||
assertEquals("80", result.get(UriComponents.Type.PORT));
|
||||
assertEquals("/javase/6/docs/api/java/util/BitSet.html", result.get(UriComponents.Type.PATH));
|
||||
assertEquals("foo=bar", result.get(UriComponents.Type.QUERY));
|
||||
assertEquals("and(java.util.BitSet)", result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
result = UriUtils.parseUriComponents("mailto:java-net@java.sun.com");
|
||||
assertEquals("mailto", result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertNull(result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("java-net@java.sun.com", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertNull(result.get(UriComponents.Type.FRAGMENT));
|
||||
|
||||
result = UriUtils.parseUriComponents("docs/guide/collections/designfaq.html#28");
|
||||
assertNull(result.get(UriComponents.Type.SCHEME));
|
||||
assertNull(result.get(UriComponents.Type.USER_INFO));
|
||||
assertNull(result.get(UriComponents.Type.HOST));
|
||||
assertNull(result.get(UriComponents.Type.PORT));
|
||||
assertEquals("docs/guide/collections/designfaq.html", result.get(UriComponents.Type.PATH));
|
||||
assertNull(result.get(UriComponents.Type.QUERY));
|
||||
assertEquals("28", result.get(UriComponents.Type.FRAGMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeScheme() throws UnsupportedEncodingException {
|
||||
|
|
|
|||
Loading…
Reference in New Issue