|
25 | 25 | import org.springframework.web.util.pattern.PathPattern.MatchingContext; |
26 | 26 |
|
27 | 27 | /** |
28 | | - * A path element representing capturing the rest of a path. In the pattern |
29 | | - * '/foo/{*foobar}' the /{*foobar} is represented as a {@link CaptureTheRestPathElement}. |
| 28 | + * A path element that captures multiple path segments. |
| 29 | + * This element is only allowed in two situations: |
| 30 | + * <ol> |
| 31 | + * <li>At the start of a path, immediately followed by a {@link LiteralPathElement} like '/{*foobar}/foo/{bar}' |
| 32 | + * <li>At the end of a path, like '/foo/{*foobar}' |
| 33 | + * </ol> |
| 34 | + * <p>Only a single {@link WildcardSegmentsPathElement} or {@link CaptureSegmentsPathElement} element is allowed |
| 35 | + * * in a pattern. In the pattern '/foo/{*foobar}' the /{*foobar} is represented as a {@link CaptureSegmentsPathElement}. |
30 | 36 | * |
31 | 37 | * @author Andy Clement |
| 38 | + * @author Brian Clozel |
32 | 39 | * @since 5.0 |
33 | 40 | */ |
34 | | -class CaptureTheRestPathElement extends PathElement { |
| 41 | +class CaptureSegmentsPathElement extends PathElement { |
35 | 42 |
|
36 | 43 | private final String variableName; |
37 | 44 |
|
38 | 45 |
|
39 | 46 | /** |
40 | | - * Create a new {@link CaptureTheRestPathElement} instance. |
| 47 | + * Create a new {@link CaptureSegmentsPathElement} instance. |
41 | 48 | * @param pos position of the path element within the path pattern text |
42 | 49 | * @param captureDescriptor a character array containing contents like '{' '*' 'a' 'b' '}' |
43 | 50 | * @param separator the separator used in the path pattern |
44 | 51 | */ |
45 | | - CaptureTheRestPathElement(int pos, char[] captureDescriptor, char separator) { |
| 52 | + CaptureSegmentsPathElement(int pos, char[] captureDescriptor, char separator) { |
46 | 53 | super(pos, separator); |
47 | 54 | this.variableName = new String(captureDescriptor, 2, captureDescriptor.length - 3); |
48 | 55 | } |
49 | 56 |
|
50 | 57 |
|
51 | 58 | @Override |
52 | 59 | public boolean matches(int pathIndex, MatchingContext matchingContext) { |
53 | | - // No need to handle 'match start' checking as this captures everything |
54 | | - // anyway and cannot be followed by anything else |
55 | | - // assert next == null |
56 | | - |
57 | | - // If there is more data, it must start with the separator |
58 | | - if (pathIndex < matchingContext.pathLength && !matchingContext.isSeparator(pathIndex)) { |
| 60 | + // wildcard segments at the start of the pattern |
| 61 | + if (pathIndex == 0 && this.next != null) { |
| 62 | + int endPathIndex = pathIndex; |
| 63 | + while (endPathIndex < matchingContext.pathLength) { |
| 64 | + if (this.next.matches(endPathIndex, matchingContext)) { |
| 65 | + collectParameters(matchingContext, pathIndex, endPathIndex); |
| 66 | + return true; |
| 67 | + } |
| 68 | + endPathIndex++; |
| 69 | + } |
| 70 | + return false; |
| 71 | + } |
| 72 | + // match until the end of the path |
| 73 | + else if (pathIndex < matchingContext.pathLength && !matchingContext.isSeparator(pathIndex)) { |
59 | 74 | return false; |
60 | 75 | } |
61 | 76 | if (matchingContext.determineRemainingPath) { |
62 | 77 | matchingContext.remainingPathIndex = matchingContext.pathLength; |
63 | 78 | } |
| 79 | + collectParameters(matchingContext, pathIndex, matchingContext.pathLength); |
| 80 | + return true; |
| 81 | + } |
| 82 | + |
| 83 | + private void collectParameters(MatchingContext matchingContext, int pathIndex, int endPathIndex) { |
64 | 84 | if (matchingContext.extractingVariables) { |
65 | 85 | // Collect the parameters from all the remaining segments |
66 | | - MultiValueMap<String,String> parametersCollector = null; |
67 | | - for (int i = pathIndex; i < matchingContext.pathLength; i++) { |
| 86 | + MultiValueMap<String, String> parametersCollector = NO_PARAMETERS; |
| 87 | + for (int i = pathIndex; i < endPathIndex; i++) { |
68 | 88 | Element element = matchingContext.pathElements.get(i); |
69 | 89 | if (element instanceof PathSegment pathSegment) { |
70 | 90 | MultiValueMap<String, String> parameters = pathSegment.parameters(); |
71 | 91 | if (!parameters.isEmpty()) { |
72 | | - if (parametersCollector == null) { |
| 92 | + if (parametersCollector == NO_PARAMETERS) { |
73 | 93 | parametersCollector = new LinkedMultiValueMap<>(); |
74 | 94 | } |
75 | 95 | parametersCollector.addAll(parameters); |
76 | 96 | } |
77 | 97 | } |
78 | 98 | } |
79 | | - matchingContext.set(this.variableName, pathToString(pathIndex, matchingContext.pathElements), |
80 | | - parametersCollector == null?NO_PARAMETERS:parametersCollector); |
| 99 | + matchingContext.set(this.variableName, pathToString(pathIndex, endPathIndex, matchingContext.pathElements), |
| 100 | + parametersCollector); |
81 | 101 | } |
82 | | - return true; |
83 | 102 | } |
84 | 103 |
|
85 | | - private String pathToString(int fromSegment, List<Element> pathElements) { |
| 104 | + private String pathToString(int fromSegment, int toSegment, List<Element> pathElements) { |
86 | 105 | StringBuilder sb = new StringBuilder(); |
87 | | - for (int i = fromSegment, max = pathElements.size(); i < max; i++) { |
| 106 | + for (int i = fromSegment, max = toSegment; i < max; i++) { |
88 | 107 | Element element = pathElements.get(i); |
89 | 108 | if (element instanceof PathSegment pathSegment) { |
90 | 109 | sb.append(pathSegment.valueToMatch()); |
@@ -119,7 +138,7 @@ public int getCaptureCount() { |
119 | 138 |
|
120 | 139 | @Override |
121 | 140 | public String toString() { |
122 | | - return "CaptureTheRest(/{*" + this.variableName + "})"; |
| 141 | + return "CaptureSegments(/{*" + this.variableName + "})"; |
123 | 142 | } |
124 | 143 |
|
125 | 144 | } |
0 commit comments