1616
1717package org .springframework .util ;
1818
19+ import java .util .LinkedList ;
20+ import java .util .List ;
1921import java .util .Map ;
22+ import java .util .regex .Matcher ;
23+ import java .util .regex .Pattern ;
2024
2125/**
2226 * Package-protected helper class for {@link AntPathMatcher}.
23- * Tests whether or not a string matches against a pattern.
27+ * Tests whether or not a string matches against a pattern using a regular expression .
2428 *
2529 * <p>The pattern may contain special characters: '*' means zero or more characters;
2630 * '?' means one and only one character; '{' and '}' indicate a URI template pattern.
3034 */
3135class AntPatchStringMatcher {
3236
33- private final char [] patArr ;
37+ private static final Pattern GLOB_PATTERN = Pattern . compile ( " \\ ?| \\ *| \\ {([^/]+?) \\ }" ) ;
3438
35- private final char [] strArr ;
39+ private final Pattern pattern ;
3640
37- private int patIdxStart = 0 ;
41+ private String str ;
3842
39- private int patIdxEnd ;
40-
41- private int strIdxStart = 0 ;
42-
43- private int strIdxEnd ;
44-
45- private char ch ;
43+ private final List <String > variableNames = new LinkedList <String >();
4644
4745 private final Map <String , String > uriTemplateVariables ;
4846
49-
50- /**
51- * Construct a new instance of the <code>AntPatchStringMatcher</code>.
52- */
53- public AntPatchStringMatcher (String pattern , String str , Map <String , String > uriTemplateVariables ) {
54- this .patArr = pattern .toCharArray ();
55- this .strArr = str .toCharArray ();
56- this .patIdxEnd = this .patArr .length - 1 ;
57- this .strIdxEnd = this .strArr .length - 1 ;
47+ /** Construct a new instance of the <code>AntPatchStringMatcher</code>. */
48+ AntPatchStringMatcher (String pattern , String str , Map <String , String > uriTemplateVariables ) {
49+ this .str = str ;
5850 this .uriTemplateVariables = uriTemplateVariables ;
51+ this .pattern = createPattern (pattern );
5952 }
6053
61-
62- /**
63- * Main entry point.
64- * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
65- */
66- public boolean matchStrings () {
67- if (shortcutPossible ()) {
68- return doShortcut ();
69- }
70- if (patternContainsOnlyStar ()) {
71- return true ;
72- }
73- if (patternContainsOneTemplateVariable ()) {
74- addTemplateVariable (0 , patIdxEnd , 0 , strIdxEnd );
75- return true ;
76- }
77- if (!matchBeforeFirstStarOrCurly ()) {
78- return false ;
79- }
80- if (allCharsUsed ()) {
81- return onlyStarsLeft ();
82- }
83- if (!matchAfterLastStarOrCurly ()) {
84- return false ;
85- }
86- if (allCharsUsed ()) {
87- return onlyStarsLeft ();
88- }
89- // process pattern between stars. padIdxStart and patIdxEnd point
90- // always to a '*'.
91- while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) {
92- int patIdxTmp ;
93- if (patArr [patIdxStart ] == '{' ) {
94- patIdxTmp = findClosingCurly ();
95- addTemplateVariable (patIdxStart , patIdxTmp , strIdxStart , strIdxEnd );
96- patIdxStart = patIdxTmp + 1 ;
97- strIdxStart = strIdxEnd + 1 ;
98- continue ;
99- }
100- patIdxTmp = findNextStarOrCurly ();
101- if (consecutiveStars (patIdxTmp )) {
102- continue ;
103- }
104- // Find the pattern between padIdxStart & padIdxTmp in str between
105- // strIdxStart & strIdxEnd
106- int patLength = (patIdxTmp - patIdxStart - 1 );
107- int strLength = (strIdxEnd - strIdxStart + 1 );
108- int foundIdx = -1 ;
109- strLoop :
110- for (int i = 0 ; i <= strLength - patLength ; i ++) {
111- for (int j = 0 ; j < patLength ; j ++) {
112- ch = patArr [patIdxStart + j + 1 ];
113- if (ch != '?' ) {
114- if (ch != strArr [strIdxStart + i + j ]) {
115- continue strLoop ;
116- }
117- }
118- }
119-
120- foundIdx = strIdxStart + i ;
121- break ;
122- }
123-
124- if (foundIdx == -1 ) {
125- return false ;
126- }
127-
128- patIdxStart = patIdxTmp ;
129- strIdxStart = foundIdx + patLength ;
130- }
131-
132- return onlyStarsLeft ();
133- }
134-
135- private void addTemplateVariable (int curlyIdxStart , int curlyIdxEnd , int valIdxStart , int valIdxEnd ) {
136- if (uriTemplateVariables != null ) {
137- String varName = new String (patArr , curlyIdxStart + 1 , curlyIdxEnd - curlyIdxStart - 1 );
138- String varValue = new String (strArr , valIdxStart , valIdxEnd - valIdxStart + 1 );
139- uriTemplateVariables .put (varName , varValue );
140- }
141- }
142-
143- private boolean consecutiveStars (int patIdxTmp ) {
144- if (patIdxTmp == patIdxStart + 1 && patArr [patIdxStart ] == '*' && patArr [patIdxTmp ] == '*' ) {
145- // Two stars next to each other, skip the first one.
146- patIdxStart ++;
147- return true ;
148- }
149- return false ;
150- }
151-
152- private int findNextStarOrCurly () {
153- for (int i = patIdxStart + 1 ; i <= patIdxEnd ; i ++) {
154- if (patArr [i ] == '*' || patArr [i ] == '{' ) {
155- return i ;
54+ private Pattern createPattern (String pattern ) {
55+ StringBuilder patternBuilder = new StringBuilder ();
56+ Matcher m = GLOB_PATTERN .matcher (pattern );
57+ int end = 0 ;
58+ while (m .find ()) {
59+ patternBuilder .append (quote (pattern , end , m .start ()));
60+ String match = m .group ();
61+ if ("?" .equals (match )) {
62+ patternBuilder .append ('.' );
15663 }
157- }
158- return -1 ;
159- }
160-
161- private int findClosingCurly () {
162- for (int i = patIdxStart + 1 ; i <= patIdxEnd ; i ++) {
163- if (patArr [i ] == '}' ) {
164- return i ;
64+ else if ("*" .equals (match )) {
65+ patternBuilder .append (".*" );
16566 }
166- }
167- return -1 ;
168- }
169-
170- private boolean onlyStarsLeft () {
171- for (int i = patIdxStart ; i <= patIdxEnd ; i ++) {
172- if (patArr [i ] != '*' ) {
173- return false ;
67+ else if (match .startsWith ("{" ) && match .endsWith ("}" )) {
68+ patternBuilder .append ("(.*)" );
69+ variableNames .add (m .group (1 ));
17470 }
71+ end = m .end ();
17572 }
176- return true ;
177- }
178-
179- private boolean allCharsUsed () {
180- return strIdxStart > strIdxEnd ;
73+ patternBuilder .append (quote (pattern , end , pattern .length ()));
74+ return Pattern .compile (patternBuilder .toString ());
18175 }
18276
183- private boolean shortcutPossible () {
184- for (char ch : patArr ) {
185- if (ch == '*' || ch == '{' || ch == '}' ) {
186- return false ;
187- }
77+ private String quote (String s , int start , int end ) {
78+ if (start == end ) {
79+ return "" ;
18880 }
189- return true ;
81+ return Pattern . quote ( s . substring ( start , end )) ;
19082 }
19183
192- private boolean doShortcut () {
193- if (patIdxEnd != strIdxEnd ) {
194- return false ; // Pattern and string do not have the same size
195- }
196- for (int i = 0 ; i <= patIdxEnd ; i ++) {
197- ch = patArr [i ];
198- if (ch != '?' ) {
199- if (ch != strArr [i ]) {
200- return false ;// Character mismatch
201- }
202- }
203- }
204- return true ; // String matches against pattern
205- }
206-
207- private boolean patternContainsOnlyStar () {
208- return (patIdxEnd == 0 && patArr [0 ] == '*' );
209- }
210-
211- private boolean patternContainsOneTemplateVariable () {
212- if ((patIdxEnd >= 2 && patArr [0 ] == '{' && patArr [patIdxEnd ] == '}' )) {
213- for (int i = 1 ; i < patIdxEnd ; i ++) {
214- if (patArr [i ] == '}' ) {
215- return false ;
84+ /**
85+ * Main entry point.
86+ *
87+ * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
88+ */
89+ public boolean matchStrings () {
90+ Matcher matcher = pattern .matcher (str );
91+ if (matcher .matches ()) {
92+ if (uriTemplateVariables != null ) {
93+ for (int i = 1 ; i <= matcher .groupCount (); i ++) {
94+ String name = this .variableNames .get (i - 1 );
95+ String value = matcher .group (i );
96+ uriTemplateVariables .put (name , value );
21697 }
21798 }
21899 return true ;
@@ -222,30 +103,4 @@ private boolean patternContainsOneTemplateVariable() {
222103 }
223104 }
224105
225- private boolean matchBeforeFirstStarOrCurly () {
226- while ((ch = patArr [patIdxStart ]) != '*' && ch != '{' && strIdxStart <= strIdxEnd ) {
227- if (ch != '?' ) {
228- if (ch != strArr [strIdxStart ]) {
229- return false ;
230- }
231- }
232- patIdxStart ++;
233- strIdxStart ++;
234- }
235- return true ;
236- }
237-
238- private boolean matchAfterLastStarOrCurly () {
239- while ((ch = patArr [patIdxEnd ]) != '*' && ch != '}' && strIdxStart <= strIdxEnd ) {
240- if (ch != '?' ) {
241- if (ch != strArr [strIdxEnd ]) {
242- return false ;
243- }
244- }
245- patIdxEnd --;
246- strIdxEnd --;
247- }
248- return true ;
249- }
250-
251106}
0 commit comments