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