Refine PathContainer.Segment value representation
Segment.value() now returns the actual original path segment value including path parameters while semicolonContent() is removed. valueDecoded() is renamed to valueToMatch() to reflect it is the value for pattern matching which is not only decoded but also has path parameters removed.
This commit is contained in:
parent
2f17c5f3b6
commit
1d201a55db
|
@ -119,14 +119,15 @@ class DefaultPathContainer implements PathContainer {
|
|||
private static PathContainer.Segment parsePathSegment(String input, Charset charset) {
|
||||
int index = input.indexOf(';');
|
||||
if (index == -1) {
|
||||
String inputDecoded = StringUtils.uriDecode(input, charset);
|
||||
return new DefaultPathSegment(input, inputDecoded, "", EMPTY_MAP);
|
||||
String valueToMatch = StringUtils.uriDecode(input, charset);
|
||||
return new DefaultPathSegment(input, valueToMatch, EMPTY_MAP);
|
||||
}
|
||||
else {
|
||||
String valueToMatch = StringUtils.uriDecode(input.substring(0, index), charset);
|
||||
String pathParameterContent = input.substring(index);
|
||||
MultiValueMap<String, String> parameters = parseParams(pathParameterContent, charset);
|
||||
return new DefaultPathSegment(input, valueToMatch, parameters);
|
||||
}
|
||||
String value = input.substring(0, index);
|
||||
String valueDecoded = StringUtils.uriDecode(value, charset);
|
||||
String semicolonContent = input.substring(index);
|
||||
MultiValueMap<String, String> parameters = parseParams(semicolonContent, charset);
|
||||
return new DefaultPathSegment(value, valueDecoded, semicolonContent, parameters);
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> parseParams(String input, Charset charset) {
|
||||
|
@ -189,22 +190,17 @@ class DefaultPathContainer implements PathContainer {
|
|||
|
||||
private final String value;
|
||||
|
||||
private final String valueDecoded;
|
||||
private final String valueToMatch;
|
||||
|
||||
private final char[] valueDecodedChars;
|
||||
|
||||
private final String semicolonContent;
|
||||
private final char[] valueToMatchAsChars;
|
||||
|
||||
private final MultiValueMap<String, String> parameters;
|
||||
|
||||
DefaultPathSegment(String value, String valueDecoded, String semicolonContent,
|
||||
MultiValueMap<String, String> params) {
|
||||
|
||||
DefaultPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
|
||||
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
|
||||
this.value = value;
|
||||
this.valueDecoded = valueDecoded;
|
||||
this.valueDecodedChars = valueDecoded.toCharArray();
|
||||
this.semicolonContent = semicolonContent;
|
||||
this.valueToMatch = valueToMatch;
|
||||
this.valueToMatchAsChars = valueToMatch.toCharArray();
|
||||
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
|
||||
}
|
||||
|
||||
|
@ -214,18 +210,13 @@ class DefaultPathContainer implements PathContainer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String valueDecoded() {
|
||||
return this.valueDecoded;
|
||||
public String valueToMatch() {
|
||||
return this.valueToMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] valueDecodedChars() {
|
||||
return this.valueDecodedChars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String semicolonContent() {
|
||||
return this.semicolonContent;
|
||||
public char[] valueToMatchAsChars() {
|
||||
return this.valueToMatchAsChars;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -241,26 +232,16 @@ class DefaultPathContainer implements PathContainer {
|
|||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultPathSegment segment = (DefaultPathSegment) other;
|
||||
return (this.value.equals(segment.value) &&
|
||||
this.semicolonContent.equals(segment.semicolonContent) &&
|
||||
this.parameters.equals(segment.parameters));
|
||||
return this.value.equals(((DefaultPathSegment) other).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.value.hashCode();
|
||||
result = 31 * result + this.semicolonContent.hashCode();
|
||||
result = 31 * result + this.parameters.hashCode();
|
||||
return result;
|
||||
return this.value.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[value='" + this.value + "\', " +
|
||||
"semicolonContent='" + this.semicolonContent + "\', " +
|
||||
"parameters=" + this.parameters + "']";
|
||||
}
|
||||
return "[value='" + this.value + "', parameters=" + this.parameters + "']"; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,9 +65,6 @@ class DefaultRequestPath implements RequestPath {
|
|||
for (int i=0; i < path.elements().size(); i++) {
|
||||
PathContainer.Element element = path.elements().get(i);
|
||||
counter += element.value().length();
|
||||
if (element instanceof PathContainer.Segment) {
|
||||
counter += ((Segment) element).semicolonContent().length();
|
||||
}
|
||||
if (length == counter) {
|
||||
return path.subPath(0, i + 1);
|
||||
}
|
||||
|
|
|
@ -104,21 +104,16 @@ public interface PathContainer {
|
|||
interface Segment extends Element {
|
||||
|
||||
/**
|
||||
* Return the path segment {@link #value()} decoded.
|
||||
* Return the path segment value to use for pattern matching purposes.
|
||||
* This may differ from {@link #value()} such as being decoded, without
|
||||
* path parameters, etc.
|
||||
*/
|
||||
String valueDecoded();
|
||||
String valueToMatch();
|
||||
|
||||
/**
|
||||
* Variant of {@link #valueDecoded()} as a {@code char[]}.
|
||||
* Variant of {@link #valueToMatch()} as a {@code char[]}.
|
||||
*/
|
||||
char[] valueDecodedChars();
|
||||
|
||||
/**
|
||||
* Return the portion of the path segment after and including the first
|
||||
* ";" (semicolon) representing path parameters. The actual parsed
|
||||
* parameters if any can be obtained via {@link #parameters()}.
|
||||
*/
|
||||
String semicolonContent();
|
||||
char[] valueToMatchAsChars();
|
||||
|
||||
/**
|
||||
* Path parameters parsed from the path segment.
|
||||
|
|
|
@ -86,7 +86,7 @@ class CaptureTheRestPathElement extends PathElement {
|
|||
for (int i = fromSegment, max = pathElements.size(); i < max; i++) {
|
||||
Element element = pathElements.get(i);
|
||||
if (element instanceof Segment) {
|
||||
buf.append(((Segment)element).valueDecoded());
|
||||
buf.append(((Segment)element).valueToMatch());
|
||||
}
|
||||
else {
|
||||
buf.append(element.value());
|
||||
|
|
|
@ -62,13 +62,13 @@ class LiteralPathElement extends PathElement {
|
|||
if (!(element instanceof Segment)) {
|
||||
return false;
|
||||
}
|
||||
String value = ((Segment)element).valueDecoded();
|
||||
String value = ((Segment)element).valueToMatch();
|
||||
if (value.length() != len) {
|
||||
// Not enough data to match this path element
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] data = ((Segment)element).valueDecodedChars();
|
||||
char[] data = ((Segment)element).valueToMatchAsChars();
|
||||
if (this.caseSensitive) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (data[i] != this.text[i]) {
|
||||
|
|
|
@ -683,7 +683,7 @@ public class PathPattern implements Comparable<PathPattern> {
|
|||
String pathElementValue(int pathIndex) {
|
||||
Element element = (pathIndex < pathLength) ? pathElements.get(pathIndex) : null;
|
||||
if (element instanceof Segment) {
|
||||
return ((Segment)element).valueDecoded();
|
||||
return ((Segment)element).valueToMatch();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -68,13 +68,13 @@ class SingleCharWildcardedPathElement extends PathElement {
|
|||
if (!(element instanceof Segment)) {
|
||||
return false;
|
||||
}
|
||||
String value = ((Segment)element).valueDecoded();
|
||||
String value = ((Segment)element).valueToMatch();
|
||||
if (value.length() != len) {
|
||||
// Not enough data to match this path element
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] data = ((Segment)element).valueDecodedChars();
|
||||
char[] data = ((Segment)element).valueToMatchAsChars();
|
||||
if (this.caseSensitive) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
char ch = this.text[i];
|
||||
|
|
|
@ -50,7 +50,7 @@ class WildcardPathElement extends PathElement {
|
|||
// Should not match a separator
|
||||
return false;
|
||||
}
|
||||
segmentData = ((Segment)element).valueDecoded();
|
||||
segmentData = ((Segment)element).valueToMatch();
|
||||
pathIndex++;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,14 +38,14 @@ public class DefaultPathContainerTests {
|
|||
@Test
|
||||
public void pathSegment() throws Exception {
|
||||
// basic
|
||||
testPathSegment("cars", "", "cars", "cars", new LinkedMultiValueMap<>());
|
||||
testPathSegment("cars", "cars", new LinkedMultiValueMap<>());
|
||||
|
||||
// empty
|
||||
testPathSegment("", "", "", "", new LinkedMultiValueMap<>());
|
||||
testPathSegment("", "", new LinkedMultiValueMap<>());
|
||||
|
||||
// spaces
|
||||
testPathSegment("%20%20", "", "%20%20", " ", new LinkedMultiValueMap<>());
|
||||
testPathSegment("%20a%20", "", "%20a%20", " a ", new LinkedMultiValueMap<>());
|
||||
testPathSegment("%20%20", " ", new LinkedMultiValueMap<>());
|
||||
testPathSegment("%20a%20", " a ", new LinkedMultiValueMap<>());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -56,30 +56,29 @@ public class DefaultPathContainerTests {
|
|||
params.add("colors", "blue");
|
||||
params.add("colors", "green");
|
||||
params.add("year", "2012");
|
||||
testPathSegment("cars", ";colors=red,blue,green;year=2012", "cars", "cars", params);
|
||||
testPathSegment("cars;colors=red,blue,green;year=2012", "cars", params);
|
||||
|
||||
// trailing semicolon
|
||||
params = new LinkedMultiValueMap<>();
|
||||
params.add("p", "1");
|
||||
testPathSegment("path", ";p=1;", "path", "path", params);
|
||||
testPathSegment("path;p=1;", "path", params);
|
||||
|
||||
// params with spaces
|
||||
params = new LinkedMultiValueMap<>();
|
||||
params.add("param name", "param value");
|
||||
testPathSegment("path", ";param%20name=param%20value;%20", "path", "path", params);
|
||||
testPathSegment("path;param%20name=param%20value;%20", "path", params);
|
||||
|
||||
// empty params
|
||||
params = new LinkedMultiValueMap<>();
|
||||
params.add("p", "1");
|
||||
testPathSegment("path", ";;;%20;%20;p=1;%20", "path", "path", params);
|
||||
testPathSegment("path;;;%20;%20;p=1;%20", "path", params);
|
||||
}
|
||||
|
||||
private void testPathSegment(String rawValue, String semicolonContent,
|
||||
String value, String valueDecoded, MultiValueMap<String, String> params) {
|
||||
private void testPathSegment(String rawValue, String valueToMatch, MultiValueMap<String, String> params) {
|
||||
|
||||
PathContainer container = DefaultPathContainer.parsePath(rawValue + semicolonContent, UTF_8);
|
||||
PathContainer container = DefaultPathContainer.parsePath(rawValue, UTF_8);
|
||||
|
||||
if ("".equals(value)) {
|
||||
if ("".equals(rawValue)) {
|
||||
assertEquals(0, container.elements().size());
|
||||
return;
|
||||
}
|
||||
|
@ -87,9 +86,8 @@ public class DefaultPathContainerTests {
|
|||
assertEquals(1, container.elements().size());
|
||||
PathContainer.Segment segment = (PathContainer.Segment) container.elements().get(0);
|
||||
|
||||
assertEquals("value: '" + rawValue + "'", value, segment.value());
|
||||
assertEquals("valueDecoded: '" + rawValue + "'", valueDecoded, segment.valueDecoded());
|
||||
assertEquals("semicolonContent: '" + rawValue + "'", semicolonContent, segment.semicolonContent());
|
||||
assertEquals("value: '" + rawValue + "'", rawValue, segment.value());
|
||||
assertEquals("valueToMatch: '" + rawValue + "'", valueToMatch, segment.valueToMatch());
|
||||
assertEquals("params: '" + rawValue + "'", params, segment.parameters());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue