forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PatternsRequestCondition.java
181 lines (160 loc) · 5.6 KB
/
PatternsRequestCondition.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright 2002-2020 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
*
* https://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.reactive.result.condition;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.http.server.PathContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* A logical disjunction (' || ') request condition that matches a request
* against a set of URL path patterns.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 5.0
*/
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
private static final SortedSet<PathPattern> EMPTY_PATH_PATTERN =
new TreeSet<>(Collections.singleton(new PathPatternParser().parse("")));
private final SortedSet<PathPattern> patterns;
/**
* Creates a new instance with the given URL patterns.
* @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
*/
public PatternsRequestCondition(PathPattern... patterns) {
this(ObjectUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
}
/**
* Creates a new instance with the given URL patterns.
*/
public PatternsRequestCondition(List<PathPattern> patterns) {
this.patterns = (patterns.isEmpty() ? EMPTY_PATH_PATTERN : new TreeSet<>(patterns));
}
private PatternsRequestCondition(SortedSet<PathPattern> patterns) {
this.patterns = patterns;
}
public Set<PathPattern> getPatterns() {
return this.patterns;
}
@Override
protected Collection<PathPattern> getContent() {
return this.patterns;
}
@Override
protected String getToStringInfix() {
return " || ";
}
/**
* Returns a new instance with URL patterns from the current instance ("this") and
* the "other" instance as follows:
* <ul>
* <li>If there are patterns in both instances, combine the patterns in "this" with
* the patterns in "other" using {@link PathPattern#combine(PathPattern)}.
* <li>If only one instance has patterns, use them.
* <li>If neither instance has patterns, use an empty String (i.e. "").
* </ul>
*/
@Override
public PatternsRequestCondition combine(PatternsRequestCondition other) {
if (isEmptyPathPattern() && other.isEmptyPathPattern()) {
return this;
}
else if (other.isEmptyPathPattern()) {
return this;
}
else if (isEmptyPathPattern()) {
return other;
}
else {
SortedSet<PathPattern> combined = new TreeSet<>();
for (PathPattern pattern1 : this.patterns) {
for (PathPattern pattern2 : other.patterns) {
combined.add(pattern1.combine(pattern2));
}
}
return new PatternsRequestCondition(combined);
}
}
private boolean isEmptyPathPattern() {
return this.patterns == EMPTY_PATH_PATTERN;
}
/**
* Checks if any of the patterns match the given request and returns an instance
* that is guaranteed to contain matching patterns, sorted.
* @param exchange the current exchange
* @return the same instance if the condition contains no patterns;
* or a new condition with sorted matching patterns;
* or {@code null} if no patterns match.
*/
@Override
@Nullable
public PatternsRequestCondition getMatchingCondition(ServerWebExchange exchange) {
SortedSet<PathPattern> matches = getMatchingPatterns(exchange);
return (matches != null ? new PatternsRequestCondition(matches) : null);
}
@Nullable
private SortedSet<PathPattern> getMatchingPatterns(ServerWebExchange exchange) {
PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
TreeSet<PathPattern> result = null;
for (PathPattern pattern : this.patterns) {
if (pattern.matches(lookupPath)) {
result = (result != null ? result : new TreeSet<>());
result.add(pattern);
}
}
return result;
}
/**
* Compare the two conditions based on the URL patterns they contain.
* Patterns are compared one at a time, from top to bottom. If all compared
* patterns match equally, but one instance has more patterns, it is
* considered a closer match.
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(ServerWebExchange)} to ensure they
* contain only patterns that match the request and are sorted with
* the best matches on top.
*/
@Override
public int compareTo(PatternsRequestCondition other, ServerWebExchange exchange) {
Iterator<PathPattern> iterator = this.patterns.iterator();
Iterator<PathPattern> iteratorOther = other.getPatterns().iterator();
while (iterator.hasNext() && iteratorOther.hasNext()) {
int result = PathPattern.SPECIFICITY_COMPARATOR.compare(iterator.next(), iteratorOther.next());
if (result != 0) {
return result;
}
}
if (iterator.hasNext()) {
return -1;
}
else if (iteratorOther.hasNext()) {
return 1;
}
else {
return 0;
}
}
}