View Javadoc

1   /*
2    * Copyright 2002-2012 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.util;
18  
19  import java.util.LinkedList;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  /**
26   * Package-protected helper class for {@link AntPathMatcher}. Tests whether or not a string matches against a pattern
27   * via a {@link Pattern}.
28   *
29   * <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and only one
30   * character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
31   *
32   * @author Arjen Poutsma
33   * @author Rossen Stoyanchev
34   * @since 3.0
35   */
36  class AntPathStringMatcher {
37  
38  	private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
39  
40  	private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
41  
42  	private final Pattern pattern;
43  
44  	private final List<String> variableNames = new LinkedList<String>();
45  
46  
47  	/** Construct a new instance of the <code>AntPatchStringMatcher</code>. */
48  	AntPathStringMatcher(String pattern) {
49  		this.pattern = createPattern(pattern);
50  	}
51  
52  	private Pattern createPattern(String pattern) {
53  		StringBuilder patternBuilder = new StringBuilder();
54  		Matcher m = GLOB_PATTERN.matcher(pattern);
55  		int end = 0;
56  		while (m.find()) {
57  			patternBuilder.append(quote(pattern, end, m.start()));
58  			String match = m.group();
59  			if ("?".equals(match)) {
60  				patternBuilder.append('.');
61  			}
62  			else if ("*".equals(match)) {
63  				patternBuilder.append(".*");
64  			}
65  			else if (match.startsWith("{") && match.endsWith("}")) {
66  				int colonIdx = match.indexOf(':');
67  				if (colonIdx == -1) {
68  					patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
69  					variableNames.add(m.group(1));
70  				}
71  				else {
72  					String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
73  					patternBuilder.append('(');
74  					patternBuilder.append(variablePattern);
75  					patternBuilder.append(')');
76  					String variableName = match.substring(1, colonIdx);
77  					variableNames.add(variableName);
78  				}
79  			}
80  			end = m.end();
81  		}
82  		patternBuilder.append(quote(pattern, end, pattern.length()));
83  		return Pattern.compile(patternBuilder.toString());
84  	}
85  
86  	private String quote(String s, int start, int end) {
87  		if (start == end) {
88  			return "";
89  		}
90  		return Pattern.quote(s.substring(start, end));
91  	}
92  
93  	/**
94  	 * Main entry point.
95  	 *
96  	 * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
97  	 */
98  	public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
99  		Matcher matcher = pattern.matcher(str);
100 		if (matcher.matches()) {
101 			if (uriTemplateVariables != null) {
102 				// SPR-8455
103 				Assert.isTrue(variableNames.size() == matcher.groupCount(),
104 						"The number of capturing groups in the pattern segment " + pattern +
105 						" does not match the number of URI template variables it defines, which can occur if " +
106 						" capturing groups are used in a URI template regex. Use non-capturing groups instead.");
107 				for (int i = 1; i <= matcher.groupCount(); i++) {
108 					String name = this.variableNames.get(i - 1);
109 					String value = matcher.group(i);
110 					uriTemplateVariables.put(name, value);
111 				}
112 			}
113 			return true;
114 		}
115 		else {
116 			return false;
117 		}
118 	}
119 
120 }