Coverage Report - org.apache.maven.shared.utils.io.SelectorUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
SelectorUtils
74%
207/278
66%
165/248
8.9
 
 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  0
     {
 59  0
     }
 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  12
         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  12
         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  1
             return true;
 105  
         }
 106  
         else
 107  
         {
 108  10
             if ( isAntPrefixedPattern( pattern ) )
 109  
             {
 110  9
                 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
 111  
                                              pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 112  
             }
 113  
 
 114  10
             String altPattern = pattern.replace( '\\', '/' );
 115  10
             String altStr = str.replace( '\\', '/' );
 116  
 
 117  10
             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  10
         if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
 129  
         {
 130  2
             return false;
 131  
         }
 132  
 
 133  8
         List<String> patDirs = tokenizePath( pattern, separator );
 134  8
         List<String> strDirs = tokenizePath( str, separator );
 135  
 
 136  8
         int patIdxStart = 0;
 137  8
         int patIdxEnd = patDirs.size() - 1;
 138  8
         int strIdxStart = 0;
 139  8
         int strIdxEnd = strDirs.size() - 1;
 140  
 
 141  
         // up to first '**'
 142  13
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 143  
         {
 144  9
             String patDir = patDirs.get( patIdxStart );
 145  9
             if ( "**".equals( patDir ) )
 146  
             {
 147  2
                 break;
 148  
             }
 149  7
             if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) )
 150  
             {
 151  2
                 return false;
 152  
             }
 153  5
             patIdxStart++;
 154  5
             strIdxStart++;
 155  5
         }
 156  
 
 157  6
         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  0
         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  319
         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  0
             pattern =
 193  
                 pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 194  
 
 195  0
             return str.matches( pattern );
 196  
         }
 197  
         else
 198  
         {
 199  319
             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  0
                 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
 203  
                                              pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 204  
             }
 205  
 
 206  319
             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  319
         if ( str.startsWith( File.separator ) != pattern.startsWith( File.separator ) )
 217  
         {
 218  0
             return false;
 219  
         }
 220  
 
 221  319
         List<String> patDirs = tokenizePath( pattern, File.separator );
 222  319
         List<String> strDirs = tokenizePath( str, File.separator );
 223  
 
 224  319
         int patIdxStart = 0;
 225  319
         int patIdxEnd = patDirs.size() - 1;
 226  319
         int strIdxStart = 0;
 227  319
         int strIdxEnd = strDirs.size() - 1;
 228  
 
 229  
         // up to first '**'
 230  319
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 231  
         {
 232  319
             String patDir = patDirs.get( patIdxStart );
 233  319
             if ( "**".equals( patDir ) )
 234  
             {
 235  319
                 break;
 236  
             }
 237  0
             if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) )
 238  
             {
 239  0
                 return false;
 240  
             }
 241  0
             patIdxStart++;
 242  0
             strIdxStart++;
 243  0
         }
 244  319
         if ( strIdxStart > strIdxEnd )
 245  
         {
 246  
             // String is exhausted
 247  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 248  
             {
 249  0
                 if ( !"**".equals( patDirs.get( i ) ) )
 250  
                 {
 251  0
                     return false;
 252  
                 }
 253  
             }
 254  0
             return true;
 255  
         }
 256  
         else
 257  
         {
 258  319
             if ( patIdxStart > patIdxEnd )
 259  
             {
 260  
                 // String not exhausted, but pattern is. Failure.
 261  0
                 return false;
 262  
             }
 263  
         }
 264  
 
 265  
         // up to last '**'
 266  323
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 267  
         {
 268  322
             String patDir = patDirs.get( patIdxEnd );
 269  322
             if ( "**".equals( patDir ) )
 270  
             {
 271  110
                 break;
 272  
             }
 273  212
             if ( !match( patDir, strDirs.get( strIdxEnd ), isCaseSensitive ) )
 274  
             {
 275  208
                 return false;
 276  
             }
 277  4
             patIdxEnd--;
 278  4
             strIdxEnd--;
 279  4
         }
 280  111
         if ( strIdxStart > strIdxEnd )
 281  
         {
 282  
             // String is exhausted
 283  2
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 284  
             {
 285  1
                 if ( !"**".equals( patDirs.get( i ) ) )
 286  
                 {
 287  0
                     return false;
 288  
                 }
 289  
             }
 290  1
             return true;
 291  
         }
 292  
 
 293  110
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 294  
         {
 295  103
             int patIdxTmp = -1;
 296  206
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 297  
             {
 298  206
                 if ( "**".equals( patDirs.get( i ) ) )
 299  
                 {
 300  103
                     patIdxTmp = i;
 301  103
                     break;
 302  
                 }
 303  
             }
 304  103
             if ( patIdxTmp == patIdxStart + 1 )
 305  
             {
 306  
                 // '**/**' situation, so skip one
 307  0
                 patIdxStart++;
 308  0
                 continue;
 309  
             }
 310  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 311  
             // strIdxStart & strIdxEnd
 312  103
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 313  103
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 314  103
             int foundIdx = -1;
 315  
             strLoop:
 316  283
             for ( int i = 0; i <= strLength - patLength; i++ )
 317  
             {
 318  180
                 for ( int j = 0; j < patLength; j++ )
 319  
                 {
 320  180
                     String subPat = patDirs.get( patIdxStart + j + 1 );
 321  180
                     String subStr = strDirs.get( strIdxStart + i + j );
 322  180
                     if ( !match( subPat, subStr, isCaseSensitive ) )
 323  
                     {
 324  180
                         continue strLoop;
 325  
                     }
 326  
                 }
 327  
 
 328  0
                 foundIdx = strIdxStart + i;
 329  0
                 break;
 330  
             }
 331  
 
 332  103
             if ( foundIdx == -1 )
 333  
             {
 334  103
                 return false;
 335  
             }
 336  
 
 337  0
             patIdxStart = patIdxTmp;
 338  0
             strIdxStart = foundIdx + patLength;
 339  0
         }
 340  
 
 341  14
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 342  
         {
 343  7
             if ( !"**".equals( patDirs.get( i ) ) )
 344  
             {
 345  0
                 return false;
 346  
             }
 347  
         }
 348  
 
 349  7
         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  0
         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  1223
         char[] patArr = pattern.toCharArray();
 388  1223
         char[] strArr = str.toCharArray();
 389  1223
         int patIdxStart = 0;
 390  1223
         int patIdxEnd = patArr.length - 1;
 391  1223
         int strIdxStart = 0;
 392  1223
         int strIdxEnd = strArr.length - 1;
 393  
         char ch;
 394  
 
 395  1223
         boolean containsStar = false;
 396  8353
         for ( char aPatArr : patArr )
 397  
         {
 398  7354
             if ( aPatArr == '*' )
 399  
             {
 400  224
                 containsStar = true;
 401  224
                 break;
 402  
             }
 403  
         }
 404  
 
 405  1223
         if ( !containsStar )
 406  
         {
 407  
             // No '*'s, so we make a shortcut
 408  999
             if ( patIdxEnd != strIdxEnd )
 409  
             {
 410  820
                 return false; // Pattern and string do not have the same size
 411  
             }
 412  210
             for ( int i = 0; i <= patIdxEnd; i++ )
 413  
             {
 414  201
                 ch = patArr[i];
 415  201
                 if ( ch != '?' && !equals( ch, strArr[i], isCaseSensitive ) )
 416  
                 {
 417  170
                     return false; // Character mismatch
 418  
                 }
 419  
             }
 420  9
             return true; // String matches against pattern
 421  
         }
 422  
 
 423  224
         if ( patIdxEnd == 0 )
 424  
         {
 425  0
             return true; // Pattern contains only '*', which matches anything
 426  
         }
 427  
 
 428  
         // Process characters before first star
 429  241
         while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
 430  
         {
 431  161
             if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) )
 432  
             {
 433  144
                 return false; // Character mismatch
 434  
             }
 435  17
             patIdxStart++;
 436  17
             strIdxStart++;
 437  
         }
 438  80
         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  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 443  
             {
 444  0
                 if ( patArr[i] != '*' )
 445  
                 {
 446  0
                     return false;
 447  
                 }
 448  
             }
 449  0
             return true;
 450  
         }
 451  
 
 452  
         // Process characters after last star
 453  124
         while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
 454  
         {
 455  113
             if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) )
 456  
             {
 457  69
                 return false; // Character mismatch
 458  
             }
 459  44
             patIdxEnd--;
 460  44
             strIdxEnd--;
 461  
         }
 462  11
         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  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 467  
             {
 468  0
                 if ( patArr[i] != '*' )
 469  
                 {
 470  0
                     return false;
 471  
                 }
 472  
             }
 473  0
             return true;
 474  
         }
 475  
 
 476  
         // process pattern between stars. padIdxStart and patIdxEnd point
 477  
         // always to a '*'.
 478  13
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 479  
         {
 480  2
             int patIdxTmp = -1;
 481  2
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 482  
             {
 483  2
                 if ( patArr[i] == '*' )
 484  
                 {
 485  2
                     patIdxTmp = i;
 486  2
                     break;
 487  
                 }
 488  
             }
 489  2
             if ( patIdxTmp == patIdxStart + 1 )
 490  
             {
 491  
                 // Two stars next to each other, skip the first one.
 492  2
                 patIdxStart++;
 493  2
                 continue;
 494  
             }
 495  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 496  
             // strIdxStart & strIdxEnd
 497  0
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 498  0
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 499  0
             int foundIdx = -1;
 500  
             strLoop:
 501  0
             for ( int i = 0; i <= strLength - patLength; i++ )
 502  
             {
 503  0
                 for ( int j = 0; j < patLength; j++ )
 504  
                 {
 505  0
                     ch = patArr[patIdxStart + j + 1];
 506  0
                     if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) )
 507  
                     {
 508  0
                         continue strLoop;
 509  
                     }
 510  
                 }
 511  
 
 512  0
                 foundIdx = strIdxStart + i;
 513  0
                 break;
 514  
             }
 515  
 
 516  0
             if ( foundIdx == -1 )
 517  
             {
 518  0
                 return false;
 519  
             }
 520  
 
 521  0
             patIdxStart = patIdxTmp;
 522  0
             strIdxStart = foundIdx + patLength;
 523  0
         }
 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  22
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 528  
         {
 529  11
             if ( patArr[i] != '*' )
 530  
             {
 531  0
                 return false;
 532  
             }
 533  
         }
 534  11
         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  475
         if ( c1 == c2 )
 543  
         {
 544  92
             return true;
 545  
         }
 546  383
         if ( !isCaseSensitive )
 547  
         {
 548  
             // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
 549  0
             if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 )
 550  
                 || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) )
 551  
             {
 552  0
                 return true;
 553  
             }
 554  
         }
 555  383
         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  654
         List<String> ret = new ArrayList<String>();
 569  654
         StringTokenizer st = new StringTokenizer( path, separator );
 570  1979
         while ( st.hasMoreTokens() )
 571  
         {
 572  1325
             ret.add( st.nextToken() );
 573  
         }
 574  654
         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  8
         if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
 585  
         {
 586  0
             return false;
 587  
         }
 588  
 
 589  8
         return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
 590  
     }
 591  
 
 592  
     private static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator )
 593  
     {
 594  9
         List<String> ret = new ArrayList<String>();
 595  9
         StringTokenizer st = new StringTokenizer( path, separator );
 596  18
         while ( st.hasMoreTokens() )
 597  
         {
 598  9
             ret.add( st.nextToken() );
 599  
         }
 600  9
         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  8
         String[] strDirs = tokenizePathToString( str, separator );
 609  
 
 610  8
         int patIdxStart = 0;
 611  8
         int patIdxEnd = patDirs.length - 1;
 612  8
         int strIdxStart = 0;
 613  8
         int strIdxEnd = strDirs.length - 1;
 614  
 
 615  
         // up to first '**'
 616  8
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 617  
         {
 618  6
             String patDir = patDirs[patIdxStart];
 619  6
             if ( patDir.equals( "**" ) )
 620  
             {
 621  6
                 break;
 622  
             }
 623  0
             if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
 624  
             {
 625  0
                 return false;
 626  
             }
 627  0
             patIdxStart++;
 628  0
             strIdxStart++;
 629  0
         }
 630  
 
 631  8
         return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
 632  
     }
 633  
 
 634  
     private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str,
 635  
                                                                @Nonnull String separator )
 636  
     {
 637  9
         return str.startsWith( separator ) != matchPattern.startsWith( separator );
 638  
     }
 639  
 
 640  
     private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
 641  
     {
 642  10
         return str.startsWith( separator ) != pattern.startsWith( separator );
 643  
     }
 644  
 
 645  
 
 646  
     static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
 647  
     {
 648  852
         int patIdxStart = 0;
 649  852
         int patIdxEnd = patDirs.length - 1;
 650  852
         int strIdxStart = 0;
 651  852
         int strIdxEnd = strDirs.length - 1;
 652  
 
 653  
         // up to first '**'
 654  855
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 655  
         {
 656  762
             String patDir = patDirs[patIdxStart];
 657  762
             if ( patDir.equals( "**" ) )
 658  
             {
 659  736
                 break;
 660  
             }
 661  26
             if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
 662  
             {
 663  23
                 return false;
 664  
             }
 665  3
             patIdxStart++;
 666  3
             strIdxStart++;
 667  3
         }
 668  829
         if ( strIdxStart > strIdxEnd )
 669  
         {
 670  
             // String is exhausted
 671  179
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 672  
             {
 673  168
                 if ( !patDirs[i].equals( "**" ) )
 674  
                 {
 675  82
                     return false;
 676  
                 }
 677  
             }
 678  11
             return true;
 679  
         }
 680  
         else
 681  
         {
 682  736
             if ( patIdxStart > patIdxEnd )
 683  
             {
 684  
                 // String not exhausted, but pattern is. Failure.
 685  0
                 return false;
 686  
             }
 687  
         }
 688  
 
 689  
         // up to last '**'
 690  744
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 691  
         {
 692  740
             String patDir = patDirs[patIdxEnd];
 693  740
             if ( patDir.equals( "**" ) )
 694  
             {
 695  280
                 break;
 696  
             }
 697  460
             if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
 698  
             {
 699  452
                 return false;
 700  
             }
 701  8
             patIdxEnd--;
 702  8
             strIdxEnd--;
 703  8
         }
 704  284
         if ( strIdxStart > strIdxEnd )
 705  
         {
 706  
             // String is exhausted
 707  8
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 708  
             {
 709  4
                 if ( !patDirs[i].equals( "**" ) )
 710  
                 {
 711  0
                     return false;
 712  
                 }
 713  
             }
 714  4
             return true;
 715  
         }
 716  
 
 717  280
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 718  
         {
 719  234
             int patIdxTmp = -1;
 720  468
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 721  
             {
 722  468
                 if ( patDirs[i].equals( "**" ) )
 723  
                 {
 724  234
                     patIdxTmp = i;
 725  234
                     break;
 726  
                 }
 727  
             }
 728  234
             if ( patIdxTmp == patIdxStart + 1 )
 729  
             {
 730  
                 // '**/**' situation, so skip one
 731  0
                 patIdxStart++;
 732  0
                 continue;
 733  
             }
 734  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 735  
             // strIdxStart & strIdxEnd
 736  234
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 737  234
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 738  234
             int foundIdx = -1;
 739  
             strLoop:
 740  572
             for ( int i = 0; i <= strLength - patLength; i++ )
 741  
             {
 742  338
                 for ( int j = 0; j < patLength; j++ )
 743  
                 {
 744  338
                     String subPat = patDirs[patIdxStart + j + 1];
 745  338
                     String subStr = strDirs[strIdxStart + i + j];
 746  338
                     if ( !match( subPat, subStr, isCaseSensitive ) )
 747  
                     {
 748  338
                         continue strLoop;
 749  
                     }
 750  
                 }
 751  
 
 752  0
                 foundIdx = strIdxStart + i;
 753  0
                 break;
 754  
             }
 755  
 
 756  234
             if ( foundIdx == -1 )
 757  
             {
 758  234
                 return false;
 759  
             }
 760  
 
 761  0
             patIdxStart = patIdxTmp;
 762  0
             strIdxStart = foundIdx + patLength;
 763  0
         }
 764  
 
 765  92
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 766  
         {
 767  46
             if ( !patDirs[i].equals( "**" ) )
 768  
             {
 769  0
                 return false;
 770  
             }
 771  
         }
 772  
 
 773  46
         return true;
 774  
     }
 775  
 
 776  
     static boolean isRegexPrefixedPattern( String pattern )
 777  
     {
 778  179
         return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
 779  
             && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
 780  
     }
 781  
 
 782  
     static boolean isAntPrefixedPattern( String pattern )
 783  
     {
 784  177
         return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
 785  
             && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
 786  
     }
 787  
 
 788  
     static boolean matchAntPathPattern( @Nonnull MatchPattern matchPattern, @Nonnull String str, @Nonnull String separator,
 789  
                                         boolean isCaseSensitive )
 790  
     {
 791  1
         if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) )
 792  
         {
 793  0
             return false;
 794  
         }
 795  1
         String[] patDirs = matchPattern.getTokenizedPathString();
 796  1
         String[] strDirs = tokenizePathToString( str, separator );
 797  1
         return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
 798  
     }
 799  
 }