18
18
19
19
import java .util .Comparator ;
20
20
import java .util .LinkedHashMap ;
21
+ import java .util .LinkedList ;
22
+ import java .util .List ;
21
23
import java .util .Map ;
22
24
import java .util .concurrent .ConcurrentHashMap ;
23
25
import java .util .regex .Matcher ;
43
45
* @author Juergen Hoeller
44
46
* @author Rob Harrop
45
47
* @author Arjen Poutsma
48
+ * @author Rossen Stoyanchev
46
49
* @since 16.07.2003
47
50
*/
48
51
public class AntPathMatcher implements PathMatcher {
@@ -55,7 +58,7 @@ public class AntPathMatcher implements PathMatcher {
55
58
private String pathSeparator = DEFAULT_PATH_SEPARATOR ;
56
59
57
60
private final Map <String , AntPathStringMatcher > stringMatcherCache =
58
- new ConcurrentHashMap <String , AntPathStringMatcher >();
61
+ new ConcurrentHashMap <String , AntPathStringMatcher >(256 );
59
62
60
63
61
64
/** Set the path separator to use for pattern parsing. Default is "/", as in Ant. */
@@ -213,8 +216,9 @@ else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
213
216
}
214
217
215
218
/**
216
- * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:<br> '*'
217
- * means zero or more characters<br> '?' means one and only one character
219
+ * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:
220
+ * <br>'*' means zero or more characters
221
+ * <br>'?' means one and only one character
218
222
* @param pattern pattern to match against. Must not be <code>null</code>.
219
223
* @param str string which must be matched against the pattern. Must not be <code>null</code>.
220
224
* @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
@@ -462,4 +466,88 @@ private int getPatternLength(String pattern) {
462
466
}
463
467
}
464
468
469
+
470
+ /**
471
+ * Tests whether or not a string matches against a pattern via a {@link Pattern}.
472
+ * <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and
473
+ * only one character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
474
+ */
475
+ private static class AntPathStringMatcher {
476
+
477
+ private static final Pattern GLOB_PATTERN = Pattern .compile ("\\ ?|\\ *|\\ {((?:\\ {[^/]+?\\ }|[^/{}]|\\ \\ [{}])+?)\\ }" );
478
+
479
+ private static final String DEFAULT_VARIABLE_PATTERN = "(.*)" ;
480
+
481
+ private final Pattern pattern ;
482
+
483
+ private final List <String > variableNames = new LinkedList <String >();
484
+
485
+ public AntPathStringMatcher (String pattern ) {
486
+ StringBuilder patternBuilder = new StringBuilder ();
487
+ Matcher m = GLOB_PATTERN .matcher (pattern );
488
+ int end = 0 ;
489
+ while (m .find ()) {
490
+ patternBuilder .append (quote (pattern , end , m .start ()));
491
+ String match = m .group ();
492
+ if ("?" .equals (match )) {
493
+ patternBuilder .append ('.' );
494
+ }
495
+ else if ("*" .equals (match )) {
496
+ patternBuilder .append (".*" );
497
+ }
498
+ else if (match .startsWith ("{" ) && match .endsWith ("}" )) {
499
+ int colonIdx = match .indexOf (':' );
500
+ if (colonIdx == -1 ) {
501
+ patternBuilder .append (DEFAULT_VARIABLE_PATTERN );
502
+ this .variableNames .add (m .group (1 ));
503
+ }
504
+ else {
505
+ String variablePattern = match .substring (colonIdx + 1 , match .length () - 1 );
506
+ patternBuilder .append ('(' );
507
+ patternBuilder .append (variablePattern );
508
+ patternBuilder .append (')' );
509
+ String variableName = match .substring (1 , colonIdx );
510
+ this .variableNames .add (variableName );
511
+ }
512
+ }
513
+ end = m .end ();
514
+ }
515
+ patternBuilder .append (quote (pattern , end , pattern .length ()));
516
+ this .pattern = Pattern .compile (patternBuilder .toString ());
517
+ }
518
+
519
+ private String quote (String s , int start , int end ) {
520
+ if (start == end ) {
521
+ return "" ;
522
+ }
523
+ return Pattern .quote (s .substring (start , end ));
524
+ }
525
+
526
+ /**
527
+ * Main entry point.
528
+ * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
529
+ */
530
+ public boolean matchStrings (String str , Map <String , String > uriTemplateVariables ) {
531
+ Matcher matcher = this .pattern .matcher (str );
532
+ if (matcher .matches ()) {
533
+ if (uriTemplateVariables != null ) {
534
+ // SPR-8455
535
+ Assert .isTrue (this .variableNames .size () == matcher .groupCount (),
536
+ "The number of capturing groups in the pattern segment " + this .pattern +
537
+ " does not match the number of URI template variables it defines, which can occur if " +
538
+ " capturing groups are used in a URI template regex. Use non-capturing groups instead." );
539
+ for (int i = 1 ; i <= matcher .groupCount (); i ++) {
540
+ String name = this .variableNames .get (i - 1 );
541
+ String value = matcher .group (i );
542
+ uriTemplateVariables .put (name , value );
543
+ }
544
+ }
545
+ return true ;
546
+ }
547
+ else {
548
+ return false ;
549
+ }
550
+ }
551
+ }
552
+
465
553
}
0 commit comments