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