Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 609   Methods: 12
NCLOC: 324   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SelectorUtils.java 81.1% 88% 66.7% 84.6%
 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 acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements 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 apache@apache.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.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.types.selectors;
 56   
 
 57   
 import java.io.File;
 58   
 import java.util.StringTokenizer;
 59   
 import java.util.Vector;
 60   
 
 61   
 import org.apache.tools.ant.types.Resource;
 62   
 
 63   
 /**
 64   
  * <p>This is a utility class used by selectors and DirectoryScanner. The
 65   
  * functionality more properly belongs just to selectors, but unfortunately
 66   
  * DirectoryScanner exposed these as protected methods. Thus we have to
 67   
  * support any subclasses of DirectoryScanner that may access these methods.
 68   
  * </p>
 69   
  * <p>This is a Singleton.</p>
 70   
  *
 71   
  * @author Arnout J. Kuiper
 72   
  * <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   
  * @since 1.5
 76   
  */
 77   
 public final class SelectorUtils {
 78   
 
 79   
     private static SelectorUtils instance = new SelectorUtils();
 80   
 
 81   
     /**
 82   
      * Private Constructor
 83   
      */
 84  1
     private SelectorUtils() {
 85   
     }
 86   
 
 87   
      /**
 88   
       * Retrieves the instance of the Singleton.
 89   
       */
 90  0
     public static SelectorUtils getInstance() {
 91  0
         return instance;
 92   
     }
 93   
 
 94   
     /**
 95   
      * Tests whether or not a given path matches the start of a given
 96   
      * pattern up to the first "**".
 97   
      * <p>
 98   
      * This is not a general purpose test and should only be used if you
 99   
      * can live with false positives. For example, <code>pattern=**\a</code>
 100   
      * and <code>str=b</code> will yield <code>true</code>.
 101   
      *
 102   
      * @param pattern The pattern to match against. Must not be
 103   
      *                <code>null</code>.
 104   
      * @param str     The path to match, as a String. Must not be
 105   
      *                <code>null</code>.
 106   
      *
 107   
      * @return whether or not a given path matches the start of a given
 108   
      * pattern up to the first "**".
 109   
      */
 110  0
     public static boolean matchPatternStart(String pattern, String str) {
 111  0
         return matchPatternStart(pattern, str, true);
 112   
     }
 113   
     /**
 114   
      * Tests whether or not a given path matches the start of a given
 115   
      * pattern up to the first "**".
 116   
      * <p>
 117   
      * This is not a general purpose test and should only be used if you
 118   
      * can live with false positives. For example, <code>pattern=**\a</code>
 119   
      * and <code>str=b</code> will yield <code>true</code>.
 120   
      *
 121   
      * @param pattern The pattern to match against. Must not be
 122   
      *                <code>null</code>.
 123   
      * @param str     The path to match, as a String. Must not be
 124   
      *                <code>null</code>.
 125   
      * @param isCaseSensitive Whether or not matching should be performed
 126   
      *                        case sensitively.
 127   
      *
 128   
      * @return whether or not a given path matches the start of a given
 129   
      * pattern up to the first "**".
 130   
      */
 131  4912
     public static boolean matchPatternStart(String pattern, String str,
 132   
                                                boolean isCaseSensitive) {
 133   
         // When str starts with a File.separator, pattern has to start with a
 134   
         // File.separator.
 135   
         // When pattern starts with a File.separator, str has to start with a
 136   
         // File.separator.
 137  4912
         if (str.startsWith(File.separator) !=
 138   
             pattern.startsWith(File.separator)) {
 139  0
             return false;
 140   
         }
 141   
 
 142  4912
         Vector patDirs = tokenizePath (pattern);
 143  4912
         Vector strDirs = tokenizePath (str);
 144   
 
 145  4912
         int patIdxStart = 0;
 146  4912
         int patIdxEnd   = patDirs.size()-1;
 147  4912
         int strIdxStart = 0;
 148  4912
         int strIdxEnd   = strDirs.size()-1;
 149   
 
 150   
         // up to first '**'
 151  4912
         while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
 152  6420
             String patDir = (String)patDirs.elementAt(patIdxStart);
 153  6420
             if (patDir.equals("**")) {
 154  1850
                 break;
 155   
             }
 156  4570
             if (!match(patDir,(String)strDirs.elementAt(strIdxStart),
 157   
                     isCaseSensitive)) {
 158  2919
                 return false;
 159   
             }
 160  1651
             patIdxStart++;
 161  1651
             strIdxStart++;
 162   
         }
 163   
 
 164  1993
         if (strIdxStart > strIdxEnd) {
 165   
             // String is exhausted
 166  140
             return true;
 167  1853
         } else if (patIdxStart > patIdxEnd) {
 168   
             // String not exhausted, but pattern is. Failure.
 169  3
             return false;
 170   
         } else {
 171   
             // pattern now holds ** while string is not exhausted
 172   
             // this will generate false positives but we can live with that.
 173  1850
             return true;
 174   
         }
 175   
     }
 176   
 
 177   
     /**
 178   
      * Tests whether or not a given path matches a given pattern.
 179   
      *
 180   
      * @param pattern The pattern to match against. Must not be
 181   
      *                <code>null</code>.
 182   
      * @param str     The path to match, as a String. Must not be
 183   
      *                <code>null</code>.
 184   
      *
 185   
      * @return <code>true</code> if the pattern matches against the string,
 186   
      *         or <code>false</code> otherwise.
 187   
      */
 188  0
     public static boolean matchPath(String pattern, String str) {
 189  0
         return matchPath(pattern, str, true);
 190   
     }
 191   
 
 192   
     /**
 193   
      * Tests whether or not a given path matches a given pattern.
 194   
      *
 195   
      * @param pattern The pattern to match against. Must not be
 196   
      *                <code>null</code>.
 197   
      * @param str     The path to match, as a String. Must not be
 198   
      *                <code>null</code>.
 199   
      * @param isCaseSensitive Whether or not matching should be performed
 200   
      *                        case sensitively.
 201   
      *
 202   
      * @return <code>true</code> if the pattern matches against the string,
 203   
      *         or <code>false</code> otherwise.
 204   
      */
 205  147800
     public static boolean matchPath(String pattern, String str,
 206   
             boolean isCaseSensitive) {
 207   
         // When str starts with a File.separator, pattern has to start with a
 208   
         // File.separator.
 209   
         // When pattern starts with a File.separator, str has to start with a
 210   
         // File.separator.
 211  147800
         if (str.startsWith(File.separator) !=
 212   
             pattern.startsWith(File.separator)) {
 213  0
             return false;
 214   
         }
 215   
 
 216  147800
         Vector patDirs = tokenizePath (pattern);
 217  147800
         Vector strDirs = tokenizePath (str);
 218   
 
 219  147800
         int patIdxStart = 0;
 220  147800
         int patIdxEnd   = patDirs.size()-1;
 221  147800
         int strIdxStart = 0;
 222  147800
         int strIdxEnd   = strDirs.size()-1;
 223   
 
 224   
         // up to first '**'
 225  147800
         while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
 226  237278
             String patDir = (String)patDirs.elementAt(patIdxStart);
 227  237278
             if (patDir.equals("**")) {
 228  112595
                 break;
 229   
             }
 230  124683
             if (!match(patDir,(String)strDirs.elementAt(strIdxStart),
 231   
                     isCaseSensitive)) {
 232  32084
                 patDirs = null; strDirs = null;
 233  32084
                 return false;
 234   
             }
 235  92599
             patIdxStart++;
 236  92599
             strIdxStart++;
 237   
         }
 238  115716
         if (strIdxStart > strIdxEnd) {
 239   
             // String is exhausted
 240  2179
             for (int i = patIdxStart; i <= patIdxEnd; i++) {
 241  2934
                 if (!patDirs.elementAt(i).equals("**")) {
 242  1700
                     patDirs = null; strDirs = null;
 243  1700
                     return false;
 244   
                 }
 245   
             }
 246  479
             return true;
 247   
         } else {
 248  113537
             if (patIdxStart > patIdxEnd) {
 249   
                 // String not exhausted, but pattern is. Failure.
 250  942
                 patDirs = null; strDirs = null;
 251  942
                 return false;
 252   
             }
 253   
         }
 254   
 
 255   
         // up to last '**'
 256  112595
         while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
 257  114658
             String patDir = (String)patDirs.elementAt(patIdxEnd);
 258  114658
             if (patDir.equals("**")) {
 259  32809
                 break;
 260   
             }
 261  81849
             if (!match(patDir,(String)strDirs.elementAt(strIdxEnd),
 262   
                     isCaseSensitive)) {
 263  79612
                 patDirs = null; strDirs = null;
 264  79612
                 return false;
 265   
             }
 266  2237
             patIdxEnd--;
 267  2237
             strIdxEnd--;
 268   
         }
 269  32983
         if (strIdxStart > strIdxEnd) {
 270   
             // String is exhausted
 271  174
             for (int i = patIdxStart; i <= patIdxEnd; i++) {
 272  174
                 if (!patDirs.elementAt(i).equals("**")) {
 273  0
                     patDirs = null; strDirs = null;
 274  0
                     return false;
 275   
                 }
 276   
             }
 277  174
             return true;
 278   
         }
 279   
 
 280  32809
         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
 281  17166
             int patIdxTmp = -1;
 282  34276
             for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
 283  34276
                 if (patDirs.elementAt(i).equals("**")) {
 284  17166
                     patIdxTmp = i;
 285  17166
                     break;
 286   
                 }
 287   
             }
 288  17166
             if (patIdxTmp == patIdxStart+1) {
 289   
                 // '**/**' situation, so skip one
 290  56
                 patIdxStart++;
 291  56
                 continue;
 292   
             }
 293   
             // Find the pattern between padIdxStart & padIdxTmp in str between
 294   
             // strIdxStart & strIdxEnd
 295  17110
             int patLength = (patIdxTmp-patIdxStart-1);
 296  17110
             int strLength = (strIdxEnd-strIdxStart+1);
 297  17110
             int foundIdx  = -1;
 298  17110
 strLoop:
 299  71624
             for (int i = 0; i <= strLength - patLength; i++) {
 300  56137
                 for (int j = 0; j < patLength; j++) {
 301  56137
                     String subPat = (String)patDirs.elementAt(patIdxStart+j+1);
 302  56137
                     String subStr = (String)strDirs.elementAt(strIdxStart+i+j);
 303  56137
                     if (!match(subPat,subStr, isCaseSensitive)) {
 304  54514
                         continue strLoop;
 305   
                     }
 306   
                 }
 307   
 
 308  1623
                 foundIdx = strIdxStart+i;
 309  1623
                 break;
 310   
             }
 311   
 
 312  17110
             if (foundIdx == -1) {
 313  15487
                 patDirs = null; strDirs = null;
 314  15487
                 return false;
 315   
             }
 316   
 
 317  1623
             patIdxStart = patIdxTmp;
 318  1623
             strIdxStart = foundIdx+patLength;
 319   
         }
 320   
 
 321  17322
         for (int i = patIdxStart; i <= patIdxEnd; i++) {
 322  17322
             if (!patDirs.elementAt(i).equals("**")) {
 323  0
                 patDirs = null; strDirs = null;
 324  0
                 return false;
 325   
             }
 326   
         }
 327   
 
 328  17322
         return true;
 329   
     }
 330   
 
 331   
     /**
 332   
      * Tests whether or not a string matches against a pattern.
 333   
      * The pattern may contain two special characters:<br>
 334   
      * '*' means zero or more characters<br>
 335   
      * '?' means one and only one character
 336   
      *
 337   
      * @param pattern The pattern to match against.
 338   
      *                Must not be <code>null</code>.
 339   
      * @param str     The string which must be matched against the pattern.
 340   
      *                Must not be <code>null</code>.
 341   
      *
 342   
      * @return <code>true</code> if the string matches against the pattern,
 343   
      *         or <code>false</code> otherwise.
 344   
      */
 345  25
     public static boolean match(String pattern, String str) {
 346  25
         return match(pattern, str, true);
 347   
     }
 348   
 
 349   
     /**
 350   
      * Tests whether or not a string matches against a pattern.
 351   
      * The pattern may contain two special characters:<br>
 352   
      * '*' means zero or more characters<br>
 353   
      * '?' means one and only one character
 354   
      *
 355   
      * @param pattern The pattern to match against.
 356   
      *                Must not be <code>null</code>.
 357   
      * @param str     The string which must be matched against the pattern.
 358   
      *                Must not be <code>null</code>.
 359   
      * @param isCaseSensitive Whether or not matching should be performed
 360   
      *                        case sensitively.
 361   
      *
 362   
      *
 363   
      * @return <code>true</code> if the string matches against the pattern,
 364   
      *         or <code>false</code> otherwise.
 365   
      */
 366  267264
     public static boolean match(String pattern, String str,
 367   
             boolean isCaseSensitive) {
 368  267264
         char[] patArr = pattern.toCharArray();
 369  267264
         char[] strArr = str.toCharArray();
 370  267264
         int patIdxStart = 0;
 371  267264
         int patIdxEnd   = patArr.length-1;
 372  267264
         int strIdxStart = 0;
 373  267264
         int strIdxEnd   = strArr.length-1;
 374  267264
         char ch;
 375   
 
 376  267264
         boolean containsStar = false;
 377  267264
         for (int i = 0; i < patArr.length; i++) {
 378  1301047
             if (patArr[i] == '*') {
 379  45750
                 containsStar = true;
 380  45750
                 break;
 381   
             }
 382   
         }
 383   
 
 384  267264
         if (!containsStar) {
 385   
             // No '*'s, so we make a shortcut
 386  221514
             if (patIdxEnd != strIdxEnd) {
 387  115531
                 return false; // Pattern and string do not have the same size
 388   
             }
 389  105983
             for (int i = 0; i <= patIdxEnd; i++) {
 390  455168
                 ch = patArr[i];
 391  455168
                 if (ch != '?') {
 392  455168
                     if (isCaseSensitive && ch != strArr[i]) {
 393  9606
                         return false;// Character mismatch
 394   
                     }
 395  445562
                     if (!isCaseSensitive && Character.toUpperCase(ch) !=
 396   
                         Character.toUpperCase(strArr[i])) {
 397  0
                         return false; // Character mismatch
 398   
                     }
 399   
                 }
 400   
             }
 401  96377
             return true; // String matches against pattern
 402   
         }
 403   
 
 404  45750
         if (patIdxEnd == 0) {
 405  124
             return true; // Pattern contains only '*', which matches anything
 406   
         }
 407   
 
 408   
         // Process characters before first star
 409  ?
         while((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
 410  33882
             if (ch != '?') {
 411  33882
                 if (isCaseSensitive && ch != strArr[strIdxStart]) {
 412  33261
                     return false;// Character mismatch
 413   
                 }
 414  621
                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
 415   
                     Character.toUpperCase(strArr[strIdxStart])) {
 416  0
                     return false;// Character mismatch
 417   
                 }
 418   
             }
 419  621
             patIdxStart++;
 420  621
             strIdxStart++;
 421   
         }
 422  12365
         if (strIdxStart > strIdxEnd) {
 423   
             // All characters in the string are used. Check if only '*'s are
 424   
             // left in the pattern. If so, we succeeded. Otherwise failure.
 425  4
             for (int i = patIdxStart; i <= patIdxEnd; i++) {
 426  8
                 if (patArr[i] != '*') {
 427  0
                     return false;
 428   
                 }
 429   
             }
 430  4
             return true;
 431   
         }
 432   
 
 433   
         // Process characters after last star
 434  ?
         while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
 435  15127
             if (ch != '?') {
 436  15127
                 if (isCaseSensitive && ch != strArr[strIdxEnd]) {
 437  10132
                     return false;// Character mismatch
 438   
                 }
 439  4995
                 if (!isCaseSensitive && Character.toUpperCase(ch) !=
 440   
                     Character.toUpperCase(strArr[strIdxEnd])) {
 441  9
                     return false;// Character mismatch
 442   
                 }
 443   
             }
 444  4986
             patIdxEnd--;
 445  4986
             strIdxEnd--;
 446   
         }
 447  2220
         if (strIdxStart > strIdxEnd) {
 448   
             // All characters in the string are used. Check if only '*'s are
 449   
             // left in the pattern. If so, we succeeded. Otherwise failure.
 450  6
             for (int i = patIdxStart; i <= patIdxEnd; i++) {
 451  6
                 if (patArr[i] != '*') {
 452  3
                     return false;
 453   
                 }
 454   
             }
 455  0
             return true;
 456   
         }
 457   
 
 458   
         // process pattern between stars. padIdxStart and patIdxEnd point
 459   
         // always to a '*'.
 460  2217
         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
 461  1163
             int patIdxTmp = -1;
 462  4496
             for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
 463  4496
                 if (patArr[i] == '*') {
 464  1163
                     patIdxTmp = i;
 465  1163
                     break;
 466   
                 }
 467   
             }
 468  1163
             if (patIdxTmp == patIdxStart+1) {
 469   
                 // Two stars next to each other, skip the first one.
 470  8
                 patIdxStart++;
 471  8
                 continue;
 472   
             }
 473   
             // Find the pattern between padIdxStart & padIdxTmp in str between
 474   
             // strIdxStart & strIdxEnd
 475  1155
             int patLength = (patIdxTmp-patIdxStart-1);
 476  1155
             int strLength = (strIdxEnd-strIdxStart+1);
 477  1155
             int foundIdx  = -1;
 478  1155
             strLoop:
 479  9370
             for (int i = 0; i <= strLength - patLength; i++) {
 480  8777
                 for (int j = 0; j < patLength; j++) {
 481  9855
                     ch = patArr[patIdxStart+j+1];
 482  9855
                     if (ch != '?') {
 483  9855
                         if (isCaseSensitive && ch != strArr[strIdxStart+i+j]) {
 484  8215
                             continue strLoop;
 485   
                         }
 486  1640
                         if (!isCaseSensitive && Character.toUpperCase(ch) !=
 487   
                             Character.toUpperCase(strArr[strIdxStart+i+j])) {
 488  0
                             continue strLoop;
 489   
                         }
 490   
                     }
 491   
                 }
 492   
 
 493  562
                 foundIdx = strIdxStart+i;
 494  562
                 break;
 495   
             }
 496   
 
 497  1155
             if (foundIdx == -1) {
 498  593
                 return false;
 499   
             }
 500   
 
 501  562
             patIdxStart = patIdxTmp;
 502  562
             strIdxStart = foundIdx+patLength;
 503   
         }
 504   
 
 505   
         // All characters in the string are used. Check if only '*'s are left
 506   
         // in the pattern. If so, we succeeded. Otherwise failure.
 507  1624
         for (int i = patIdxStart; i <= patIdxEnd; i++) {
 508  1624
             if (patArr[i] != '*') {
 509  0
                 return false;
 510   
             }
 511   
         }
 512  1624
         return true;
 513   
     }
 514   
 
 515   
     /**
 516   
      * Breaks a path up into a Vector of path elements, tokenizing on
 517   
      * <code>File.separator</code>.
 518   
      *
 519   
      * @param path Path to tokenize. Must not be <code>null</code>.
 520   
      *
 521   
      * @return a Vector of path elements from the tokenized path
 522   
      */
 523  305424
     public static Vector tokenizePath (String path) {
 524  305424
         Vector ret = new Vector();
 525  305424
         StringTokenizer st = new StringTokenizer(path,File.separator);
 526  305424
         while (st.hasMoreTokens()) {
 527  986402
             ret.addElement(st.nextToken());
 528   
         }
 529  305424
         return ret;
 530   
     }
 531   
 
 532   
 
 533   
     /**
 534   
      * Returns dependency information on these two files. If src has been
 535   
      * modified later than target, it returns true. If target doesn't exist,
 536   
      * it likewise returns true. Otherwise, target is newer than src and
 537   
      * is not out of date, thus the method returns false. It also returns
 538   
      * false if the src file doesn't even exist, since how could the
 539   
      * target then be out of date.
 540   
      *
 541   
      * @param src the original file
 542   
      * @param target the file being compared against
 543   
      * @param granularity the amount in seconds of slack we will give in
 544   
      *        determining out of dateness
 545   
      * @return whether the target is out of date
 546   
      */
 547  72
     public static boolean isOutOfDate(File src, File target, int granularity) {
 548  72
         if (!src.exists()) {
 549  0
             return false;
 550   
         }
 551  72
         if (!target.exists()) {
 552  11
             return true;
 553   
         }
 554  61
         if ((src.lastModified() - granularity) > target.lastModified()) {
 555  15
             return true;
 556   
         }
 557  46
         return false;
 558   
     }
 559   
 
 560   
     /**
 561   
      * Returns dependency information on these two resources. If src has been
 562   
      * modified later than target, it returns true. If target doesn't exist,
 563   
      * it likewise returns true. Otherwise, target is newer than src and
 564   
      * is not out of date, thus the method returns false. It also returns
 565   
      * false if the src file doesn't even exist, since how could the
 566   
      * target then be out of date.
 567   
      *
 568   
      * @param src the original resource
 569   
      * @param target the resource being compared against
 570   
      * @param granularity the amount in seconds of slack we will give in
 571   
      *        determining out of dateness
 572   
      * @return whether the target is out of date
 573   
      */
 574  0
     public static boolean isOutOfDate(Resource src, Resource target, 
 575   
                                       int granularity) {
 576  0
         if (!src.isExists()) {
 577  0
             return false;
 578   
         }
 579  0
         if (!target.isExists()) {
 580  0
             return true;
 581   
         }
 582  0
         if ((src.getLastModified() - granularity) > target.getLastModified()) {
 583  0
             return true;
 584   
         }
 585  0
         return false;
 586   
     }
 587   
 
 588   
    /**
 589   
      * "Flattens" a string by removing all whitespace (space, tab, linefeed,
 590   
      * carriage return, and formfeed). This uses StringTokenizer and the
 591   
      * default set of tokens as documented in the single arguement constructor.
 592   
      *
 593   
      * @param input a String to remove all whitespace.
 594   
      * @return a String that has had all whitespace removed.
 595   
      */
 596  2526
     public static String removeWhitespace(String input) {
 597  2526
         StringBuffer result = new StringBuffer();
 598  2526
         if (input != null) {
 599  2526
            StringTokenizer st = new StringTokenizer(input);
 600  2526
            while (st.hasMoreTokens()){
 601  6128
               result.append(st.nextToken());
 602   
            }
 603   
         }
 604  2526
         return result.toString();
 605   
     }
 606   
 
 607   
 }
 608   
 
 609