1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.condition;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Set;
28 import javax.servlet.http.HttpServletRequest;
29
30 import org.springframework.util.AntPathMatcher;
31 import org.springframework.util.PathMatcher;
32 import org.springframework.util.StringUtils;
33 import org.springframework.web.util.UrlPathHelper;
34
35
36
37
38
39
40
41
42 public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
43
44 private final Set<String> patterns;
45
46 private final UrlPathHelper pathHelper;
47
48 private final PathMatcher pathMatcher;
49
50 private final boolean useSuffixPatternMatch;
51
52 private final boolean useTrailingSlashMatch;
53
54 private final List<String> fileExtensions = new ArrayList<String>();
55
56
57
58
59
60
61
62 public PatternsRequestCondition(String... patterns) {
63 this(asList(patterns), null, null, true, true, null);
64 }
65
66
67
68
69
70
71
72
73
74
75 public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher,
76 boolean useSuffixPatternMatch, boolean useTrailingSlashMatch) {
77
78 this(asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, null);
79 }
80
81
82
83
84
85
86
87
88
89
90
91 public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper,
92 PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
93 List<String> fileExtensions) {
94
95 this(asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, fileExtensions);
96 }
97
98
99
100
101 private PatternsRequestCondition(Collection<String> patterns, UrlPathHelper urlPathHelper,
102 PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
103 List<String> fileExtensions) {
104
105 this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
106 this.pathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
107 this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
108 this.useSuffixPatternMatch = useSuffixPatternMatch;
109 this.useTrailingSlashMatch = useTrailingSlashMatch;
110 if (fileExtensions != null) {
111 for (String fileExtension : fileExtensions) {
112 if (fileExtension.charAt(0) != '.') {
113 fileExtension = "." + fileExtension;
114 }
115 this.fileExtensions.add(fileExtension);
116 }
117 }
118 }
119
120
121 private static List<String> asList(String... patterns) {
122 return (patterns != null ? Arrays.asList(patterns) : Collections.<String>emptyList());
123 }
124
125 private static Set<String> prependLeadingSlash(Collection<String> patterns) {
126 if (patterns == null) {
127 return Collections.emptySet();
128 }
129 Set<String> result = new LinkedHashSet<String>(patterns.size());
130 for (String pattern : patterns) {
131 if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
132 pattern = "/" + pattern;
133 }
134 result.add(pattern);
135 }
136 return result;
137 }
138
139 public Set<String> getPatterns() {
140 return this.patterns;
141 }
142
143 @Override
144 protected Collection<String> getContent() {
145 return this.patterns;
146 }
147
148 @Override
149 protected String getToStringInfix() {
150 return " || ";
151 }
152
153
154
155
156
157
158
159
160
161
162
163 @Override
164 public PatternsRequestCondition combine(PatternsRequestCondition other) {
165 Set<String> result = new LinkedHashSet<String>();
166 if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
167 for (String pattern1 : this.patterns) {
168 for (String pattern2 : other.patterns) {
169 result.add(this.pathMatcher.combine(pattern1, pattern2));
170 }
171 }
172 }
173 else if (!this.patterns.isEmpty()) {
174 result.addAll(this.patterns);
175 }
176 else if (!other.patterns.isEmpty()) {
177 result.addAll(other.patterns);
178 }
179 else {
180 result.add("");
181 }
182 return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
183 this.useTrailingSlashMatch, this.fileExtensions);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 @Override
203 public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
204
205 if (this.patterns.isEmpty()) {
206 return this;
207 }
208
209 String lookupPath = this.pathHelper.getLookupPathForRequest(request);
210 List<String> matches = getMatchingPatterns(lookupPath);
211
212 return matches.isEmpty() ? null :
213 new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
214 this.useTrailingSlashMatch, this.fileExtensions);
215 }
216
217
218
219
220
221
222
223
224
225
226
227 public List<String> getMatchingPatterns(String lookupPath) {
228 List<String> matches = new ArrayList<String>();
229 for (String pattern : this.patterns) {
230 String match = getMatchingPattern(pattern, lookupPath);
231 if (match != null) {
232 matches.add(match);
233 }
234 }
235 Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
236 return matches;
237 }
238
239 private String getMatchingPattern(String pattern, String lookupPath) {
240 if (pattern.equals(lookupPath)) {
241 return pattern;
242 }
243 if (this.useSuffixPatternMatch) {
244 if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
245 for (String extension : this.fileExtensions) {
246 if (this.pathMatcher.match(pattern + extension, lookupPath)) {
247 return pattern + extension;
248 }
249 }
250 }
251 else {
252 boolean hasSuffix = pattern.indexOf('.') != -1;
253 if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
254 return pattern + ".*";
255 }
256 }
257 }
258 if (this.pathMatcher.match(pattern, lookupPath)) {
259 return pattern;
260 }
261 if (this.useTrailingSlashMatch) {
262 if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
263 return pattern +"/";
264 }
265 }
266 return null;
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280 @Override
281 public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
282 String lookupPath = this.pathHelper.getLookupPathForRequest(request);
283 Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
284 Iterator<String> iterator = this.patterns.iterator();
285 Iterator<String> iteratorOther = other.patterns.iterator();
286 while (iterator.hasNext() && iteratorOther.hasNext()) {
287 int result = patternComparator.compare(iterator.next(), iteratorOther.next());
288 if (result != 0) {
289 return result;
290 }
291 }
292 if (iterator.hasNext()) {
293 return -1;
294 }
295 else if (iteratorOther.hasNext()) {
296 return 1;
297 }
298 else {
299 return 0;
300 }
301 }
302
303 }