Coverage Report - org.codehaus.plexus.util.SelectorUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
SelectorUtils
71%
198/277
62%
156/248
7.115
 
 1  
 /*
 2  
  * The Apache Software License, Version 1.1
 3  
  *
 4  
  * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
 5  
  * reserved.
 6  
  *
 7  
  * Redistribution and use in source and binary forms, with or without
 8  
  * modification, are permitted provided that the following conditions
 9  
  * are met:
 10  
  *
 11  
  * 1. Redistributions of source code must retain the above copyright
 12  
  *    notice, this list of conditions and the following disclaimer.
 13  
  *
 14  
  * 2. Redistributions in binary form must reproduce the above copyright
 15  
  *    notice, this list of conditions and the following disclaimer in
 16  
  *    the documentation and/or other materials provided with the
 17  
  *    distribution.
 18  
  *
 19  
  * 3. The end-user documentation included with the redistribution, if
 20  
  *    any, must include the following acknowledgement:
 21  
  *       "This product includes software developed by the
 22  
  *        Apache Software Foundation (http://www.codehaus.org/)."
 23  
  *    Alternately, this acknowledgement may appear in the software itself,
 24  
  *    if and wherever such third-party acknowledgements normally appear.
 25  
  *
 26  
  * 4. The names "Ant" and "Apache Software
 27  
  *    Foundation" must not be used to endorse or promote products derived
 28  
  *    from this software without prior written permission. For written
 29  
  *    permission, please contact codehaus@codehaus.org.
 30  
  *
 31  
  * 5. Products derived from this software may not be called "Apache"
 32  
  *    nor may "Apache" appear in their names without prior written
 33  
  *    permission of the Apache Group.
 34  
  *
 35  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38  
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39  
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46  
  * SUCH DAMAGE.
 47  
  * ====================================================================
 48  
  *
 49  
  * This software consists of voluntary contributions made by many
 50  
  * individuals on behalf of the Apache Software Foundation.  For more
 51  
  * information on the Apache Software Foundation, please see
 52  
  * <http://www.codehaus.org/>.
 53  
  */
 54  
 
 55  
 package org.codehaus.plexus.util;
 56  
 
 57  
 import java.io.File;
 58  
 import java.util.ArrayList;
 59  
 import java.util.List;
 60  
 import java.util.StringTokenizer;
 61  
 
 62  
 /**
 63  
  * <p>
 64  
  * This is a utility class used by selectors and DirectoryScanner. The functionality more properly belongs just to
 65  
  * selectors, but unfortunately DirectoryScanner exposed these as protected methods. Thus we have to support any
 66  
  * subclasses of DirectoryScanner that may access these methods.
 67  
  * </p>
 68  
  * <p>
 69  
  * This is a Singleton.
 70  
  * </p>
 71  
  *
 72  
  * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
 73  
  * @author Magesh Umasankar
 74  
  * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
 75  
  * @version $Id$
 76  
  * @since 1.5
 77  
  */
 78  
 public final class SelectorUtils
 79  
 {
 80  
 
 81  
     public static final String PATTERN_HANDLER_PREFIX = "[";
 82  
 
 83  
     public static final String PATTERN_HANDLER_SUFFIX = "]";
 84  
 
 85  
     public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
 86  
 
 87  
     public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
 88  
 
 89  1
     private static SelectorUtils instance = new SelectorUtils();
 90  
 
 91  
     /**
 92  
      * Private Constructor
 93  
      */
 94  
     private SelectorUtils()
 95  1
     {
 96  1
     }
 97  
 
 98  
     /**
 99  
      * Retrieves the manager of the Singleton.
 100  
      */
 101  
     public static SelectorUtils getInstance()
 102  
     {
 103  0
         return instance;
 104  
     }
 105  
 
 106  
     /**
 107  
      * Tests whether or not a given path matches the start of a given pattern up to the first "**".
 108  
      * <p/>
 109  
      * This is not a general purpose test and should only be used if you can live with false positives. For example,
 110  
      * <code>pattern=**\a</code> and <code>str=b</code> will yield <code>true</code>.
 111  
      *
 112  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 113  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 114  
      * @return whether or not a given path matches the start of a given pattern up to the first "**".
 115  
      */
 116  
     public static boolean matchPatternStart( String pattern, String str )
 117  
     {
 118  0
         return matchPatternStart( pattern, str, true );
 119  
     }
 120  
 
 121  
     /**
 122  
      * Tests whether or not a given path matches the start of a given pattern up to the first "**".
 123  
      * <p/>
 124  
      * This is not a general purpose test and should only be used if you can live with false positives. For example,
 125  
      * <code>pattern=**\a</code> and <code>str=b</code> will yield <code>true</code>.
 126  
      *
 127  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 128  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 129  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 130  
      * @return whether or not a given path matches the start of a given pattern up to the first "**".
 131  
      */
 132  
     public static boolean matchPatternStart( String pattern, String str, boolean isCaseSensitive )
 133  
     {
 134  0
         if ( isRegexPrefixedPattern( pattern ) )
 135  
         {
 136  
             // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
 137  
             // a file to deal with, or we can definitely say this is an exclusion...
 138  0
             return true;
 139  
         }
 140  
         else
 141  
         {
 142  0
             if ( isAntPrefixedPattern( pattern ) )
 143  
             {
 144  0
                 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
 145  
                                              pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 146  
             }
 147  
 
 148  0
             String altStr = str.replace( '\\', '/' );
 149  
 
 150  0
             return matchAntPathPatternStart( pattern, str, File.separator, isCaseSensitive )
 151  
                 || matchAntPathPatternStart( pattern, altStr, "/", isCaseSensitive );
 152  
         }
 153  
     }
 154  
 
 155  
     static boolean isAntPrefixedPattern( String pattern )
 156  
     {
 157  462
         return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
 158  
             && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
 159  
     }
 160  
 
 161  
     @SuppressWarnings( "SimplifiableIfStatement" )
 162  
     static boolean matchAntPathPatternStart( MatchPattern pattern, String str, String separator,
 163  
                                              boolean isCaseSensitive )
 164  
     {
 165  72
         if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
 166  
         {
 167  0
             return false;
 168  
         }
 169  
 
 170  72
         return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
 171  
     }
 172  
 
 173  
     static boolean matchAntPathPatternStart( String pattern, String str, String separator, boolean isCaseSensitive )
 174  
     {
 175  
         // When str starts with a File.separator, pattern has to start with a
 176  
         // File.separator.
 177  
         // When pattern starts with a File.separator, str has to start with a
 178  
         // File.separator.
 179  0
         if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
 180  
         {
 181  0
             return false;
 182  
         }
 183  
 
 184  0
         String[] patDirs = tokenizePathToString( pattern, separator );
 185  0
         return matchAntPathPatternStart( patDirs, str, separator, isCaseSensitive );
 186  
     }
 187  
 
 188  
     // When str starts with a File.separator, pattern has to start with a
 189  
     // File.separator.
 190  
     // When pattern starts with a File.separator, str has to start with a
 191  
     // File.separator.
 192  
     private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
 193  
     {
 194  303
         return str.startsWith( separator ) != pattern.startsWith( separator );
 195  
     }
 196  
 
 197  
     private static boolean separatorPatternStartSlashMismatch( MatchPattern matchPattern, String str, String separator )
 198  
     {
 199  73
         return str.startsWith( separator ) != matchPattern.startsWith( separator );
 200  
     }
 201  
 
 202  
     static boolean matchAntPathPatternStart( String[] patDirs, String str, String separator, boolean isCaseSensitive )
 203  
     {
 204  72
         String[] strDirs = tokenizePathToString( str, separator );
 205  
 
 206  72
         int patIdxStart = 0;
 207  72
         int patIdxEnd = patDirs.length - 1;
 208  72
         int strIdxStart = 0;
 209  72
         int strIdxEnd = strDirs.length - 1;
 210  
 
 211  
         // up to first '**'
 212  90
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 213  
         {
 214  72
             String patDir = patDirs[patIdxStart];
 215  72
             if ( patDir.equals( "**" ) )
 216  
             {
 217  36
                 break;
 218  
             }
 219  36
             if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
 220  
             {
 221  18
                 return false;
 222  
             }
 223  18
             patIdxStart++;
 224  18
             strIdxStart++;
 225  18
         }
 226  
 
 227  54
         return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
 228  
     }
 229  
 
 230  
     /**
 231  
      * Tests whether or not a given path matches a given pattern.
 232  
      *
 233  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 234  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 235  
      * @return <code>true</code> if the pattern matches against the string, or <code>false</code> otherwise.
 236  
      */
 237  
     public static boolean matchPath( String pattern, String str )
 238  
     {
 239  4
         return matchPath( pattern, str, true );
 240  
     }
 241  
 
 242  
     /**
 243  
      * Tests whether or not a given path matches a given pattern.
 244  
      *
 245  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 246  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 247  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 248  
      * @return <code>true</code> if the pattern matches against the string, or <code>false</code> otherwise.
 249  
      */
 250  
     public static boolean matchPath( String pattern, String str, boolean isCaseSensitive )
 251  
     {
 252  295
         return matchPath( pattern, str, File.separator, isCaseSensitive );
 253  
     }
 254  
 
 255  
     public static boolean matchPath( String pattern, String str, String separator, boolean isCaseSensitive )
 256  
     {
 257  303
         if ( isRegexPrefixedPattern( pattern ) )
 258  
         {
 259  0
             pattern =
 260  
                 pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 261  
 
 262  0
             return str.matches( pattern );
 263  
         }
 264  
         else
 265  
         {
 266  303
             if ( isAntPrefixedPattern( pattern ) )
 267  
             {
 268  0
                 pattern = pattern.substring( ANT_HANDLER_PREFIX.length(),
 269  
                                              pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
 270  
             }
 271  
 
 272  303
             return matchAntPathPattern( pattern, str, separator, isCaseSensitive );
 273  
         }
 274  
     }
 275  
 
 276  
     static boolean isRegexPrefixedPattern( String pattern )
 277  
     {
 278  462
         return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
 279  
             && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
 280  
     }
 281  
 
 282  
     static boolean matchAntPathPattern( MatchPattern matchPattern, String str, String separator,
 283  
                                         boolean isCaseSensitive )
 284  
     {
 285  1
         if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) )
 286  
         {
 287  0
             return false;
 288  
         }
 289  1
         String[] patDirs = matchPattern.getTokenizedPathString();
 290  1
         String[] strDirs = tokenizePathToString( str, separator );
 291  1
         return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
 292  
     }
 293  
 
 294  
     static boolean matchAntPathPattern( String pattern, String str, String separator, boolean isCaseSensitive )
 295  
     {
 296  303
         if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
 297  
         {
 298  6
             return false;
 299  
         }
 300  297
         String[] patDirs = tokenizePathToString( pattern, separator );
 301  297
         String[] strDirs = tokenizePathToString( str, separator );
 302  297
         return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
 303  
 
 304  
     }
 305  
 
 306  
     static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
 307  
     {
 308  298
         int patIdxStart = 0;
 309  298
         int patIdxEnd = patDirs.length - 1;
 310  298
         int strIdxStart = 0;
 311  298
         int strIdxEnd = strDirs.length - 1;
 312  
 
 313  
         // up to first '**'
 314  311
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 315  
         {
 316  304
             String patDir = patDirs[patIdxStart];
 317  304
             if ( patDir.equals( "**" ) )
 318  
             {
 319  291
                 break;
 320  
             }
 321  13
             if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
 322  
             {
 323  0
                 return false;
 324  
             }
 325  13
             patIdxStart++;
 326  13
             strIdxStart++;
 327  13
         }
 328  298
         if ( strIdxStart > strIdxEnd )
 329  
         {
 330  
             // String is exhausted
 331  7
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 332  
             {
 333  0
                 if ( !patDirs[i].equals( "**" ) )
 334  
                 {
 335  0
                     return false;
 336  
                 }
 337  
             }
 338  7
             return true;
 339  
         }
 340  
         else
 341  
         {
 342  291
             if ( patIdxStart > patIdxEnd )
 343  
             {
 344  
                 // String not exhausted, but pattern is. Failure.
 345  0
                 return false;
 346  
             }
 347  
         }
 348  
 
 349  
         // up to last '**'
 350  291
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 351  
         {
 352  291
             String patDir = patDirs[patIdxEnd];
 353  291
             if ( patDir.equals( "**" ) )
 354  
             {
 355  95
                 break;
 356  
             }
 357  196
             if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
 358  
             {
 359  196
                 return false;
 360  
             }
 361  0
             patIdxEnd--;
 362  0
             strIdxEnd--;
 363  0
         }
 364  95
         if ( strIdxStart > strIdxEnd )
 365  
         {
 366  
             // String is exhausted
 367  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 368  
             {
 369  0
                 if ( !patDirs[i].equals( "**" ) )
 370  
                 {
 371  0
                     return false;
 372  
                 }
 373  
             }
 374  0
             return true;
 375  
         }
 376  
 
 377  95
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 378  
         {
 379  91
             int patIdxTmp = -1;
 380  182
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 381  
             {
 382  182
                 if ( patDirs[i].equals( "**" ) )
 383  
                 {
 384  91
                     patIdxTmp = i;
 385  91
                     break;
 386  
                 }
 387  
             }
 388  91
             if ( patIdxTmp == patIdxStart + 1 )
 389  
             {
 390  
                 // '**/**' situation, so skip one
 391  0
                 patIdxStart++;
 392  0
                 continue;
 393  
             }
 394  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 395  
             // strIdxStart & strIdxEnd
 396  91
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 397  91
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 398  91
             int foundIdx = -1;
 399  247
             strLoop: for ( int i = 0; i <= strLength - patLength; i++ )
 400  
             {
 401  156
                 for ( int j = 0; j < patLength; j++ )
 402  
                 {
 403  156
                     String subPat = patDirs[patIdxStart + j + 1];
 404  156
                     String subStr = strDirs[strIdxStart + i + j];
 405  156
                     if ( !match( subPat, subStr, isCaseSensitive ) )
 406  
                     {
 407  156
                         continue strLoop;
 408  
                     }
 409  
                 }
 410  
 
 411  0
                 foundIdx = strIdxStart + i;
 412  0
                 break;
 413  
             }
 414  
 
 415  91
             if ( foundIdx == -1 )
 416  
             {
 417  91
                 return false;
 418  
             }
 419  
 
 420  0
             patIdxStart = patIdxTmp;
 421  0
             strIdxStart = foundIdx + patLength;
 422  0
         }
 423  
 
 424  8
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 425  
         {
 426  4
             if ( !patDirs[i].equals( "**" ) )
 427  
             {
 428  0
                 return false;
 429  
             }
 430  
         }
 431  
 
 432  4
         return true;
 433  
     }
 434  
 
 435  
     static boolean matchAntPathPattern( char[][] patDirs, char[][] strDirs, boolean isCaseSensitive )
 436  
     {
 437  779
         int patIdxStart = 0;
 438  779
         int patIdxEnd = patDirs.length - 1;
 439  779
         int strIdxStart = 0;
 440  779
         int strIdxEnd = strDirs.length - 1;
 441  
 
 442  
         // up to first '**'
 443  812
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 444  
         {
 445  664
             char[] patDir = patDirs[patIdxStart];
 446  664
             if ( isDoubleStar( patDir ) )
 447  
             {
 448  450
                 break;
 449  
             }
 450  214
             if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
 451  
             {
 452  181
                 return false;
 453  
             }
 454  33
             patIdxStart++;
 455  33
             strIdxStart++;
 456  33
         }
 457  598
         if ( strIdxStart > strIdxEnd )
 458  
         {
 459  
             // String is exhausted
 460  215
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 461  
             {
 462  187
                 if ( !isDoubleStar( patDirs[i] ) )
 463  
                 {
 464  94
                     return false;
 465  
                 }
 466  
             }
 467  28
             return true;
 468  
         }
 469  
         else
 470  
         {
 471  476
             if ( patIdxStart > patIdxEnd )
 472  
             {
 473  
                 // String not exhausted, but pattern is. Failure.
 474  26
                 return false;
 475  
             }
 476  
         }
 477  
 
 478  
         // up to last '**'
 479  499
         while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
 480  
         {
 481  489
             char[] patDir = patDirs[patIdxEnd];
 482  489
             if ( isDoubleStar( patDir ) )
 483  
             {
 484  192
                 break;
 485  
             }
 486  297
             if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
 487  
             {
 488  248
                 return false;
 489  
             }
 490  49
             patIdxEnd--;
 491  49
             strIdxEnd--;
 492  49
         }
 493  202
         if ( strIdxStart > strIdxEnd )
 494  
         {
 495  
             // String is exhausted
 496  20
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 497  
             {
 498  16
                 if ( !isDoubleStar( patDirs[i] ) )
 499  
                 {
 500  6
                     return false;
 501  
                 }
 502  
             }
 503  4
             return true;
 504  
         }
 505  
 
 506  199
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 507  
         {
 508  121
             int patIdxTmp = -1;
 509  242
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 510  
             {
 511  242
                 if ( isDoubleStar( patDirs[i] ) )
 512  
                 {
 513  121
                     patIdxTmp = i;
 514  121
                     break;
 515  
                 }
 516  
             }
 517  121
             if ( patIdxTmp == patIdxStart + 1 )
 518  
             {
 519  
                 // '**/**' situation, so skip one
 520  0
                 patIdxStart++;
 521  0
                 continue;
 522  
             }
 523  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 524  
             // strIdxStart & strIdxEnd
 525  121
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 526  121
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 527  121
             int foundIdx = -1;
 528  340
             strLoop: for ( int i = 0; i <= strLength - patLength; i++ )
 529  
             {
 530  233
                 for ( int j = 0; j < patLength; j++ )
 531  
                 {
 532  226
                     char[] subPat = patDirs[patIdxStart + j + 1];
 533  226
                     char[] subStr = strDirs[strIdxStart + i + j];
 534  226
                     if ( !match( subPat, subStr, isCaseSensitive ) )
 535  
                     {
 536  219
                         continue strLoop;
 537  
                     }
 538  
                 }
 539  
 
 540  7
                 foundIdx = strIdxStart + i;
 541  7
                 break;
 542  
             }
 543  
 
 544  121
             if ( foundIdx == -1 )
 545  
             {
 546  114
                 return false;
 547  
             }
 548  
 
 549  7
             patIdxStart = patIdxTmp;
 550  7
             strIdxStart = foundIdx + patLength;
 551  7
         }
 552  
 
 553  156
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 554  
         {
 555  78
             if ( !isDoubleStar( patDirs[i] ) )
 556  
             {
 557  0
                 return false;
 558  
             }
 559  
         }
 560  
 
 561  78
         return true;
 562  
     }
 563  
 
 564  
     private static boolean isDoubleStar( char[] patDir )
 565  
     {
 566  1676
         return patDir != null && patDir.length == 2 && patDir[0] == '*' && patDir[1] == '*';
 567  
     }
 568  
 
 569  
     /**
 570  
      * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:<br>
 571  
      * '*' means zero or more characters<br>
 572  
      * '?' means one and only one character
 573  
      *
 574  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 575  
      * @param str The string which must be matched against the pattern. Must not be <code>null</code>.
 576  
      * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
 577  
      */
 578  
     public static boolean match( String pattern, String str )
 579  
     {
 580  0
         return match( pattern, str, true );
 581  
     }
 582  
 
 583  
     /**
 584  
      * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:<br>
 585  
      * '*' means zero or more characters<br>
 586  
      * '?' means one and only one character
 587  
      *
 588  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 589  
      * @param str The string which must be matched against the pattern. Must not be <code>null</code>.
 590  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 591  
      * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
 592  
      */
 593  
     public static boolean match( String pattern, String str, boolean isCaseSensitive )
 594  
     {
 595  401
         char[] patArr = pattern.toCharArray();
 596  401
         char[] strArr = str.toCharArray();
 597  401
         return match( patArr, strArr, isCaseSensitive );
 598  
     }
 599  
 
 600  
     public static boolean match( char[] patArr, char[] strArr, boolean isCaseSensitive )
 601  
     {
 602  1138
         int patIdxStart = 0;
 603  1138
         int patIdxEnd = patArr.length - 1;
 604  1138
         int strIdxStart = 0;
 605  1138
         int strIdxEnd = strArr.length - 1;
 606  
         char ch;
 607  
 
 608  1138
         boolean containsStar = false;
 609  9317
         for ( char aPatArr : patArr )
 610  
         {
 611  8333
             if ( aPatArr == '*' )
 612  
             {
 613  154
                 containsStar = true;
 614  154
                 break;
 615  
             }
 616  
         }
 617  
 
 618  1138
         if ( !containsStar )
 619  
         {
 620  
             // No '*'s, so we make a shortcut
 621  984
             if ( patIdxEnd != strIdxEnd )
 622  
             {
 623  760
                 return false; // Pattern and string do not have the same size
 624  
             }
 625  1036
             for ( int i = 0; i <= patIdxEnd; i++ )
 626  
             {
 627  970
                 ch = patArr[i];
 628  970
                 if ( ch != '?' && !equals( ch, strArr[i], isCaseSensitive ) )
 629  
                 {
 630  158
                     return false; // Character mismatch
 631  
                 }
 632  
             }
 633  66
             return true; // String matches against pattern
 634  
         }
 635  
 
 636  154
         if ( patIdxEnd == 0 )
 637  
         {
 638  48
             return true; // Pattern contains only '*', which matches anything
 639  
         }
 640  
 
 641  
         // Process characters before first star
 642  119
         while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
 643  
         {
 644  94
             if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) )
 645  
             {
 646  81
                 return false; // Character mismatch
 647  
             }
 648  13
             patIdxStart++;
 649  13
             strIdxStart++;
 650  
         }
 651  25
         if ( strIdxStart > strIdxEnd )
 652  
         {
 653  
             // All characters in the string are used. Check if only '*'s are
 654  
             // left in the pattern. If so, we succeeded. Otherwise failure.
 655  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 656  
             {
 657  0
                 if ( patArr[i] != '*' )
 658  
                 {
 659  0
                     return false;
 660  
                 }
 661  
             }
 662  0
             return true;
 663  
         }
 664  
 
 665  
         // Process characters after last star
 666  37
         while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
 667  
         {
 668  31
             if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) )
 669  
             {
 670  19
                 return false; // Character mismatch
 671  
             }
 672  12
             patIdxEnd--;
 673  12
             strIdxEnd--;
 674  
         }
 675  6
         if ( strIdxStart > strIdxEnd )
 676  
         {
 677  
             // All characters in the string are used. Check if only '*'s are
 678  
             // left in the pattern. If so, we succeeded. Otherwise failure.
 679  0
             for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 680  
             {
 681  0
                 if ( patArr[i] != '*' )
 682  
                 {
 683  0
                     return false;
 684  
                 }
 685  
             }
 686  0
             return true;
 687  
         }
 688  
 
 689  
         // process pattern between stars. padIdxStart and patIdxEnd point
 690  
         // always to a '*'.
 691  8
         while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
 692  
         {
 693  2
             int patIdxTmp = -1;
 694  2
             for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
 695  
             {
 696  2
                 if ( patArr[i] == '*' )
 697  
                 {
 698  2
                     patIdxTmp = i;
 699  2
                     break;
 700  
                 }
 701  
             }
 702  2
             if ( patIdxTmp == patIdxStart + 1 )
 703  
             {
 704  
                 // Two stars next to each other, skip the first one.
 705  2
                 patIdxStart++;
 706  2
                 continue;
 707  
             }
 708  
             // Find the pattern between padIdxStart & padIdxTmp in str between
 709  
             // strIdxStart & strIdxEnd
 710  0
             int patLength = ( patIdxTmp - patIdxStart - 1 );
 711  0
             int strLength = ( strIdxEnd - strIdxStart + 1 );
 712  0
             int foundIdx = -1;
 713  0
             strLoop: for ( int i = 0; i <= strLength - patLength; i++ )
 714  
             {
 715  0
                 for ( int j = 0; j < patLength; j++ )
 716  
                 {
 717  0
                     ch = patArr[patIdxStart + j + 1];
 718  0
                     if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) )
 719  
                     {
 720  0
                         continue strLoop;
 721  
                     }
 722  
                 }
 723  
 
 724  0
                 foundIdx = strIdxStart + i;
 725  0
                 break;
 726  
             }
 727  
 
 728  0
             if ( foundIdx == -1 )
 729  
             {
 730  0
                 return false;
 731  
             }
 732  
 
 733  0
             patIdxStart = patIdxTmp;
 734  0
             strIdxStart = foundIdx + patLength;
 735  0
         }
 736  
 
 737  
         // All characters in the string are used. Check if only '*'s are left
 738  
         // in the pattern. If so, we succeeded. Otherwise failure.
 739  12
         for ( int i = patIdxStart; i <= patIdxEnd; i++ )
 740  
         {
 741  6
             if ( patArr[i] != '*' )
 742  
             {
 743  0
                 return false;
 744  
             }
 745  
         }
 746  6
         return true;
 747  
     }
 748  
 
 749  
     /**
 750  
      * Tests whether two characters are equal.
 751  
      */
 752  
     private static boolean equals( char c1, char c2, boolean isCaseSensitive )
 753  
     {
 754  1095
         if ( c1 == c2 )
 755  
         {
 756  837
             return true;
 757  
         }
 758  258
         if ( !isCaseSensitive )
 759  
         {
 760  
             // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
 761  0
             if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 )
 762  
                 || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) )
 763  
             {
 764  0
                 return true;
 765  
             }
 766  
         }
 767  258
         return false;
 768  
     }
 769  
 
 770  
     private static String[] tokenizePathToString( String path, String separator )
 771  
     {
 772  667
         List<String> ret = new ArrayList<String>();
 773  667
         StringTokenizer st = new StringTokenizer( path, separator );
 774  2024
         while ( st.hasMoreTokens() )
 775  
         {
 776  1357
             ret.add( st.nextToken() );
 777  
         }
 778  667
         return ret.toArray( new String[ret.size()] );
 779  
     }
 780  
 
 781  
     /**
 782  
      * Returns dependency information on these two files. If src has been modified later than target, it returns true.
 783  
      * If target doesn't exist, it likewise returns true. Otherwise, target is newer than src and is not out of date,
 784  
      * thus the method returns false. It also returns false if the src file doesn't even exist, since how could the
 785  
      * target then be out of date.
 786  
      *
 787  
      * @param src the original file
 788  
      * @param target the file being compared against
 789  
      * @param granularity the amount in seconds of slack we will give in determining out of dateness
 790  
      * @return whether the target is out of date
 791  
      */
 792  
     public static boolean isOutOfDate( File src, File target, int granularity )
 793  
     {
 794  0
         if ( !src.exists() )
 795  
         {
 796  0
             return false;
 797  
         }
 798  0
         if ( !target.exists() )
 799  
         {
 800  0
             return true;
 801  
         }
 802  0
         if ( ( src.lastModified() - granularity ) > target.lastModified() )
 803  
         {
 804  0
             return true;
 805  
         }
 806  0
         return false;
 807  
     }
 808  
 
 809  
     /**
 810  
      * "Flattens" a string by removing all whitespace (space, tab, linefeed, carriage return, and formfeed). This uses
 811  
      * StringTokenizer and the default set of tokens as documented in the single argument constructor.
 812  
      *
 813  
      * @param input a String to remove all whitespace.
 814  
      * @return a String that has had all whitespace removed.
 815  
      */
 816  
     public static String removeWhitespace( String input )
 817  
     {
 818  0
         StringBuilder result = new StringBuilder();
 819  0
         if ( input != null )
 820  
         {
 821  0
             StringTokenizer st = new StringTokenizer( input );
 822  0
             while ( st.hasMoreTokens() )
 823  
             {
 824  0
                 result.append( st.nextToken() );
 825  
             }
 826  
         }
 827  0
         return result.toString();
 828  
     }
 829  
 }