1 package org.apache.maven.shared.utils.io;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.StringTokenizer;
26
27 import javax.annotation.Nonnull;
28
29 /**
30 * <p>This is a utility class used by selectors and DirectoryScanner. The
31 * functionality more properly belongs just to selectors, but unfortunately
32 * DirectoryScanner exposed these as protected methods. Thus we have to
33 * support any subclasses of DirectoryScanner that may access these methods.
34 * </p>
35 * <p>This is a Singleton.</p>
36 *
37 * @author Arnout J. Kuiper
38 * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
39 * @author Magesh Umasankar
40 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
41 *
42 */
43 public final class SelectorUtils
44 {
45
46 private static final String PATTERN_HANDLER_PREFIX = "[";
47
48 public static final String PATTERN_HANDLER_SUFFIX = "]";
49
50 public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
51
52 public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
53
54 /**
55 * Private Constructor
56 */
57 private SelectorUtils()
58 {
59 }
60
61 /**
62 * Tests whether or not a given path matches the start of a given
63 * pattern up to the first "**".
64 * <p/>
65 * This is not a general purpose test and should only be used if you
66 * can live with false positives. For example, <code>pattern=**\a</code>
67 * and <code>str=b</code> will yield <code>true</code>.
68 *
69 * @param pattern The pattern to match against. Must not be
70 * <code>null</code>.
71 * @param str The path to match, as a String. Must not be
72 * <code>null</code>.
73 * @return whether or not a given path matches the start of a given
74 * pattern up to the first "**".
75 */
76 public static boolean matchPatternStart( String pattern, String str )
77 {
78 return matchPatternStart( pattern, str, true );
79 }
80
81 /**
82 * Tests whether or not a given path matches the start of a given
83 * pattern up to the first "**".
84 * <p/>
85 * This is not a general purpose test and should only be used if you
86 * can live with false positives. For example, <code>pattern=**\a</code>
87 * and <code>str=b</code> will yield <code>true</code>.
88 *
89 * @param pattern The pattern to match against. Must not be
90 * <code>null</code>.
91 * @param str The path to match, as a String. Must not be
92 * <code>null</code>.
93 * @param isCaseSensitive Whether or not matching should be performed
94 * case sensitively.
95 * @return whether or not a given path matches the start of a given
96 * pattern up to the first "**".
97 */
98 public static boolean matchPatternStart( String pattern, String str, boolean isCaseSensitive )
99 {
100 if ( isRegexPrefixedPattern( pattern ) )
101 {
102 // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
103 // a file to deal with, or we can definitely say this is an exclusion...
104 return true;
105 }
106 else
107 {
108 if ( isAntPrefixedPattern( pattern ) )
109 {
110 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
111 pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
112 }
113
114 String altPattern = pattern.replace( '\\', '/' );
115 String altStr = str.replace( '\\', '/' );
116
117 return matchAntPathPatternStart( altPattern, altStr, "/", isCaseSensitive );
118 }
119 }
120
121 private static boolean matchAntPathPatternStart( String pattern, String str, String separator,
122 boolean isCaseSensitive )
123 {
124 // When str starts with a File.separator, pattern has to start with a
125 // File.separator.
126 // When pattern starts with a File.separator, str has to start with a
127 // File.separator.
128 if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
129 {
130 return false;
131 }
132
133 List<String> patDirs = tokenizePath( pattern, separator );
134 List<String> strDirs = tokenizePath( str, separator );
135
136 int patIdxStart = 0;
137 int patIdxEnd = patDirs.size() - 1;
138 int strIdxStart = 0;
139 int strIdxEnd = strDirs.size() - 1;
140
141 // up to first '**'
142 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
143 {
144 String patDir = patDirs.get( patIdxStart );
145 if ( "**".equals( patDir ) )
146 {
147 break;
148 }
149 if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) )
150 {
151 return false;
152 }
153 patIdxStart++;
154 strIdxStart++;
155 }
156
157 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
158 }
159
160 /**
161 * Tests whether or not a given path matches a given pattern.
162 *
163 * @param pattern The pattern to match against. Must not be
164 * <code>null</code>.
165 * @param str The path to match, as a String. Must not be
166 * <code>null</code>.
167 * @return <code>true</code> if the pattern matches against the string,
168 * or <code>false</code> otherwise.
169 */
170 public static boolean matchPath( String pattern, String str )
171 {
172 return matchPath( pattern, str, true );
173 }
174
175 /**
176 * Tests whether or not a given path matches a given pattern.
177 *
178 * @param pattern The pattern to match against. Must not be
179 * <code>null</code>.
180 * @param str The path to match, as a String. Must not be
181 * <code>null</code>.
182 * @param isCaseSensitive Whether or not matching should be performed
183 * case sensitively.
184 * @return <code>true</code> if the pattern matches against the string,
185 * or <code>false</code> otherwise.
186 */
187 public static boolean matchPath( String pattern, String str, boolean isCaseSensitive )
188 {
189 if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
190 && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
191 {
192 pattern =
193 pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
194
195 return str.matches( pattern );
196 }
197 else
198 {
199 if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
200 && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
201 {
202 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
203 pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
204 }
205
206 return matchAntPathPattern( pattern, str, isCaseSensitive );
207 }
208 }
209
210 private static boolean matchAntPathPattern( String pattern, String str, boolean isCaseSensitive )
211 {
212 // When str starts with a File.separator, pattern has to start with a
213 // File.separator.
214 // When pattern starts with a File.separator, str has to start with a
215 // File.separator.
216 if ( str.startsWith( File.separator ) != pattern.startsWith( File.separator ) )
217 {
218 return false;
219 }
220
221 List<String> patDirs = tokenizePath( pattern, File.separator );
222 List<String> strDirs = tokenizePath( str, File.separator );
223
224 int patIdxStart = 0;
225 int patIdxEnd = patDirs.size() - 1;
226 int strIdxStart = 0;
227 int strIdxEnd = strDirs.size() - 1;
228
229 // up to first '**'
230 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
231 {
232 String patDir = patDirs.get( patIdxStart );
233 if ( "**".equals( patDir ) )
234 {
235 break;
236 }
237 if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) )
238 {
239 return false;
240 }
241 patIdxStart++;
242 strIdxStart++;
243 }
244 if ( strIdxStart > strIdxEnd )
245 {
246 // String is exhausted
247 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
248 {
249 if ( !"**".equals( patDirs.get( i ) ) )
250 {
251 return false;
252 }
253 }
254 return true;
255 }
256 else
257 {
258 if ( patIdxStart > patIdxEnd )
259 {
260 // String not exhausted, but pattern is. Failure.
261 return false;
262 }
263 }
264
265 // up to last '**'
266 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
267 {
268 String patDir = patDirs.get( patIdxEnd );
269 if ( "**".equals( patDir ) )
270 {
271 break;
272 }
273 if ( !match( patDir, strDirs.get( strIdxEnd ), isCaseSensitive ) )
274 {
275 return false;
276 }
277 patIdxEnd--;
278 strIdxEnd--;
279 }
280 if ( strIdxStart > strIdxEnd )
281 {
282 // String is exhausted
283 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
284 {
285 if ( !"**".equals( patDirs.get( i ) ) )
286 {
287 return false;
288 }
289 }
290 return true;
291 }
292
293 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
294 {
295 int patIdxTmp = -1;
296 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
297 {
298 if ( "**".equals( patDirs.get( i ) ) )
299 {
300 patIdxTmp = i;
301 break;
302 }
303 }
304 if ( patIdxTmp == patIdxStart + 1 )
305 {
306 // '**/**' situation, so skip one
307 patIdxStart++;
308 continue;
309 }
310 // Find the pattern between padIdxStart & padIdxTmp in str between
311 // strIdxStart & strIdxEnd
312 int patLength = ( patIdxTmp - patIdxStart - 1 );
313 int strLength = ( strIdxEnd - strIdxStart + 1 );
314 int foundIdx = -1;
315 strLoop:
316 for ( int i = 0; i <= strLength - patLength; i++ )
317 {
318 for ( int j = 0; j < patLength; j++ )
319 {
320 String subPat = patDirs.get( patIdxStart + j + 1 );
321 String subStr = strDirs.get( strIdxStart + i + j );
322 if ( !match( subPat, subStr, isCaseSensitive ) )
323 {
324 continue strLoop;
325 }
326 }
327
328 foundIdx = strIdxStart + i;
329 break;
330 }
331
332 if ( foundIdx == -1 )
333 {
334 return false;
335 }
336
337 patIdxStart = patIdxTmp;
338 strIdxStart = foundIdx + patLength;
339 }
340
341 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
342 {
343 if ( !"**".equals( patDirs.get( i ) ) )
344 {
345 return false;
346 }
347 }
348
349 return true;
350 }
351
352 /**
353 * Tests whether or not a string matches against a pattern.
354 * The pattern may contain two special characters:<br>
355 * '*' means zero or more characters<br>
356 * '?' means one and only one character
357 *
358 * @param pattern The pattern to match against.
359 * Must not be <code>null</code>.
360 * @param str The string which must be matched against the pattern.
361 * Must not be <code>null</code>.
362 * @return <code>true</code> if the string matches against the pattern,
363 * or <code>false</code> otherwise.
364 */
365 public static boolean match( String pattern, String str )
366 {
367 return match( pattern, str, true );
368 }
369
370 /**
371 * Tests whether or not a string matches against a pattern.
372 * The pattern may contain two special characters:<br>
373 * '*' means zero or more characters<br>
374 * '?' means one and only one character
375 *
376 * @param pattern The pattern to match against.
377 * Must not be <code>null</code>.
378 * @param str The string which must be matched against the pattern.
379 * Must not be <code>null</code>.
380 * @param isCaseSensitive Whether or not matching should be performed
381 * case sensitively.
382 * @return <code>true</code> if the string matches against the pattern,
383 * or <code>false</code> otherwise.
384 */
385 public static boolean match( String pattern, String str, boolean isCaseSensitive )
386 {
387 char[] patArr = pattern.toCharArray();
388 char[] strArr = str.toCharArray();
389 int patIdxStart = 0;
390 int patIdxEnd = patArr.length - 1;
391 int strIdxStart = 0;
392 int strIdxEnd = strArr.length - 1;
393 char ch;
394
395 boolean containsStar = false;
396 for ( char aPatArr : patArr )
397 {
398 if ( aPatArr == '*' )
399 {
400 containsStar = true;
401 break;
402 }
403 }
404
405 if ( !containsStar )
406 {
407 // No '*'s, so we make a shortcut
408 if ( patIdxEnd != strIdxEnd )
409 {
410 return false; // Pattern and string do not have the same size
411 }
412 for ( int i = 0; i <= patIdxEnd; i++ )
413 {
414 ch = patArr[i];
415 if ( ch != '?' && !equals( ch, strArr[i], isCaseSensitive ) )
416 {
417 return false; // Character mismatch
418 }
419 }
420 return true; // String matches against pattern
421 }
422
423 if ( patIdxEnd == 0 )
424 {
425 return true; // Pattern contains only '*', which matches anything
426 }
427
428 // Process characters before first star
429 while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
430 {
431 if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) )
432 {
433 return false; // Character mismatch
434 }
435 patIdxStart++;
436 strIdxStart++;
437 }
438 if ( strIdxStart > strIdxEnd )
439 {
440 // All characters in the string are used. Check if only '*'s are
441 // left in the pattern. If so, we succeeded. Otherwise failure.
442 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
443 {
444 if ( patArr[i] != '*' )
445 {
446 return false;
447 }
448 }
449 return true;
450 }
451
452 // Process characters after last star
453 while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
454 {
455 if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) )
456 {
457 return false; // Character mismatch
458 }
459 patIdxEnd--;
460 strIdxEnd--;
461 }
462 if ( strIdxStart > strIdxEnd )
463 {
464 // All characters in the string are used. Check if only '*'s are
465 // left in the pattern. If so, we succeeded. Otherwise failure.
466 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
467 {
468 if ( patArr[i] != '*' )
469 {
470 return false;
471 }
472 }
473 return true;
474 }
475
476 // process pattern between stars. padIdxStart and patIdxEnd point
477 // always to a '*'.
478 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
479 {
480 int patIdxTmp = -1;
481 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
482 {
483 if ( patArr[i] == '*' )
484 {
485 patIdxTmp = i;
486 break;
487 }
488 }
489 if ( patIdxTmp == patIdxStart + 1 )
490 {
491 // Two stars next to each other, skip the first one.
492 patIdxStart++;
493 continue;
494 }
495 // Find the pattern between padIdxStart & padIdxTmp in str between
496 // strIdxStart & strIdxEnd
497 int patLength = ( patIdxTmp - patIdxStart - 1 );
498 int strLength = ( strIdxEnd - strIdxStart + 1 );
499 int foundIdx = -1;
500 strLoop:
501 for ( int i = 0; i <= strLength - patLength; i++ )
502 {
503 for ( int j = 0; j < patLength; j++ )
504 {
505 ch = patArr[patIdxStart + j + 1];
506 if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) )
507 {
508 continue strLoop;
509 }
510 }
511
512 foundIdx = strIdxStart + i;
513 break;
514 }
515
516 if ( foundIdx == -1 )
517 {
518 return false;
519 }
520
521 patIdxStart = patIdxTmp;
522 strIdxStart = foundIdx + patLength;
523 }
524
525 // All characters in the string are used. Check if only '*'s are left
526 // in the pattern. If so, we succeeded. Otherwise failure.
527 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
528 {
529 if ( patArr[i] != '*' )
530 {
531 return false;
532 }
533 }
534 return true;
535 }
536
537 /**
538 * Tests whether two characters are equal.
539 */
540 private static boolean equals( char c1, char c2, boolean isCaseSensitive )
541 {
542 if ( c1 == c2 )
543 {
544 return true;
545 }
546 if ( !isCaseSensitive )
547 {
548 // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
549 if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 )
550 || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) )
551 {
552 return true;
553 }
554 }
555 return false;
556 }
557
558 /**
559 * Breaks a path up into a List of path elements, tokenizing on
560 * <code>File.separator</code>.
561 *
562 * @param path Path to tokenize. Must not be <code>null</code>.
563 * @param separator The separator to use
564 * @return a List of path elements from the tokenized path
565 */
566 private static List<String> tokenizePath( String path, String separator )
567 {
568 List<String> ret = new ArrayList<String>();
569 StringTokenizer st = new StringTokenizer( path, separator );
570 while ( st.hasMoreTokens() )
571 {
572 ret.add( st.nextToken() );
573 }
574 return ret;
575 }
576
577
578 static boolean matchAntPathPatternStart( @Nonnull MatchPattern pattern,
579 @Nonnull String str,
580 @Nonnull String separator,
581 boolean isCaseSensitive )
582 {
583 return !separatorPatternStartSlashMismatch( pattern, str, separator )
584 && matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
585 }
586
587 private static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator )
588 {
589 List<String> ret = new ArrayList<String>();
590 StringTokenizer st = new StringTokenizer( path, separator );
591 while ( st.hasMoreTokens() )
592 {
593 ret.add( st.nextToken() );
594 }
595 return ret.toArray( new String[ret.size()] );
596 }
597
598 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
599 @Nonnull String str,
600 @Nonnull String separator,
601 boolean isCaseSensitive )
602 {
603 String[] strDirs = tokenizePathToString( str, separator );
604 return matchAntPathPatternStart( patDirs, strDirs, isCaseSensitive );
605 }
606
607 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
608 @Nonnull String[] tokenizedFileName,
609 boolean isCaseSensitive )
610 {
611
612 int patIdxStart = 0;
613 int patIdxEnd = patDirs.length - 1;
614 int strIdxStart = 0;
615 int strIdxEnd = tokenizedFileName.length - 1;
616
617 // up to first '**'
618 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
619 {
620 String patDir = patDirs[patIdxStart];
621 if ( patDir.equals( "**" ) )
622 {
623 break;
624 }
625 if ( !match( patDir, tokenizedFileName[strIdxStart], isCaseSensitive ) )
626 {
627 return false;
628 }
629 patIdxStart++;
630 strIdxStart++;
631 }
632
633 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
634 }
635
636 private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str,
637 @Nonnull String separator )
638 {
639 return str.startsWith( separator ) != matchPattern.startsWith( separator );
640 }
641
642 private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
643 {
644 return str.startsWith( separator ) != pattern.startsWith( separator );
645 }
646
647
648 static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
649 {
650 int patIdxStart = 0;
651 int patIdxEnd = patDirs.length - 1;
652 int strIdxStart = 0;
653 int strIdxEnd = strDirs.length - 1;
654
655 // up to first '**'
656 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
657 {
658 String patDir = patDirs[patIdxStart];
659 if ( patDir.equals( "**" ) )
660 {
661 break;
662 }
663 if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
664 {
665 return false;
666 }
667 patIdxStart++;
668 strIdxStart++;
669 }
670 if ( strIdxStart > strIdxEnd )
671 {
672 // String is exhausted
673 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
674 {
675 if ( !patDirs[i].equals( "**" ) )
676 {
677 return false;
678 }
679 }
680 return true;
681 }
682 else
683 {
684 if ( patIdxStart > patIdxEnd )
685 {
686 // String not exhausted, but pattern is. Failure.
687 return false;
688 }
689 }
690
691 // up to last '**'
692 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
693 {
694 String patDir = patDirs[patIdxEnd];
695 if ( patDir.equals( "**" ) )
696 {
697 break;
698 }
699 if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
700 {
701 return false;
702 }
703 patIdxEnd--;
704 strIdxEnd--;
705 }
706 if ( strIdxStart > strIdxEnd )
707 {
708 // String is exhausted
709 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
710 {
711 if ( !patDirs[i].equals( "**" ) )
712 {
713 return false;
714 }
715 }
716 return true;
717 }
718
719 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
720 {
721 int patIdxTmp = -1;
722 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
723 {
724 if ( patDirs[i].equals( "**" ) )
725 {
726 patIdxTmp = i;
727 break;
728 }
729 }
730 if ( patIdxTmp == patIdxStart + 1 )
731 {
732 // '**/**' situation, so skip one
733 patIdxStart++;
734 continue;
735 }
736 // Find the pattern between padIdxStart & padIdxTmp in str between
737 // strIdxStart & strIdxEnd
738 int patLength = ( patIdxTmp - patIdxStart - 1 );
739 int strLength = ( strIdxEnd - strIdxStart + 1 );
740 int foundIdx = -1;
741 strLoop:
742 for ( int i = 0; i <= strLength - patLength; i++ )
743 {
744 for ( int j = 0; j < patLength; j++ )
745 {
746 String subPat = patDirs[patIdxStart + j + 1];
747 String subStr = strDirs[strIdxStart + i + j];
748 if ( !match( subPat, subStr, isCaseSensitive ) )
749 {
750 continue strLoop;
751 }
752 }
753
754 foundIdx = strIdxStart + i;
755 break;
756 }
757
758 if ( foundIdx == -1 )
759 {
760 return false;
761 }
762
763 patIdxStart = patIdxTmp;
764 strIdxStart = foundIdx + patLength;
765 }
766
767 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
768 {
769 if ( !patDirs[i].equals( "**" ) )
770 {
771 return false;
772 }
773 }
774
775 return true;
776 }
777
778 static boolean isRegexPrefixedPattern( String pattern )
779 {
780 return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
781 && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
782 }
783
784 static boolean isAntPrefixedPattern( String pattern )
785 {
786 return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
787 && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
788 }
789
790 static boolean matchAntPathPattern( @Nonnull MatchPattern matchPattern, @Nonnull String str, @Nonnull String separator,
791 boolean isCaseSensitive )
792 {
793 if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) )
794 {
795 return false;
796 }
797 String[] patDirs = matchPattern.getTokenizedPathString();
798 String[] strDirs = tokenizePathToString( str, separator );
799 return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
800 }
801 }