Add ability to clone to UriComponentsBuilder hierarchy
Issue: SPR-12494
This commit is contained in:
parent
2fccf3ff44
commit
189ec75789
|
@ -54,7 +54,7 @@ import org.springframework.web.util.HierarchicalUriComponents.PathComponent;
|
|||
* @see #fromPath(String)
|
||||
* @see #fromUri(URI)
|
||||
*/
|
||||
public class UriComponentsBuilder {
|
||||
public class UriComponentsBuilder implements Cloneable {
|
||||
|
||||
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");
|
||||
|
||||
|
@ -98,7 +98,7 @@ public class UriComponentsBuilder {
|
|||
|
||||
private String port;
|
||||
|
||||
private CompositePathComponentBuilder pathBuilder = new CompositePathComponentBuilder();
|
||||
private CompositePathComponentBuilder pathBuilder;
|
||||
|
||||
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
|
@ -112,6 +112,22 @@ public class UriComponentsBuilder {
|
|||
* @see #fromUri(URI)
|
||||
*/
|
||||
protected UriComponentsBuilder() {
|
||||
this.pathBuilder = new CompositePathComponentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the given UriComponentsBuilder.
|
||||
* @param other the other builder to copy from
|
||||
*/
|
||||
protected UriComponentsBuilder(UriComponentsBuilder other) {
|
||||
this.scheme = other.scheme;
|
||||
this.ssp = other.ssp;
|
||||
this.userInfo = other.userInfo;
|
||||
this.host = other.host;
|
||||
this.port = other.port;
|
||||
this.pathBuilder = (CompositePathComponentBuilder) other.pathBuilder.clone();
|
||||
this.queryParams.putAll(other.queryParams);
|
||||
this.fragment = other.fragment;
|
||||
}
|
||||
|
||||
|
||||
|
@ -627,16 +643,23 @@ public class UriComponentsBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new UriComponentsBuilder(this);
|
||||
}
|
||||
|
||||
private interface PathComponentBuilder {
|
||||
|
||||
private interface PathComponentBuilder extends Cloneable {
|
||||
|
||||
PathComponent build();
|
||||
|
||||
Object clone();
|
||||
}
|
||||
|
||||
|
||||
private static class CompositePathComponentBuilder implements PathComponentBuilder {
|
||||
|
||||
private final LinkedList<PathComponentBuilder> componentBuilders = new LinkedList<PathComponentBuilder>();
|
||||
private final LinkedList<PathComponentBuilder> builders = new LinkedList<PathComponentBuilder>();
|
||||
|
||||
public CompositePathComponentBuilder() {
|
||||
}
|
||||
|
@ -651,7 +674,7 @@ public class UriComponentsBuilder {
|
|||
FullPathComponentBuilder fpBuilder = getLastBuilder(FullPathComponentBuilder.class);
|
||||
if (psBuilder == null) {
|
||||
psBuilder = new PathSegmentComponentBuilder();
|
||||
this.componentBuilders.add(psBuilder);
|
||||
this.builders.add(psBuilder);
|
||||
if (fpBuilder != null) {
|
||||
fpBuilder.removeTrailingSlash();
|
||||
}
|
||||
|
@ -669,7 +692,7 @@ public class UriComponentsBuilder {
|
|||
}
|
||||
if (fpBuilder == null) {
|
||||
fpBuilder = new FullPathComponentBuilder();
|
||||
this.componentBuilders.add(fpBuilder);
|
||||
this.builders.add(fpBuilder);
|
||||
}
|
||||
fpBuilder.append(path);
|
||||
}
|
||||
|
@ -677,8 +700,8 @@ public class UriComponentsBuilder {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getLastBuilder(Class<T> builderClass) {
|
||||
if (!this.componentBuilders.isEmpty()) {
|
||||
PathComponentBuilder last = this.componentBuilders.getLast();
|
||||
if (!this.builders.isEmpty()) {
|
||||
PathComponentBuilder last = this.builders.getLast();
|
||||
if (builderClass.isInstance(last)) {
|
||||
return (T) last;
|
||||
}
|
||||
|
@ -688,9 +711,9 @@ public class UriComponentsBuilder {
|
|||
|
||||
@Override
|
||||
public PathComponent build() {
|
||||
int size = this.componentBuilders.size();
|
||||
int size = this.builders.size();
|
||||
List<PathComponent> components = new ArrayList<PathComponent>(size);
|
||||
for (PathComponentBuilder componentBuilder : this.componentBuilders) {
|
||||
for (PathComponentBuilder componentBuilder : this.builders) {
|
||||
PathComponent pathComponent = componentBuilder.build();
|
||||
if (pathComponent != null) {
|
||||
components.add(pathComponent);
|
||||
|
@ -704,6 +727,15 @@ public class UriComponentsBuilder {
|
|||
}
|
||||
return new HierarchicalUriComponents.PathComponentComposite(components);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
CompositePathComponentBuilder compositeBuilder = new CompositePathComponentBuilder();
|
||||
for (PathComponentBuilder builder : this.builders) {
|
||||
compositeBuilder.builders.add((PathComponentBuilder) builder.clone());
|
||||
}
|
||||
return compositeBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -737,6 +769,13 @@ public class UriComponentsBuilder {
|
|||
this.path.deleteCharAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
FullPathComponentBuilder builder = new FullPathComponentBuilder();
|
||||
builder.append(this.path.toString());
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -757,6 +796,13 @@ public class UriComponentsBuilder {
|
|||
return (this.pathSegments.isEmpty() ? null :
|
||||
new HierarchicalUriComponents.PathSegmentComponent(this.pathSegments));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PathSegmentComponentBuilder builder = new PathSegmentComponentBuilder();
|
||||
builder.pathSegments.addAll(this.pathSegments);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,14 @@
|
|||
|
||||
package org.springframework.web.util;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
|
@ -23,14 +31,10 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Phillip Webb
|
||||
|
@ -436,4 +440,28 @@ public class UriComponentsBuilderTests {
|
|||
assertThat(components.getFragment(), is(nullValue()));
|
||||
assertThat(components.toString(), equalTo("/example"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClone() throws URISyntaxException {
|
||||
UriComponentsBuilder builder1 = UriComponentsBuilder.newInstance();
|
||||
builder1.scheme("http").host("e1.com").path("/p1").pathSegment("ps1").queryParam("q1").fragment("f1");
|
||||
|
||||
UriComponentsBuilder builder2 = (UriComponentsBuilder) builder1.clone();
|
||||
builder2.scheme("https").host("e2.com").path("p2").pathSegment("ps2").queryParam("q2").fragment("f2");
|
||||
|
||||
UriComponents result1 = builder1.build();
|
||||
assertEquals("http", result1.getScheme());
|
||||
assertEquals("e1.com", result1.getHost());
|
||||
assertEquals("/p1/ps1", result1.getPath());
|
||||
assertEquals("q1", result1.getQuery());
|
||||
assertEquals("f1", result1.getFragment());
|
||||
|
||||
UriComponents result2 = builder2.build();
|
||||
assertEquals("https", result2.getScheme());
|
||||
assertEquals("e2.com", result2.getHost());
|
||||
assertEquals("/p1/ps1/p2/ps2", result2.getPath());
|
||||
assertEquals("q1&q2", result2.getQuery());
|
||||
assertEquals("f2", result2.getFragment());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,6 +93,26 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor. Protected to prevent direct instantiation.
|
||||
*
|
||||
* @see #fromController(Class)
|
||||
* @see #fromMethodName(Class, String, Object...)
|
||||
* @see #fromMethodCall(Object)
|
||||
* @see #fromMappingName(String)
|
||||
* @see #fromMethod(java.lang.reflect.Method, Object...)
|
||||
*/
|
||||
protected MvcUriComponentsBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the given MvcUriComponentsBuilder.
|
||||
* @param other the other builder to copy from
|
||||
*/
|
||||
protected MvcUriComponentsBuilder(MvcUriComponentsBuilder other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponentsBuilder} from the mapping of a controller class
|
||||
* and current request information including Servlet mapping. If the controller
|
||||
|
@ -431,6 +451,11 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new MvcUriComponentsBuilder(this);
|
||||
}
|
||||
|
||||
|
||||
private static class ControllerMethodInvocationInterceptor
|
||||
implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {
|
||||
|
|
|
@ -51,6 +51,15 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
|||
protected ServletUriComponentsBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy of the given ServletUriComponentsBuilder.
|
||||
* @param other the other builder to copy from
|
||||
*/
|
||||
protected ServletUriComponentsBuilder(ServletUriComponentsBuilder other) {
|
||||
super(other);
|
||||
this.originalPath = other.originalPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a builder from the host, port, scheme, and context path of the
|
||||
* given HttpServletRequest.
|
||||
|
@ -232,4 +241,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
|||
return extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new ServletUriComponentsBuilder(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue