|
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