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 @SuppressWarnings("SimplifiableIfStatement")
579 static boolean matchAntPathPatternStart( @Nonnull MatchPattern pattern,
580 @Nonnull String str,
581 @Nonnull String separator,
582 boolean isCaseSensitive )
583 {
584 if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
585 {
586 return false;
587 }
588
589 return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
590 }
591
592 private static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator )
593 {
594 List<String> ret = new ArrayList<String>();
595 StringTokenizer st = new StringTokenizer( path, separator );
596 while ( st.hasMoreTokens() )
597 {
598 ret.add( st.nextToken() );
599 }
600 return ret.toArray( new String[ret.size()] );
601 }
602
603 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
604 @Nonnull String str,
605 @Nonnull String separator,
606 boolean isCaseSensitive )
607 {
608 String[] strDirs = tokenizePathToString( str, separator );
609 return matchAntPathPatternStart( patDirs, strDirs, isCaseSensitive );
610 }
611
612 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
613 @Nonnull String[] tokenizedFileName,
614 boolean isCaseSensitive )
615 {
616
617 int patIdxStart = 0;
618 int patIdxEnd = patDirs.length - 1;
619 int strIdxStart = 0;
620 int strIdxEnd = tokenizedFileName.length - 1;
621
622 // up to first '**'
623 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
624 {
625 String patDir = patDirs[patIdxStart];
626 if ( patDir.equals( "**" ) )
627 {
628 break;
629 }
630 if ( !match( patDir, tokenizedFileName[strIdxStart], isCaseSensitive ) )
631 {
632 return false;
633 }
634 patIdxStart++;
635 strIdxStart++;
636 }
637
638 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
639 }
640
641 private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str,
642 @Nonnull String separator )
643 {
644 return str.startsWith( separator ) != matchPattern.startsWith( separator );
645 }
646
647 private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
648 {
649 return str.startsWith( separator ) != pattern.startsWith( separator );
650 }
651
652
653 static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
654 {
655 int patIdxStart = 0;
656 int patIdxEnd = patDirs.length - 1;
657 int strIdxStart = 0;
658 int strIdxEnd = strDirs.length - 1;
659
660 // up to first '**'
661 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
662 {
663 String patDir = patDirs[patIdxStart];
664 if ( patDir.equals( "**" ) )
665 {
666 break;
667 }
668 if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
669 {
670 return false;
671 }
672 patIdxStart++;
673 strIdxStart++;
674 }
675 if ( strIdxStart > strIdxEnd )
676 {
677 // String is exhausted
678 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
679 {
680 if ( !patDirs[i].equals( "**" ) )
681 {
682 return false;
683 }
684 }
685 return true;
686 }
687 else
688 {
689 if ( patIdxStart > patIdxEnd )
690 {
691 // String not exhausted, but pattern is. Failure.
692 return false;
693 }
694 }
695
696 // up to last '**'
697 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
698 {
699 String patDir = patDirs[patIdxEnd];
700 if ( patDir.equals( "**" ) )
701 {
702 break;
703 }
704 if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
705 {
706 return false;
707 }
708 patIdxEnd--;
709 strIdxEnd--;
710 }
711 if ( strIdxStart > strIdxEnd )
712 {
713 // String is exhausted
714 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
715 {
716 if ( !patDirs[i].equals( "**" ) )
717 {
718 return false;
719 }
720 }
721 return true;
722 }
723
724 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
725 {
726 int patIdxTmp = -1;
727 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
728 {
729 if ( patDirs[i].equals( "**" ) )
730 {
731 patIdxTmp = i;
732 break;
733 }
734 }
735 if ( patIdxTmp == patIdxStart + 1 )
736 {
737 // '**/**' situation, so skip one
738 patIdxStart++;
739 continue;
740 }
741 // Find the pattern between padIdxStart & padIdxTmp in str between
742 // strIdxStart & strIdxEnd
743 int patLength = ( patIdxTmp - patIdxStart - 1 );
744 int strLength = ( strIdxEnd - strIdxStart + 1 );
745 int foundIdx = -1;
746 strLoop:
747 for ( int i = 0; i <= strLength - patLength; i++ )
748 {
749 for ( int j = 0; j < patLength; j++ )
750 {
751 String subPat = patDirs[patIdxStart + j + 1];
752 String subStr = strDirs[strIdxStart + i + j];
753 if ( !match( subPat, subStr, isCaseSensitive ) )
754 {
755 continue strLoop;
756 }
757 }
758
759 foundIdx = strIdxStart + i;
760 break;
761 }
762
763 if ( foundIdx == -1 )
764 {
765 return false;
766 }
767
768 patIdxStart = patIdxTmp;
769 strIdxStart = foundIdx + patLength;
770 }
771
772 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
773 {
774 if ( !patDirs[i].equals( "**" ) )
775 {
776 return false;
777 }
778 }
779
780 return true;
781 }
782
783 static boolean isRegexPrefixedPattern( String pattern )
784 {
785 return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
786 && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
787 }
788
789 static boolean isAntPrefixedPattern( String pattern )
790 {
791 return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
792 && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
793 }
794
795 static boolean matchAntPathPattern( @Nonnull MatchPattern matchPattern, @Nonnull String str, @Nonnull String separator,
796 boolean isCaseSensitive )
797 {
798 if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) )
799 {
800 return false;
801 }
802 String[] patDirs = matchPattern.getTokenizedPathString();
803 String[] strDirs = tokenizePathToString( str, separator );
804 return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
805 }
806 }